WatchOS2 WCSession sendMessage doesn't wake iPhone on background

后端 未结 3 1331
悲&欢浪女
悲&欢浪女 2021-01-04 04:17

This is being tested on both Simulator and real physical device iphone5s. I tried to use WCSession sendMessage to communicate from WatchOS2 extension to iPhone iOS9 code. It

相关标签:
3条回答
  • 2021-01-04 04:51

    After hours of trying and hint from @jeron. I finally figured out the problem myself.

    In my session:didReceiveMessage delegate method, I have two calls. 1.replyHandler call. 2. I have an async process running (RXPromise) in my case, It nested quite a few RXPromise callbacks to fetch various data from cloud service. I didn't pay attention to it, because it is supposed to call and return right away. But now that I commented out RXPromise block all together, it can wake up iOS app in the background every time.

    Finally I figure out the trouble make is because after RXPromise call, it is not guaranty to be landed back to main thread anymore. And I believe session:didReceiveMessage has to be return on the main thread. I didn't see this mentioned anywhere on the Apple documentation.

    Final solution:

    - (void)session:(WCSession *)session
        didReceiveMessage:(NSDictionary<NSString *, id> *)message
             replyHandler:(void (^)(NSDictionary<NSString *, id> *_Nonnull))replyHandler {
    
        replyHandler(@{ @"schedule" : @"OK" });
    
        dispatch_async(dispatch_get_main_queue(), ^{
          Nested RXPromise calls.....
        });
    
    }
    
    0 讨论(0)
  • 2021-01-04 04:54

    It is important that you activate the WCSession in your AppDelegate didFinishLaunchingWithOptions method. Also you have to set the WCSessionDelegate there. If you do it somewhere else, the code might not be executed when the system starts the killed app in the background.

    Also, you are supposed to send the reply via the replyHandler. If you try to send someway else, the system waits for a reply that never comes. Hence the timeout error.

    Here is an example that wakes up the app if it is killed:

    In the WatchExtension:

    Setup the session. Typically in your ExtensionDelegate:

    func applicationDidFinishLaunching() {
        if WCSession.isSupported() {
            let session = WCSession.defaultSession()
            session.delegate = self
            session.activateSession()
        }
    }
    

    And then send the message when you need something from the app:

    if WCSession.defaultSession().reachable {
        let messageDict = ["message": "hello iPhone!"]
        WCSession.defaultSession().sendMessage(messageDict, replyHandler: { (replyDict) -> Void in
            print(replyDict)
            }, errorHandler: { (error) -> Void in
            print(error)
        }
    }
    

    In the iPhone App:

    Same session setup, but this time also set the delegate:

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        ...
        if WCSession.isSupported() {
            let session = WCSession.defaultSession()
            session.delegate = self
            session.activateSession()
        }
    }
    

    And then implement the delegate method to send the reply to the watch:

    func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) {
        replyHandler(["message": "Hello Watch!"])
    }
    

    This works whenever there is a connection between the Watch and the iPhone. If the app is not running, the system starts it in the background.

    I don't know if the system waits long enough until you received your data from iCloud, but this example definitely wakes up the app.

    0 讨论(0)
  • 2021-01-04 04:55

    Well, you can use transferUserInfo in order to queue the calls. Using sendMessage will result in errors when app is killed

    0 讨论(0)
提交回复
热议问题