为现有应用添加Watch Extension:如何检测登录状态及传递登录数据
刚好之前做过类似的需求,给你梳理下Watch App和手机主应用同步登录状态、传递登录数据的靠谱方案,核心就是用苹果官方的WatchConnectivity框架,这是目前跨设备同步最稳定的方式:
一、先搞定WatchConnectivity的基础配置
不管是手机主应用还是Watch Extension,都得先把会话搭起来:
- 两个target都要导入
WatchConnectivity框架,并且确保Bundle ID符合关联规则(比如主应用是com.yourapp.main,Watch App是com.yourapp.main.watchkitapp,Extension是com.yourapp.main.watchkitapp.watchkitextension)。 - 在手机端的
AppDelegate/SceneDelegate,以及Watch端的ExtensionDelegate里,激活会话:
手机端代码示例:
import WatchConnectivity class AppDelegate: UIResponder, UIApplicationDelegate, WCSessionDelegate { var session: WCSession? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { if WCSession.isSupported() { session = WCSession.default session?.delegate = self session?.activate() } return true } // 必须实现的代理方法,处理会话激活结果 func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {} }
Watch端ExtensionDelegate代码示例:
import WatchConnectivity import WatchKit class ExtensionDelegate: NSObject, WKExtensionDelegate, WCSessionDelegate { var session: WCSession? func applicationDidFinishLaunching() { if WCSession.isSupported() { session = WCSession.default session?.delegate = self session?.activate() } } func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {} }
二、检查手机端的登录状态
有两种实用的方式,按需选:
主动请求式:Watch App启动时,主动问手机端“用户登录了吗?”
在Watch的界面控制器里发请求:import WatchConnectivity class MainInterfaceController: WKInterfaceController { override func awake(withContext context: Any?) { super.awake(withContext: context) // 先判断手机端是否可达 if WCSession.default.isReachable { WCSession.default.sendMessage(["action": "check_login"], replyHandler: { response in if let isLoggedIn = response["is_logged_in"] as? Bool { if isLoggedIn { // 跳转到登录后的界面 } else { // 显示提示,让用户打开手机端登录 } } }, errorHandler: { error in print("请求登录状态失败:\(error.localizedDescription)") }) } } }手机端的代理里处理这个请求:
func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) { if message["action"] as? String == "check_login" { // 从手机端的存储(比如Keychain/UserDefaults)拿登录状态 let isLoggedIn = UserDefaults.standard.bool(forKey: "user_has_logged_in") replyHandler(["is_logged_in": isLoggedIn]) } }被动同步式:手机端登录状态变了,主动推给Watch
比如手机端登录成功后,直接发状态给Watch:// 手机端登录成功后调用这个方法 func syncLoginStatusToWatch() { if WCSession.default.isReachable { WCSession.default.sendMessage(["is_logged_in": true], replyHandler: nil, errorHandler: nil) } // 用updateApplicationContext做持久化同步,即使Watch没启动,下次打开也能拿到最新状态 do { try WCSession.default.updateApplicationContext(["is_logged_in": true]) } catch { print("更新上下文失败:\(error)") } }Watch端监听上下文变化:
func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) { if let isLoggedIn = applicationContext["is_logged_in"] as? Bool { // 更新Watch端的登录状态和界面 } }
三、传递登录数据到Watch App
登录数据(比如token、用户ID)属于敏感信息,一定要用Keychain存储,同时通过WatchConnectivity传递:
实时传递(sendMessage):手机端登录成功后立刻发数据
// 手机端代码 func sendLoginDataToWatch() { guard let token = KeychainManager.shared.getAuthToken(), let userId = UserDefaults.standard.string(forKey: "user_id") else { return } let loginData = [ "auth_token": token, "user_id": userId, "nickname": UserDefaults.standard.string(forKey: "user_nickname") ?? "" ] if WCSession.default.isReachable { WCSession.default.sendMessage(["login_data": loginData], replyHandler: { _ in print("登录数据已发送给Watch") }, errorHandler: { error in print("发送登录数据失败:\(error)") }) } }Watch端接收并存储:
func session(_ session: WCSession, didReceiveMessage message: [String : Any]) { if let loginData = message["login_data"] as? [String: Any] { // 敏感数据存Keychain,普通数据存UserDefaults if let token = loginData["auth_token"] as? String { WatchKeychainManager.shared.saveAuthToken(token) } if let userId = loginData["user_id"] as? String { UserDefaults.standard.set(userId, forKey: "watch_user_id") } // 更新界面到登录后状态 } }持久化同步(updateApplicationContext):防止实时发送失败,保证Watch下次启动能拿到数据
// 手机端代码 func updateLoginContext() { guard let token = KeychainManager.shared.getAuthToken(), let userId = UserDefaults.standard.string(forKey: "user_id") else { return } let context = [ "is_logged_in": true, "auth_token": token, "user_id": userId ] do { try WCSession.default.updateApplicationContext(context) } catch { print("更新登录上下文失败:\(error)") } }Watch端启动时读取:
override func awake(withContext context: Any?) { super.awake(withContext: context) let currentContext = WCSession.default.applicationContext if let isLoggedIn = currentContext["is_logged_in"] as? Bool, isLoggedIn { if let token = currentContext["auth_token"] as? String { WatchKeychainManager.shared.saveAuthToken(token) } // 初始化登录后的逻辑 } }
四、几个关键注意事项
- 敏感数据(比如token)绝对不能用UserDefaults存储,一定要用Keychain,避免明文泄露。
- 要处理会话不可达的情况(比如手机端没打开),这时候Watch端可以显示提示,引导用户打开手机应用完成登录。
- 两个端的WCSession代理方法要完整实现,尤其是会话激活的方法,否则会话会无法正常工作。
内容的提问来源于stack exchange,提问作者adm v
相关产品推荐
相关产品推荐

