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.

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?

WatchOS Sim can't perform sendMessage
 
 
Q