WatchOS Sim can't perform sendMessage

Hello


I began learning about WatchKit. But I can't go to next step at sendMessage()

https://medium.com/@litoarias/watchos-5-part-2-communication-between-iphone-and-apple-watch-and-vice-versa-ced41f453b11

I created new iOS App with Watch App Project in XCode 11.2 to follow the post.

At Part2, I found the problem. sendMessage() in WatchApp occured below error.


[Error Logs in Watch App] - send message to iOS from WatchOS

2019-11-06 16:38:05.378991+0900 watchapp WatchKit Extension[66565:2566272] [WC] WCSession iOS app not installed

2019-11-06 16:38:05.379949+0900 watchapp WatchKit Extension[66565:2566272] [WC] -[WCSession _onqueue_notifyOfMessageError:messageID:withErrorHandler:] (null) errorHandler: YES with WCErrorDomain:7018

Sending data has been failed. error[Error Domain=WCErrorDomain Code=7018 "Companion app is not installed." UserInfo={NSLocalizedRecoverySuggestion=Install the Companion app., NSLocalizedDescription=Companion app is not installed.}]

2019-11-06 16:38:05.381395+0900 watchapp WatchKit Extension[66565:2579690] [WC] WCSession iOS app not installed

2019-11-06 16:38:05.381644+0900 watchapp WatchKit Extension[66565:2579690] [WC] -[WCSession _onqueue_notifyOfMessageError:messageID:withErrorHandler:] (null) errorHandler: YES with WCErrorDomain:7018

Sending data has been failed. error[Error Domain=WCErrorDomain Code=7018 "Companion app is not installed." UserInfo={NSLocalizedRecoverySuggestion=Install the Companion app., NSLocalizedDescription=Companion app is not installed.}]


[Log for Printing Received Data in Watch App From iOS App]

[session(_:didReceiveMessage:replyHandler:)] Watch Receive. message[["title": gogo]] handler[(Function)]


Ofcourse, I checked my iPhone Simulator and Watch Simulator their are paired together.

And calling sendMessage() in iPhone App is very well(iOS => Watch => iOS).


Please anyone let me know, what I missed. Thanks.

Replies

You should show the code you have written so far, otherwise difficult to guess what you have not done without knowing what you have done 😉


But let's try.


I understand you test simulator. Does it work on device ?

If simulator, don't forget to launch iOS app and watchOS app.


So, you cannot send message from Watch to iOS.

Does it work the other way ?


Did you activate a session on iOS ?

Should have a pattern like this in the controller


import UIKit
import WatchConnectivity

class StartViewController: UIViewController, WCSessionDelegate {

    override func viewDidLoad() {
      
        super.viewDidLoad()
        if (WCSession.isSupported()) {  // Just activate session, on the iPhone side
            let session = WCSession.default
            session.delegate = self
            session.activate()
        }

     }

}


Did you do it ?


You need also to implement the delegate functions in the class, as described in the reference tutorial you mention:


    // MARK: - WCSessionDelegate
  
    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {

        if activationState == .activated {
            // Send what you need to the Watch        }
    }
  
    func sessionDidBecomeInactive(_ session: WCSession) {
      
    }
  
    func sessionDidDeactivate(_ session: WCSession) {
      
    }
  
    func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) {
      
        if message["request"] as? String == "myKey" {

            replyHandler(["myKey" : NSLocalizedString("OK", comment: "StartViewController")])
              
        }
    }

Thanks for your answer


I created This manager and use it in both iOS and WatchOS

XCode 11.2, iPhone XS Max Simulator(iOS 13.2), Apple Watch Series 5(WatchOS 6.1)


[WCSessionManager]

import Foundation
import WatchConnectivity

class WCSessionManager : NSObject{
    static let shared = WCSessionManager();
    private var session : WCSession = .default;
    var isReachable : Bool{
        return self.session.isReachable;
    }
    
    override init() {
        super.init();
        
        self.session.activateIfSupported(self);
        
        #if os(iOS)
            print("WatchManager Created. Paired[\(self.session.isPaired)] AppInstalled[\(self.session.isWatchAppInstalled)]");
        #else
            print("[\(#function)]]");
        #endif
    }
    
    func activate(){
        self.session.activateIfSupported(self);
        
        #if os(iOS)
            print("[\(#function)]] Paired[\(self.session.isPaired)] AppInstalled[\(self.session.isWatchAppInstalled)]");
        #else
            print("[\(#function)]]");
        #endif
    }
    
    func send(){
        self.session.sendMessage(["title" : "gogo"], replyHandler: { (replyData) in
            print("Sending data has been completed. data[\(replyData)]");
        }) { (error) in
            print("Sending data has been failed. error[\(error)]");
        }
    }
}

extension WCSessionManager : WCSessionDelegate{
    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
        print("[\(#function)] Watch did activated. state[\(activationState.rawValue)] error[\(error)]")
    }
    
    func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
        print("[\(#function)] Watch Receive. message[\(message)]")
    }
    
    func session(_ session: WCSession, didReceiveMessageData messageData: Data) {
        print("[\(#function)] Watch Receive. message[\(messageData)]")
    }
    
    func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) {
        print("[\(#function)] Watch Receive. message[\(userInfo)]")
    }
    
    func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
        print("[\(#function)] Watch Receive. message[\(applicationContext)]")
    }
    
    func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) {
        print("[\(#function)] Watch Receive. message[\(message)] handler[\(replyHandler)]")
        #if os(iOS)
        replyHandler(["reuslt" : "iOS"])
        #else
        replyHandler(["reuslt" : "Watch"])
        #endif
    }
    
    #if os(iOS)
    func sessionDidBecomeInactive(_ session: WCSession) {
        print("[\(#function)]")
    }
    
    func sessionDidDeactivate(_ session: WCSession) {
        print("[\(#function)]")
        self.session.activate()
    }
    #endif
}


[MainInterfaceController]

class MainInterfaceController: WKInterfaceController {

     var sessionManager : WCSessionManager{
        return WCSessionManager.shared;
    }
...

     func sendToPhone(){
        guard self.sessionManager.isReachable else{
            return;
        }
        
        self.sessionManager.send();
    }

    @IBAction func onSend() {
        self.sendToPhone();
    }
...
}


[ViewController]

@IBAction func onActivate(_ sender: Any) {
        WCSessionManager.shared.activate();
        WCSessionManager.shared.send();
    }


[Watch App Log]

[session(_:activationDidCompleteWith:error:)] Watch did activated. state[2] error[nil]


2019-11-07 08:59:43.102106+0900 watchapp WatchKit Extension[67432:3654839] [WC] WCSession iOS app not installed

2019-11-07 08:59:43.102863+0900 watchapp WatchKit Extension[67432:3654839] [WC] -[WCSession _onqueue_notifyOfMessageError:messageID:withErrorHandler:] (null) errorHandler: YES with WCErrorDomain:7018

Sending data has been failed. error[Error Domain=WCErrorDomain Code=7018 "Companion app is not installed." UserInfo={NSLocalizedRecoverySuggestion=Install the Companion app., NSLocalizedDescription=Companion app is not installed.}]

It is SwiftUI if I read correctly ?


Where is the equivalent in your code of


    override func viewDidLoad() { 
       
        super.viewDidLoad() 
        if (WCSession.isSupported()) {  // Just activate session, on the iPhone side 
            let session = WCSession.default 
            session.delegate = self 
            session.activate() 
        } 
 
     }

Thanks Claude31 for your reply


I checked isSupported() with self.session.activateIfSupported(self)

[WCSession+]

extension WCSession{
    func activateIfSupported(_ delegate: WCSessionDelegate){
        guard WCSession.isSupported() else{
            return;
        }
        
        self.delegate = delegate;
        self.activate();
        
        print("Watch Session Activated[\(self.activationState.rawValue)]");
    }
}


Watch has been activated and successfully called

func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {

with activationState == 2


and also func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {


[Log] - received message from iPhone

[session(_:didReceiveMessage:replyHandler:)] Watch Receive. message[["title": gogo]] handler[(Function)]


[Problem]

Watch has been activated, but it can't send message to iPhone, but it can reply to iPhone.


Thanks

Check your WatchKit Extention target.

- Uncheck "Supports Running Without iOS App Installation"

this is a bug right? because "Supports Running Without iOS App Installation" should not mean "cannot have ios app installed"? And does not on the real device, only on simulator?