可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have an app that has a very rich network layer and my apple watch app depends on all the models. Unfortunately the app is not modular enough to make this layer available in the watch app.
I solved this problem by using openParentApplication: to wake up the iPhone app, perform the request and give back the results.
In watchOS 2 this method is gone and I should use WatchConnectivity. The best way to use this would be by sending userInfo dictionaries.
But how can I wake up the iPhone app to handle my requests? To get notifications about new userInfos I have to use the WCSessionDelegate and for that I need a WCSession object. But when should I create that? And how to wake up the app?
回答1:
I asked an Apple Engineer about this and got the following tip: The iOS-App should be started in a background-task. So the following worked for me pretty well:
UIApplication *application = [UIApplication sharedApplication]; __block UIBackgroundTaskIdentifier identifier = UIBackgroundTaskInvalid; dispatch_block_t endBlock = ^ { if (identifier != UIBackgroundTaskInvalid) { [application endBackgroundTask:identifier]; } identifier = UIBackgroundTaskInvalid; }; identifier = [application beginBackgroundTaskWithExpirationHandler:endBlock];
Add this to your session:didReceiveMessage:
or session:didReceiveMessageData:
method to start a background task with a three minute timeout.
回答2:
Swift version of Benjamin Herzog's suggestion below. Of note, while I did choose to push the work initiated by the Watch to a background task, as long as my app was in the background, the system woke it up just fine. Doing the work in a background task did not appear to be required, but is best practice.
override init() { super.init() if WCSession.isSupported() { session = WCSession.defaultSession() session.delegate = self session.activateSession() } } func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { let taskID = self.beginBackgroundUpdateTask() //Do work here... self.endBackgroundUpdateTask(taskID) }) var replyValues = Dictionary() let status = "\(NSDate()): iPhone message: App received and processed a message: \(message)." print(status) replyValues["status"] = status replyHandler(replyValues) } func beginBackgroundUpdateTask() -> UIBackgroundTaskIdentifier { return UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler({}) } func endBackgroundUpdateTask(taskID: UIBackgroundTaskIdentifier) { UIApplication.sharedApplication().endBackgroundTask(taskID) }
回答3:
In the WatchKit extension you will want to use the WatchConnectivity WCSession sendMessage APIs. In the extension check that the iOS app is reachable, and then send the message:
let session = WCSession.defaultSession(); if session.reachable { let message = ["foo":"bar"] session.sendMessage(message, replyHandler: nil, errorHandler: { (error) -> Void in print("send failed with error \(error)") }) }
This message will cause the system to wake the iOS app in the background, so make sure to set up the WCSession in a piece of the iOS app code that gets called when running in the background (as an example: you don't want to put it in a UIViewController's subclass's viewDidLoad) so that the message is received. Since you will be requesting some information you might want to take advantage of the reply block.
So this is how you accomplish launching the iOS app in the background, though the WatchConnectivity WWDC session recommended trying to use the "background" transfer methods if possible. If your watch app is read-only then you should be able to queue up any changes on the iOS device using the background transfers and then they will be delivered to the watch app next time it runs.