9

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)!)
}
MSU_Bulldog
  • 3,501
  • 5
  • 37
  • 73
  • If you add an NSLog to the errorHandler, does it log an error? If so, what is it? – ccjensen Feb 01 '16 at 15:07
  • @ccjensen 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) – MSU_Bulldog Feb 01 '16 at 15:11
  • 1
    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 you – ccjensen Feb 01 '16 at 15:31
  • 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. – MSU_Bulldog Feb 01 '16 at 15:33
  • what makes the iOS app run in the background so that it can call sendMessage? – ccjensen Feb 01 '16 at 15:46
  • 1
    Calling sendMessage from the watch app will cause the iPhone app to run in the background. See the answer below. – lehn0058 Feb 01 '16 at 15:54
  • 1
    sounds like @lehn0058 and I were both giving you the same help, and from the looks of it he got you sorted out below :) – ccjensen Feb 01 '16 at 17:29

2 Answers2

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"});
    }
}
lehn0058
  • 19,977
  • 15
  • 69
  • 109
  • See my edit to the question, I changed it to the replyHandler function, but it still isn't called. Did I implement this right? – MSU_Bulldog Feb 01 '16 at 15:04
  • 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? – lehn0058 Feb 01 '16 at 15:22
  • Also, do you get any error message in your errorHandler method on your iPhone method call? – lehn0058 Feb 01 '16 at 15:25
  • 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? – MSU_Bulldog Feb 01 '16 at 15:27
  • 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. – lehn0058 Feb 01 '16 at 15:33
  • 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. – MSU_Bulldog Feb 01 '16 at 15:38
  • 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. – lehn0058 Feb 01 '16 at 15:39
  • 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. – lehn0058 Feb 01 '16 at 15:48
  • 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? – MSU_Bulldog Feb 01 '16 at 15:58
  • 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. – MSU_Bulldog Feb 01 '16 at 16:22
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 :-)

David
  • 95
  • 1
  • 7