大多数浏览器和
Developer App 均支持流媒体播放。
-
开始在 visionOS 中使用 HealthKit
探索如何使用 HealthKit 打造能够充分发挥空间画布优势的精彩体验。了解适用于 visionOS 的 HealthKit 功能,探索如何将现有 iPadOS App 移植到 visionOS,并了解有关在客人用户会话期间管理 HealthKit 的特别注意事项。你还将学习如何将 HealthKit 与 SwiftUI、Swift Charts 和 Swift 并发搭配使用,来打造创新满满的用户体验。
章节
- 0:00 - Introduction
- 1:52 - HealthKit in visionOS
- 3:48 - Spatial health experiences
- 7:47 - Guest User support
资源
- Bringing multiple windows to your SwiftUI app
- Forum: Health & Fitness
- HealthKit
- Let others use your Apple Vision Pro
- Visualizing HealthKit State of Mind in visionOS
相关视频
WWDC24
WWDC23
WWDC20
-
下载
大家好 欢迎各位 我叫 Zach 我叫 Sirinda 我们是从事 visionOS 健康体验开发的工程师 大家可能都知道 HealthKit 提供了一个集中式 加密储存库 用于储存用户的健康数据 在获得许可的情况下 你可以访问这些数据 以便打造有助于用户 增进健康和保持健康的体验
HealthKit 的功能 包括查询和写入健康数据 以及汇总和计算 一段时间内的统计数据 你也可以注册 App 以便在数据发生变化时 在前台或后台接收更新 HealthKit 中可以储存 众多健康领域的数据 包括活动、心脏和睡眠 以及临床健康记录 例如免疫接种和手术 如需进一步了解 HealthKit 请参考我们 2020 年的讲座 “开始使用 HealthKit” 去年 随着 iPadOS 17 的发布 我们将 HealthKit 引入一个新平台 让大家能够在 iPad 上 利用储存在 HealthKit 中的数据 打造丰富的体验供用户探索 比如这个 App“Rise” 没错! 而现在 随着 visionOS 2 的推出 在 Apple Vision Pro 上 也可以使用 HealthKit 了 空间计算解锁了令人兴奋的可能性 让大家有机会构建多种 沉浸式和协作式健康体验 我们很高兴能够向大家展示 为 visionOS 构建由 HealthKit 提供支持的 App 有多么简单 在本次讲座中 我们将介绍 visionOS 中的 HealthKit 功能 然后 我们将演示一些方法 大家可以采用这些方法来打造 完美融入共享空间的体验 最后 我们将讨论 visionOS 的客人用户功能 以及 HealthKit 面向客人用户的运行方式 我们首先看看这个框架 在新平台上的功能 HealthKit 在 visionOS 中的 运行方式与在 iPadOS 中非常相似 你的 App 可以读取和写入数据 计算统计数据 还可以注册以获取更新 此外 还需要注意以下几件事情 与在其他平台上一样 用户可以在“设置”App 中 查看和编辑每个 App 的 健康数据授权 用户可通过 iCloud 定期在不同设备间进行数据同步
visionOS 中的 HealthKit 适用于使用 visionOS SDK 的 App 以及针对 iOS 17 或更高版本 编译的 iPadOS App 在今天的讲座中 我们将开发一款 App 让你能够使用新的 HealthKit 心理状态 API 来记录与日历事件相对应的情绪 我想让这款 App 登陆 visionOS 要进一步了解 如何使用心理状态 API 请观看今年的讲座 “探索 HealthKit 中的身心健康 API”
我们来了解一下基础知识 在采用 HealthKit 时 至关重要的一点是 你的 App 必须检查健康数据是否可用 这可能会因设备和操作系统版本 等因素而异 如果你希望继续支持 之前的 visionOS 版本 可以使用 isHealthDataAvailable 函数 来相应调整 App 的体验 检查完健康数据是否可用后 接下来必须请求用户授权 以读取或写入 App 需要处理的数据类型
healthDataAccessRequest 函数 将触发一条提示 请求用户 向 App 授予健康数据访问权限 最好仅请求你需要的权限 并且仅在 App 需要时提出请求
遵循接下来介绍的最佳做法 可以非常轻松地更新 iPadOS App 让它能在 visionOS 上运行 事实上 你什么都不用做! 因为我的 App 会使用我列出的 API 来检查健康数据的可用性 如果我针对 iOS 17 或更高版本 进行编译 那么这个 App 无需进行更新 即可在 visionOS 2.0 上提供 “专为 iPad 设计”的体验 这为我提供了一个绝佳的起点 接下来交给 Sirinda 由她为大家介绍一些最佳做法 如果你有意开发 需要利用空间计算的 HealthKit App 就应该考虑这些最佳做法
谢谢 Zach 我想展示一下 为 visionOS 设计 App 有多么简单 首先 我要确保我们的 App 采用 visionOS 的设计模式和交互模式 以便充分利用这个平台的优势 还要寻找机会来完善 和提升我们的体验 以便显示更多信息并 在显示的健康数据中增添细微差异
然后花一点时间 来寻找灵感 设法以更胜以往的方式 让 visionOS 上的 HealthKit 体验 真正大放异彩 我们开始吧
首先 我要添加 visionOS 作为 App 的目标平台 这样就可以在 visionOS 上 运行我们的 App 了 选取 Designed for iPad 就可以实现 Zach 刚才演示的体验 今天 我要选取 Apple Vision 以充分利用 visionOS SDK
我们的 App 将使用与 iPadOS 相同 的 HealthKit 数据授权配置 搞定! 如需进一步了解如何 让现有的 App 登陆 visionOS 请观看讲座“将你的窗口 App 提升至空间计算领域” 好了!现在 我已经针对 visionOS 构建了我们的 App 它会自动采用标准系统外观 TabView 和 Sheets 等 标准 SwiftUI 组件 可以完美地转化到 visionOS 上 由于健康数据可以跨设备同步 现在可以在 Apple Vision Pro 上 查看相同的图表和见解 由于我们的见解是 使用 Swift Charts 配置的 因此看起来已经很棒了 现在 我们的 App 看起来 完美适应这个平台 接下来 我们探索一下 如何利用这幅无限宽广的空间画布 我已经做了一些更改 来提升 visionOS 上的使用体验 在这个标签中 我让图表 可动态调整大小 这样一来 当我使用“捏合并拖移” 手势展开 App 窗口时 就能一目了然地查看更多数据
为此 我要使用 今年新推出的 SwiftUI API onGeometryChange 来观察 图表大小的变化 我可以通过增加图表点的数量 来响应更大的视图
由于空间画布上还有剩余位置 何不将两个图表 并排放在一起进行比较? 幸好 添加多窗口支持非常简单 因为我的 iPad App 已经支持分屏浏览了 要进一步了解如何配置 多窗口支持 请查看讲座备注中链接的文档 我在代码中创建了一个 Button 它可以使用新图表对应的标识符 来调用 openWindow 操作 这个操作会在新窗口中打开图表 我们还可以实现更多功能! 打造完全沉浸式的空间体验 非常简单 不必重写整个 App
例如 我想重新设计 情绪记录体验 以便充分利用 visionOS 的沉浸式功能 假设我刚刚结束了 一场集体讨论会议 想要反思一下自己的感受
借助 Apple Vision Pro 我可以进入一个沉浸式空间 其他 App 处于隐藏状态 周围环境透视也会变暗 从而营造出一种 与工作空间隔离的感觉 借助这种不受干扰的方式 我可以通过多姿多彩的沉浸式体验 来反思最近的日历活动
轻点 Save to HealthKit 后 透视的颜色会变淡 我的 App 会重新显示 这样就可以 回到上次离开的位置继续进行 得益于所有这些更新 我们的 App 能够完美适应 visionOS 我们增强了现有的体验 甚至还打造了一些全新体验 很好 现在 你的 App 已经 针对 visionOS 进行了自定 你还需要做一件事 这件事与客人用户有关
客人用户是 visionOS 的 一项功能 允许其他人 设置和试用 Apple Vision Pro 这是体验空间计算的绝佳方式 并且提供了能够保护 所有者数据和隐私的功能 如需详细了解客人用户功能 请查看 Apple 支持提供的文章 “让其他人使用 你的 Apple Vision Pro”
在客人用户会话期间 客人可以使用所有者 授权的 App 在大多数情况下 使用体验不会变化 但在使用 HealthKit 时 有一些特别的注意事项 如果 App 已经获得了授权 则在客人运行 App 时 App 仍然可以访问 HealthKit 数据 但是 App 无法向客人请求 额外的 HealthKit 授权 而且如果提出这样的请求 系统也不会显示授权流程 而是会返回错误 此外 客人也无法 在 visionOS 设置中 编辑授权、隐私或安全选项 此外 以客人用户身份 与健康数据交互时 在数据写入方面也存在一项限制 在客人用户会话期间 无法将健康数据写入 HealthKit 这是为了防止客人生成的数据 与所有者的健康数据混淆 如果 App 在客人用户会话期间 尝试写入数据 则 HealthKit 会返回新的错误 通过处理这个错误 你可以安全地丢弃 客人生成的数据 确保稍后不会将这些数据 存储到所有者的健康数据储存库 在这里也可以触发提醒 向客人说明 数据未保存 感谢你详细介绍客人用户功能 更新 App 时 要牢记这些注意事项 正如 Zach 提到的 系统不会向客人显示授权表单 这意味着在客人用户会话期间 对 healthDataAccessRequest 的 任何调用都会失败并返回错误 我们建议将请求推迟到以后 此外 客人用户可能还会 尝试写入数据 但是 样本将无法保存到 健康数据储存库 因此 我更新了收到错误信息时的 现有处理逻辑 正如 Zach 所说 收到这条错误信息时 我们建议丢弃由客人生成的 健康数据样本 最后 我们需要通知客人用户 他们的数据未保存 这段代码让用户能够选取一种情绪 在这里 我修改了这个按钮 这样一来 每次在客人尝试记录情绪 而跳出错误时 都会显示一条提醒 现在 我们已经对 App 进行了配置 以便为客人用户提供顺畅无缝的体验 接下来 我们要确保 这些更改能起到预期的效果 当我以客人用户身份打开 App 时 我不会看到任何健康数据授权提示 这是因为客人在运行 App 时 会继承所有者给予的授权 因此 我可以获得 和所有者相同的 App 使用体验 但是 如果尝试记录情绪 轻点这个事件 然后进行选择 完成操作后 系统会显示一条提醒 通知我 以客人用户身份 生成的数据未保存 这种处理错误的方式 能让客人体验 App 的功能 同时可以避免将客人的数据 添加到所有者的健康数据储存库中 现在 我已经对 App 进行了配置 以便处理客人用户功能 接下来 我要 在 Apple Vision Pro 中 与朋友和家人共享我的 App 请牢记以下几项最佳做法 客人用户无法 进行健康数据访问授权 因此请务必更新你的授权调用 以便妥善处理这一错误
请务必丢弃客人用户尝试写入的 任何健康数据 这有助于防止客人生成的数据 与所有者的健康数据混淆 最后 请记住 如果所有者 已经对 App 进行了授权 则 App 可以继续读取健康数据 遵循上述最佳做法 对于确保客人用户和所有者 都能始终享有顺畅无缝的体验 至关重要! 谢谢 Sirinda 说得太好了 总结一下 HealthKit 即将登陆 visionOS 在这个新平台上支持健康数据的 读取、写入和观察 在 visionOS 上采用 HealthKit 时 请借此机会完善并重新设计 App 体验 以便充分利用空间计算 别忘了根据 HealthKit 面向客人用户的运行方式调整代码 如需了解更多信息 可以观看以下讲座 此外 请务必下载我们的示例 App 以查看示例代码 我们很高兴能够 将 HealthKit 引入 visionOS 期待看到大家 构建的各种精彩体验 感谢各位的参与!
-
-
2:43 - Check whether health data is available
import HealthKit if HKHealthStore.isHealthDataAvailable() { // Configure HealthKit-powered experiences } else { // Omit HealthKit experiences }
-
3:03 - Request authorization to read or write data
import HealthKitUI import SwiftUI func healthDataAccessRequest( store: HKHealthStore, shareTypes: Set<HKSampleType>, readTypes: Set<HKObjectType>? = nil, trigger: some Equatable, completion: @escaping (Result<Bool, any Error>) -> Void ) -> some View
-
5:59 - Update number of chart points based on chart’s size
// Update number of chart points based on chart’s size import SwiftUI import HealthKit import Charts struct ChartView: View { @State var chartBinCount: Int var body: some View { Chart { ... // Chart body } .onGeometryChange(for: Int.self) { proxy in // Observe for changes to the chart’s size Int(proxy.size.width / 80) // 80 points per chart point } action: { newValue in // Update the number of chart points chartBinCount = newValue } } }
-
6:33 - Open chart as a new window
// Opens chart as a new window struct NewChartViewerButton: View { @Environment(\.openWindow) private var openWindow var body: some View { Button("Open In New Window", systemImage: "plus.rectangle.on.rectangle") { openWindow(id: "chart-viewer-window") } } }
-
9:00 - HealthKit returns a new error if a write is attempted during a Guest User session
let sample = HKStateOfMind(date: date, kind: .momentaryEmotion, valence: valence, labels: [label], associations: [association]) do { try await healthStore.save(sample) } catch { switch error { case HKError.errorNotPermissibleForGuestUserMode: // Drop data generated in a Guest User session default: // Existing error handling } }
-
9:26 - Request authorization to State of Mind datatype
// Request authorization to State of Mind datatype @main struct HKStateOfMindDataSampleApp: App { @State var toggleHealthDataAuthorization = false @State var healthDataAuthorized: Bool? var body: some Scene { WindowGroup { TabView { ... } .healthDataAccessRequest(store: healthStore, shareTypes: [.stateOfMindType()], readTypes: [.stateOfMindType()], trigger: toggleHealthDataAuthorization) { result in switch result { case .success: healthDataAuthorized = true case .failure(let error as HKError): switch (error.code) { case .errorNotPermissibleForGuestUserMode: // Defer requests for a later time default: // Existing error handling } ... } } } } }
-
9:42 - Save a State of Mind sample from an emoji type
// Saves a State of Mind sample from an emoji type public func saveSample(date: Date, association: HKStateOfMind.Association, healthStore: HKHealthStore, didError: Binding<Bool>) async -> SaveDetails? { do { let sample = createSample(date: date, association: association) try await healthStore.save(sample) } catch { switch error { case HKError.errorNotPermissibleForGuestUserMode: // Drop data you generate in a Guest User session. didError.wrappedValue = true return SaveDetails(errorString: "Health data is not saved for Guest Users.") default: // Existing error handling. didError.wrappedValue = true return SaveDetails(errorString: "Health data could not be saved: \(error)") } } ...
-
9:58 - Present an alert with a message using the given details
// Present an alert with a message using the given details struct EventView: View { @State private var showAlert: Bool = false @State private var saveDetails: EmojiType.SaveDetails? = nil var body: some View { EmojiPicker() .alert("Unable to Save Health Data", isPresented: $showAlert, presenting: saveDetails, actions: { _ in }, // default OK button message: { details in Text(details.errorString) }) } }
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。