11

我在 iOS 和 watchOS 之间同步用户凭据时遇到问题。

我的基本设置是我的 iOS 应用程序和 watchOS 应用程序都需要与后端服务器通信,并且它们都需要访问令牌才能这样做。但是,用户只能在 iOS 应用上登录(因为需要输入用户名和密码)。

现在,当用户登录 iOS(或之后打开应用程序)时,我使用 updateApplicationContext() 将用户的凭据发送到 watchOS。用户注销时也会做类似的事情,如下:

func logInOnWatch() {
    if WCSession.isSupported() {
        session.activate()

        var userInfo = [String : Any]()
        userInfo["type"] = "logInConfirm"
        userInfo[userKey.isLoggedIn] = true
        let deviceUserID = KeychainAccess.password(for: userKey.ID, service: userKey.keyChain)
        userInfo[userKey.ID] = deviceUserID
        let accessTokenSecret = KeychainAccess.password(for: userKey.accessTokenSecret, service: userKey.keyChain)
        userInfo[userKey.accessTokenSecret] = accessTokenSecret

        do {
            try session.updateApplicationContext(userInfo)
        }
        catch {
            print(error)
        }
    }
}

func logOutOnWatch() {
    if WCSession.isSupported() {
        session.activate()

        var dict = [String : Any]()
        dict["type"] = "logInConfirm"
        dict["logInStatus"] = "User Has Not Logged In"

        do {
            try session.updateApplicationContext(dict)
        }
        catch {
            print(error)
        }
    }
}

然后我接收数据并更新手表端的界面如下:

func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
    if let contextType = applicationContext["type"] as? String {
        switch contextType {
        case "logInConfirm":
            saveUserDataToDisk(from: applicationContext)
            reloadRootControllersIfNeeded()

        ... // Handle other types of data

        default:
            return
        }
    }
}

func saveUserDataToDisk(from userInfo: [String : Any]) {
    if let _ = userInfo[userKey.isLoggedIn] {
        userDefaults.set(true, forKey: userKey.isLoggedIn)
        if let token = userInfo[userKey.accessTokenSecret] as? String {
            KeychainAccess.setPassword(token, for: userKey.accessTokenSecret, service: userKey.keyChain)
        }
        if let userID = userInfo[userKey.ID] as? String {
            KeychainAccess.setPassword(userID, for: userKey.ID, service: userKey.keyChain)
        }
    }
    else {
        removeAllData()
    }
}

func reloadRootControllersIfNeeded() {
    if (userDefaults.object(forKey: userKey.isLoggedIn) == nil) {
        /// Display the logged out controller if it isn't already the root controller.
        if (loggedIn == nil) || loggedIn! {
            WKInterfaceController.reloadRootControllers(withNames: ["LoggedOutController"], contexts: [self])
        }
        loggedIn = false
    }
    else {
        /// Display the home controller if the logged out controller is the root controller.
        if (loggedIn == nil) || !loggedIn! {
            WKInterfaceController.reloadRootControllers(withNames: ["HomeController"], contexts: [self])
        }
        loggedIn = true
    }
}

当用户登录和用户注销时,这一切都很好。

但是,在登录后使用该应用程序时,手表偶尔会将根控制器切换到已注销的控制器(可能是因为它收到一条消息,告诉它从用户默认值中删除登录标志,因此告诉它注销)。

使用 Xcode 没有很好的方法来捕捉这种情况,但它偶尔会发生。更糟糕的是,当我在 iOS 上打开应用程序(从标签栏控制器的 viewDidAppear 调用 logInOnWatch())时,手表没有更新。然后更新手表的唯一方法是注销并重新登录 iOS。只有这样,手表才会收到凭据和登录标志。

在应用程序的整个生命周期中还有其他实例,在这些实例中,一旦用户登录,我就会将用户数据从 iOS 发送到 watchOS,并且这些都可以正常工作。我想知道Apple如何处理手表上的用户默认设置是否存在问题,或者可能是WatchConnectivity的问题。

有没有更好的方法来更一致地保持这些数据同步?

4

0 回答 0