如何从Apple Watch直接在iPhone后台启动父应用?
如何从Apple Watch让iPhone父应用以后台模式启动?
你提到的Endomondo这类应用的场景确实很典型——Watch端点击触发后,iPhone父应用在后台自动启动并同步GPS数据。正如你所说,openParentApplication(_:reply:)早就被废弃了(仅支持watchOS 1),现在我们得用Watch Connectivity框架来实现这个需求,下面是具体的实现思路和步骤:
核心原理
Watch Connectivity框架里的sendMessage(_:replyHandler:errorHandler:)和sendMessageData(_:replyHandler:errorHandler:)方法,是目前触发iPhone父应用后台启动的标准方式。这两个方法会强制唤醒处于后台(甚至未运行)的iPhone应用,让它处理来自Watch的消息,完美匹配你需要的场景。
具体实现步骤
1. 两端配置Watch Connectivity
首先要在Watch Extension和iPhone父应用中都完成WCSession的初始化:
iPhone端代码(比如AppDelegate)
import WatchConnectivity class AppDelegate: UIResponder, UIApplicationDelegate, WCSessionDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // 检查设备是否支持Watch Connectivity guard WCSession.isSupported() else { return true } let session = WCSession.default session.delegate = self session.activate() return true } // 必须实现的代理方法,处理会话激活状态 func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {} }
Watch Extension端代码(比如ExtensionDelegate)
import WatchConnectivity import WatchKit class ExtensionDelegate: NSObject, WKExtensionDelegate, WCSessionDelegate { func applicationDidFinishLaunching() { guard WCSession.isSupported() else { return } let session = WCSession.default session.delegate = self session.activate() } func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {} }
2. Watch端发送触发消息
在Watch端需要启动父应用的时机(比如点击“开始”按钮时),发送消息给iPhone:
// Watch端按钮点击事件里的代码 @IBAction func startButtonTapped() { guard WCSession.default.isReachable else { // 处理设备未连接的情况,比如提示用户 print("iPhone未连接,请确保设备配对且蓝牙开启") return } // 发送自定义指令,告诉iPhone要执行什么操作 let message = ["action": "startGPS"] WCSession.default.sendMessage(message, replyHandler: { replyData in // 处理iPhone返回的结果,比如GPS数据 if let latitude = replyData["latitude"] as? Double, let longitude = replyData["longitude"] as? Double { print("收到GPS位置:\(latitude), \(longitude)") // 更新Watch界面 } }, errorHandler: { error in print("发送消息失败:\(error.localizedDescription)") }) }
3. iPhone端处理消息并执行后台任务
在iPhone的WCSession代理中实现消息接收方法,这里可以执行后台操作(比如获取GPS),如果需要长时间运行,还要申请后台任务权限:
// iPhone端WCSession代理方法 func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) { guard let action = message["action"] as? String, action == "startGPS" else { replyHandler(["error": "无效指令"]) return } // 申请后台任务权限,避免操作被系统中断 var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid backgroundTaskID = UIApplication.shared.beginBackgroundTask(withName: "WatchTriggeredGPS", expirationHandler: { // 任务超时,结束后台任务 UIApplication.shared.endBackgroundTask(backgroundTaskID) backgroundTaskID = .invalid }) // 执行你的后台操作:比如获取当前GPS位置 // 这里只是示例,实际需要用CoreLocation框架来获取真实位置 let gpsData = [ "latitude": 37.7749, "longitude": -122.4194 ] // 将结果返回给Watch replyHandler(gpsData) // 结束后台任务 UIApplication.shared.endBackgroundTask(backgroundTaskID) backgroundTaskID = .invalid }
关键注意事项
- 后台权限配置:如果你的父应用需要在后台获取GPS,必须在iPhone的
Info.plist中添加UIBackgroundModes键,包含location值(对应“位置更新”后台模式)。 - 可达性检查:
sendMessage要求Watch和iPhone处于蓝牙连接状态,如果设备断开,消息会失败。如果需要离线同步,可以用transferUserInfo(_:),但这个方法不会立即唤醒后台应用,只会在应用下次启动时处理数据。 - 真机测试:Watch Connectivity的通讯在模拟器上可能不稳定,建议用真机配对测试。
内容的提问来源于stack exchange,提问作者Oskar Gusgård




