Watch Connectivity: didReceiveMessage not called
Asked Answered
W

2

10

I am using WatchConnectivity framework to receive a string from my app. Here is how I send the string in Obj-C from the app:

-(void) viewDidLoad {

    //WATCHKIT
    WCSession* session = [WCSession defaultSession];
    session.delegate = self;
    [session activateSession];

    [self sendInfoToWatch];
}

-(void) sendInfoToWatch {

    WCSession* session = [WCSession defaultSession];
    session.delegate = self;
    [session activateSession];

    [session sendMessage:@{@"a":@"hello"} replyHandler:^(NSDictionary<NSString *,id> * _Nonnull replyMessage) {

    } errorHandler:^(NSError * _Nonnull error) {

    }];
}

My Watch app is in Swift. This is how I retrieve the message:

Note: "wc session is supported" works and gets logged to the console

override func willActivate() {
    if(WCSession.isSupported()){
        NSLog("wc session is supported")
        self.session = WCSession.defaultSession()
        self.session.delegate = self
        self.session.activateSession()
    }

    super.willActivate()
}

The following function is never called, none of the NSLog's show up, so the QRCodeTitleLabel does not update its text.

func session(session: WCSession, didReceiveMessage message: [String : AnyObject]) {
    //recieving message from iphone

    QRCodeTitleLabel.setText(message["a"]! as? String)

    NSLog("This was called")
    NSLog((message["a"]! as? String)!)

}

Does anyone know why this method is not called?

Also, I have imported WatchConnectivity and included WCSessionDelegate after my class name.

Edit:

I added the function with replyHandler, but this method still isn't called:

func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) {
    QRCodeTitleLabel.setText(message["a"]! as? String)

    NSLog("This was called")
    NSLog((message["a"]! as? String)!)
}
Wichman answered 1/2, 2016 at 14:28 Comment(7)
If you add an NSLog to the errorHandler, does it log an error? If so, what is it?Parrnell
@Parrnell Well, the "This was called" is never sent to the console, so the whole method isn't being called. So nothing shows up when I NSLog the replyHandler. By errorHandler, I'm assuming you meant replyHandler. There is no errorHandler... is there another function I should be calling to check for errors (I don't see one in the documentation)Wichman
I meant the errorHandler on the sending side (the sendMessage invocation). If the message isn't delivered it should get invoked with an NSError which may hint to us why things aren't working for youParrnell
I can only NSLog that method when I run on iPhone only (without watch extension), but when I run only on iPhone I get this error: "WatchConnectivity session on paired device is not reachable." My desired result is to retrieve the message from the iPhone on the watch when the iPhone is closed.Wichman
what makes the iOS app run in the background so that it can call sendMessage?Parrnell
Calling sendMessage from the watch app will cause the iPhone app to run in the background. See the answer below.Braunite
sounds like @Braunite and I were both giving you the same help, and from the looks of it he got you sorted out below :)Parrnell
B
13

There are 2 session:didReceiveMessage methods in the WCSession. You should implement the one with a replyHandler on the Apple Watch side.

Also, is the Apple Watch screen on? SendMessage from the iPhone will only work while the Apple Watch app is active and the screen is on. SendMessage from the Apple Watch to the iPhone on the other hand will work even if the iPhone app is not launched.

If you want to request the data when using the Watch app, you could switch the Watch App to call the sendMessage method. The iPhone app would use its replyHandler to send the data you need back to the watch. If you do this, you will need to move your WCSession initialization code in the iPhone app into the application:didFinishLaunchingWithOptions method (since your view controller will not be initialized if launched in the background).

// ***On the watch***
// ExtensionDelegate
func applicationDidFinishLaunching() { 
    self.session = WCSession.defaultSession();       
    self.session.delegate = self
    self.session.activateSession()
}

// Where you want to get the data from the iPhone
self.session.sendMessage(["retrieveData":[:]], replyHandler: { (result) -> Void in
    // TODO: Handle your data from the iPhone
}, errorHandler: { (error) -> Void in
    // TODO: Handle error - iPhone many not be reachable
})

// ***On the iPhone***
// AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.session = [WCSession defaultSession];
    self.session.delegate = self;
    [self.session activateSession];
}

- (void)session:(WCSession *)session didReceiveMessage:(nonnull NSDictionary<NSString *,id> *)message replyHandler:(nonnull void (^)(NSDictionary<NSString *,id> * _Nonnull))replyHandler
{
    if ([message objectForKey:@"retrieveData"])
    {
        replyHandler(@{@"a":@"hello"});
    }
}
Braunite answered 1/2, 2016 at 15:0 Comment(10)
See my edit to the question, I changed it to the replyHandler function, but it still isn't called. Did I implement this right?Wichman
It does look like it was implemented correctly (though I recommend moving the WCSession registration out of a willAppear method and into the AppDelegate or ExtensionDelegate). Is the watch screen on when you make this call from the iPhone?Braunite
Also, do you get any error message in your errorHandler method on your iPhone method call?Braunite
When I run just on the iPhone without the watch extension. I get an error: "WatchConnectivity session on paired device is not reachable." I need to be receiving this information when the watch app is opened and the iPhone app is closed. Am I going about this the right way for my desired result?Wichman
You are close, but I think you are doing it backwards. Try moving your sendMessage code into the watch app and the session:didReceiveMessage into the phone app.Braunite
I don't want to send anything from the watch to the iPhone, only retrieve information from the iPhone to the watch. Why would I sendMessage from the watch to the iPhone? The iPhone shouldn't receive anything.. it should only send a message to the watch that the watch can access whenever it wants.Wichman
You send an empty message from the watch to the phone so the phone can return your data in the replyHandler. You basically poke the iPhone using sendMessage so it returns the data you want. See above for an example.Braunite
Also, you should keep a local instance of your WCSession. The sendMessage is async, so if you call it without a reference it will be released before it completes.Braunite
Ok, thanks I was going about this all wrong. This makes a lot more sense. I'm having trouble converting the second part "Where you want to get the data from the iPhone" into Swift. Could you update your answer with that part in Swift, please?Wichman
Great, Thanks! I edited your answer to add handling the replyHandler, since that wasn't as easy as I had first thought. +1 for sticking in there and helping me work through it.Wichman
H
0

FWIW I have a use case where the phone deliberately initiates a sort of "linking" process with the watch using messaging, and I ran into this problem. Nothing at all would fix it except for restarting the physical phone. So you can try that :-)

Holler answered 12/3, 2021 at 19:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.