Support multiple users in tvOS apps
System & Services 进阶 20m

在 tvOS 应用中支持多用户

Support multiple users in tvOS apps

2022年6月6日

在 Apple 官方观看视频

一句话判断

如果你的 tvOS 流媒体应用还在让每个家庭成员手动选择 profile,tvOS 16 的新 API 可以让这一切自动化——切换系统用户后应用自动切换到对应的 profile。

这场 Session 讲了什么

tvOS 团队的 Felipe 详细讲解了 tvOS 16 中多用户支持的新特性。Apple TV 是家庭共享设备,不同家庭成员应该看到各自个性化的内容。Session 介绍了两个关键新功能:控制中心现在可以显示 iCloud 家庭成员(即使他们还没添加到 Apple TV),以及新的用户无关 Keychain API。

核心场景是流媒体应用的 profile 管理:当一个家庭的多个人使用同一台 Apple TV 时,应用应该能自动识别当前系统用户并加载对应的 profile,而不是每次都弹出 profile 选择器。

Session 还演示了 “Runs as Current User” entitlement 配合新 Keychain API的完整工作流:每个用户有独立的 UserDefaults,但共享同一个登录凭证。

值得深挖的点

“Runs as Current User” 的精妙设计。 在 Xcode 中添加 User Management capability 后,勾选 “Runs as Current User”,应用的进程就会以当前系统用户身份运行。所有数据(包括 Keychain、UserDefaults)自动按用户隔离,不需要改一行代码。这对游戏类应用尤其友好——每个人的进度自动分离。

用户无关 Keychain 解决了登录问题。 “Runs as Current User” 默认会让 Keychain 也按用户隔离,这意味着每个家庭成员都要单独登录。新引入的 kSecUseUserIndependentKeychain 属性打破了这一限制——设置为 true 后,存储的凭证对所有用户可见,实现一次登录全家使用。

家庭用户添加流程的简化。 tvOS 16 的控制中心现在显示 iCloud 家庭成员列表,未添加的成员以 ”+” 图标标识。选择后,只需将 iPhone 靠近 Apple TV 并在 iPhone 上确认即可完成添加。

TVUserManager 的手动映射被废弃。 以前需要手动维护系统用户到应用 profile 的映射关系,tvOS 16 中这些 API 被标记为废弃。现在系统自动处理用户与数据的关联。

代码片段

import Security

// 使用用户无关 Keychain 存储登录凭证
func saveCredentials(username: String, password: String) {
    let query: [String: Any] = [
        kSecClass as String: kSecClassGenericPassword,
        kSecAttrAccount as String: username,
        kSecValueData as String: password.data(using: .utf8)!,
        // 关键:设置为用户无关 Keychain
        kSecUseUserIndependentKeychain as String: true
    ]
    
    let status = SecItemAdd(query as CFDictionary, nil)
    guard status == errSecSuccess else { return }
}

// 读取用户无关 Keychain 中的凭证
func readCredentials() -> String? {
    let query: [String: Any] = [
        kSecClass as String: kSecClassGenericPassword,
        kSecMatchLimit as String: kSecMatchLimitOne,
        kSecReturnData as String: true,
        // 读取时也需要指定用户无关 Keychain
        kSecUseUserIndependentKeychain as String: true
    ]
    
    var result: AnyObject?
    SecItemCopyMatching(query as CFDictionary, &result)
    guard let data = result as? Data else { return nil }
    return String(data: data, encoding: .utf8)
}

// 每个用户的 profile 选择存储在 UserDefaults 中
// "Runs as Current User" 下 UserDefaults 自动按用户隔离
func saveSelectedProfile(_ profileId: String) {
    UserDefaults.standard.set(profileId, forKey: "selectedProfile")
}

最佳实践

  • 为你的 tvOS 应用启用 “Runs as Current User” entitlement,让数据自动按用户隔离
  • 登录凭证使用 kSecUseUserIndependentKeychain 存储到用户无关 Keychain,实现单次登录
  • 每个用户的 profile 选择等个性化设置存储在 UserDefaults 中,系统自动隔离
  • 结合 tvOS 15 的 iPhone 辅助登录功能,以及 tvOS 16 新增的 OAuth 和 Passkey 支持,进一步简化登录流程
  • 不再需要手动维护 TVUserManager 的用户映射关系,依赖系统的自动用户关联
  • iOS 端的代码可以直接在 tvOS 上运行,UserDefaults 和 Keychain 的 API 完全一致

还有什么值得关注

  • Shared iPad 也支持类似的多用户模型,这套方案可以跨平台复用
  • OAuth 和 Passkey 在 tvOS 16 上的支持让第三方登录更加便捷
  • 如果你的应用是纯游戏类(不需要共享登录),直接开启 “Runs as Current User” 即可,无需额外处理
  • 配套观看 WWDC 2021 的 “Simplify sign-in for your tvOS apps” 了解 iPhone 辅助登录的实现细节
WWDC 2022