스트리밍은 대부분의 브라우저와
Developer 앱에서 사용할 수 있습니다.
-
visionOS에서 HealthKit 시작하기
HealthKit을 사용하여 무한한 가능성의 공간을 효과적으로 활용하는 경험을 만들어 보세요. visionOS에서 활용할 수 있는 HealthKit의 기능에 대해 자세히 알아보고, 기존 iPadOS 앱을 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은 건강 데이터의 쿼리 및 작성, 시간 경과에 따른 데이터 통계의 집계 및 계산 등의 기능을 포함하고 있습니다 데이터가 변경될 때 포그라운드 또는 백그라운드에서 업데이트를 받도록 앱을 등록할 수도 있죠 HealthKit에는 활동, 심장, 수면 등 광범위한 건강 영역에 관한 데이터와 예방 접종, 시술 등의 임상 건강 정보 기록을 보관할 수 있죠 HealthKit에 대한 자세한 내용은 2020년에 진행한 ‘HealthKit 시작하기’ 세션에서 확인하세요 Apple은 작년에 iPadOS 17을 출시하면서 새 플랫폼에 HealthKit을 도입하여 개발자가 HealthKit에 저장된 데이터를 활용하는 풍부한 iPad용 경험을 개발할 수 있도록 했습니다, 이 RISE 앱처럼요 맞습니다 이제 visionOS 2를 통해 Apple Vision Pro에서 HealthKit을 사용할 수 있습니다 공간 컴퓨팅 덕분에 흥미롭고 다양한 몰입형 및 협업형 건강 경험을 구축할 수 있습니다 오늘은 간편하게 visionOS를 위한 HealthKit 기반 앱을 개발하는 방법을 보여드리겠습니다 이 프레젠테이션에서는 visionOS용 HealthKit의 기능을 살펴본 후 공유 공간과 잘 어우러지는 경험을 구축하는 방법을 알려 드리겠습니다 마지막으로 visionOS의 게스트 사용자 기능을 살펴본 후 게스트 사용자에 대해 HealthKit이 작동하는 방식을 설명하겠습니다 먼저 새 플랫폼에서 HealthKit이 갖는 기능을 알아보겠습니다 visionOS의 HealthKit은 iPadOS의 HealthKit과 유사하게 작동하죠 앱은 데이터를 읽고 작성하고, 통계를 계산하고 업데이트를 등록할 수 있습니다 유의해야 할 사항이 몇 가지 있습니다 다른 플랫폼에서와 마찬가지로 사용자는 설정 앱에서 각 앱의 건강 데이터에 대한 권한을 확인하고 수정할 수 있죠 또한 사용자는 iCloud를 통해 주기적으로 기기 간에 데이터를 동기화할 수 있습니다
visionOS용 HealthKit은 iOS 17 및 이후 버전을 대상으로 컴파일된 visionOS SDK 및 iPadOS 앱에서 사용할 수 있습니다 오늘 세션을 위해 저희는 새로운 HealthKit State of Mind API를 사용하여 캘린더 일정에 따라 감정을 기록할 수 있는 앱을 준비했는데요, 이제 이 앱을 visionOS로 가져오겠습니다 State of Mind API를 사용하는 방법을 자세히 알아보려면 올해 진행된 ‘HealthKit의 Wellbeing API 살펴보기’ 세션을 확인해 보세요
기본 사항을 복습해 보죠 HealthKit을 채택할 때는 건강 데이터를 사용할 수 있는지 앱에서 확인할 수 있어야 합니다 이는 기기 및 OS 버전에 따라 다를 수 있죠 이전 visionOS 버전을 계속 지원하려면 isHealthDataAvailable 함수를 사용하여 앱 경험을 적절히 조정하세요 건강 데이터의 사용 가능 여부를 확인한 후에는 앱에서 필요한 데이터 유형을 읽거나 작성할 수 있도록 사용자에게 권한을 요청해야 합니다
healthDataAccessRequest 함수를 사용하면 건강 데이터에 대한 권한을 앱에 부여하도록 사용자에게 요청할 수 있죠 앱에서 필요한 데이터만, 필요할 때만 요청하는 것이 좋습니다
이러한 권장 사항을 따르면 iPadOS 앱이 visionOS에서 실행되도록 간편하게 업데이트할 수 있습니다 사실 아무것도 하지 않아도 됩니다 아까 설명한 API를 통해 앱에서 건강 데이터 사용 가능 여부를 확인하므로 앱을 iOS 17 및 이후 버전을 대상으로 컴파일하면 이미 앱은 visionOS 2.0에서 iPad용으로 설계된 경험을 제공하죠 업데이트할 필요도 없습니다 흥미로운 내용으로 프레젠테이션을 시작했으니 이제 Sirinda에게 넘기겠습니다 Sirinda는 공간 컴퓨팅을 활용하는 HealthKit 앱을 개발하는 데 관심이 있는 경우 고려해야 할 몇 가지 모범 사례를 설명해 드릴 것입니다
고마워요, Zach 간편하게 visionOS를 위한 앱을 개발하는 방법을 보여드리겠습니다 우선 앱이 visionOS의 디자인과 상호작용 패턴을 채택하여 플랫폼의 강점을 활용할 수 있도록 할 것입니다 또한 더 많은 정보를 표시하고 표시되는 건강 데이터에 더 많은 의미를 부여하기 위해 경험을 개선할 것입니다
그런 다음 잠시 시간을 내어 visionOS에서 우수한 HealthKit 경험을 제공하기 위해 한층 더 경험을 개선할 방법을 살펴보겠습니다 이제 시작하겠습니다
우선 앱 타겟의 대상으로 visionOS를 추가합니다 이렇게 하면 앱이 visionOS에서 실행될 수 있습니다 iPad용으로 설계된 경험을 선택하면 Zach이 시연한 경험을 볼 수 있습니다 오늘은 visionOS SDK를 최대한 활용하기 위해 Apple Vision을 선택하겠습니다
앱은 iPadOS에서와 동일한 HealthKit 데이터 승인 구성을 사용합니다 제대로 작동하네요 기존 앱을 visionOS로 가져오는 방법에 대한 자세한 내용은 ‘공간 컴퓨팅에 맞게 윈도우형 앱 향상하기’ 세션에서 확인하세요 좋습니다 visionOS를 위해 앱을 빌드했으므로 표준 시스템 디자인이 앱에 자동으로 적용됩니다 TabView, sheet와 같은 표준 SwiftUI 구성 요소는 visionOS에서 원활하게 작동합니다 건강 데이터는 여러 기기에서 동기화되므로 이제 Apple Vision Pro에서 동일한 차트와 정보를 확인할 수 있습니다 앱의 정보는 Swift의 Chart로 구성되므로 이 또한 원활하게 작동하죠 visionOS에서 앱이 잘 작동하므로 이제 무한한 가능성의 공간을 사용하는 방법을 살펴보겠습니다 visionOS에서의 경험을 개선하기 위해 이미 몇 가지 변경 사항을 적용했습니다 이 탭에서는 차트가 동적으로 크기가 조정되도록 하여 사용자가 앱 윈도우를 확장하기 위해 손가락을 오므리고 드래그하면 더 많은 데이터를 한눈에 볼 수 있습니다
이를 위해서는 올해 새로 출시된 SwiftUI API인 onGeometryChange로 차트 크기의 변화를 관찰합니다 보기가 더 커지면 이에 따라 차트 포인트의 수를 늘릴 수 있습니다
공간 캔버스에 아직 자리가 남아 있으니 두 차트를 나란히 비교해 보면 어떨까요? iPad 앱에서 이미 Split View를 지원하므로 멀티 윈도우에 대한 지원을 쉽게 추가할 수 있습니다 멀티 윈도우 지원을 구성하는 방법을 자세히 알아보려면 세션 자료에서 링크로 제공된 문서를 확인하세요 코드에서 새 차트에 해당하는 식별자를 사용하여 openWindow 액션을 호출하는 Button을 만들겠습니다 이 액션은 차트를 새 윈도우에서 열죠 여기에서 한층 더 나아가죠 앱을 완전히 새로 작성하지 않아도 몰입감 넘치는 공간 경험을 만들 수 있습니다
예를 들어 visionOS의 몰입감 넘치는 기능을 활용하기 위해 앱의 감정 기록 경험을 다시 구성해 보겠습니다 방금 브레인스토밍을 마친 다음 기분을 되새겨 보려 합니다
Apple Vision Pro에서 사용자는 몰입감 넘치는 공간에 들어갈 수 있죠 다른 앱이 숨겨지고 주변 물체가 흐려지므로 작업 공간으로부터 분리된 느낌을 선사하죠 방해할 요소 없이 다채로운 몰입형 경험에서 최근 캘린더 이벤트를 되돌아볼 수 있죠
Save to HealthKit을 탭하면 주변 물체의 색상이 사라지고 앱이 다시 표시되므로 중단한 작업을 바로 계속할 수 있습니다 이러한 업데이트를 통해 앱을 visionOS에 맞게 조정하고 기존 경험을 개선하고 새 경험도 만들 수 있었습니다 멋지네요 이제 앱을 visionOS에 맞춰 조정했으므로 마지막으로 할 일이 남았습니다, 게스트 사용자와 관련된 작업이죠
게스트 사용자는 다른 사용자들이 Apple Vision Pro를 설정하고 사용해 볼 수 있도록 하는 visionOS 기능입니다 공간 컴퓨팅을 경험하기 좋은 방법이며 소유자의 개인정보와 데이터를 보호하는 기능이 포함되어 있죠 게스트 사용자에 대한 자세한 정보는 Apple 지원의 ‘다른 사용자가 Apple Vision Pro를 사용해 보도록 하기’ 도움말에서 확인하세요
게스트 사용자 세션을 통해 게스트는 소유자가 승인한 앱을 사용할 수 있습니다 대부분의 경우 경험이 달라지지 않지만 HealthKit으로 작업할 때는 몇 가지 특별한 고려 사항이 있습니다 게스트를 위해 실행되는 앱은 이미 승인된 경우 HealthKit 데이터에 접근할 수 있습니다 하지만 앱은 게스트에게 추가 HealthKit 승인을 요청할 수 없으며 요청하는 경우에도 승인 흐름이 표시되지 않으며 오류가 반환됩니다 또한 게스트는 visionOS 설정에서 승인, 개인정보 보호 보안 옵션을 수정할 수 없습니다 게스트 사용자가 건강 데이터와 상호작용할 때 적용되는 제한 사항이 한 가지 더 있는데, 데이터 작성과 관련된 것입니다 게스트 사용자 세션에서는 건강 데이터가 HealthKit에 작성될 수 없습니다 이는 게스트가 생성한 데이터와 소유자의 건강 데이터가 섞이는 것을 방지하기 위함입니다 게스트 사용자 세션 중에 앱에서 데이터 작성을 시도하면 HealthKit에서 새 오류가 반환되죠 이 오류를 처리하면 게스트가 생성한 데이터를 안전하게 삭제하여 나중에 소유자의 건강 데이터에 저장되는 것을 방지할 수 있습니다 이 위치는 또한 게스트에게 데이터가 저장되지 않음을 알리는 경고를 트리거하기에 좋습니다 게스트 사용자에 대해 설명해 줘서 고마워요 언급된 고려 사항을 염두에 두고 앱을 업데이트해 보겠습니다 Zach이 언급했듯이 승인 시트는 게스트 사용자에게 제공되지 않습니다 즉 게스트 사용자 세션 중에는 healthDataAccessRequest에 대한 모든 호출이 오류를 반환하며 실패할 것입니다 요청을 나중에 처리하기 위해 연기하는 것이 좋습니다 게스트 사용자는 또한 데이터 작성을 시도할 수 있습니다 그러나 샘플은 healthStore에 저장되지 않으므로 발생하는 오류를 처리하도록 기존 로직을 업데이트합니다 Zach이 말했듯이 이 오류가 수신되면 게스트가 생성한 건강 데이터 샘플을 삭제하는 것이 좋습니다 마지막으로, 게스트 사용자에게 데이터가 저장되지 않았다는 것을 알리고 싶죠 이것은 사용자가 감정을 선택할 수 있도록 하는 코드입니다 게스트가 감정을 기록하려고 시도함에 따라 오류가 트리거될 때마다 경고가 표시되도록 버튼을 수정했습니다 원활한 게스트 사용자 경험을 위해 앱을 구성했으므로 변경 사항이 의도한 대로 작동하는지 확인해 보겠습니다 게스트 사용자로서 앱을 열면 어떠한 건강 데이터 승인 프롬프트도 표시되지 않아야 합니다 이는 게스트를 위해 실행되는 앱에는 소유자가 부여한 승인이 적용되기 때문이죠 즉 게스트 사용자와 소유자의 앱 경험은 동일합니다 그래도 감정을 기록해 보겠습니다 이 이벤트를 탭하고 감정을 선택하겠습니다 작업을 완료하면 경고가 표시됩니다 게스트 사용자이므로 내 데이터가 저장되지 않았음을 알려 주죠 이러한 방식으로 오류를 처리하면 보관된 소유자의 건강 데이터에 데이터를 추가하지 않고도 게스트 사용자가 앱을 체험해 볼 수 있습니다 이제 게스트 사용자를 처리하도록 앱을 구성했으므로 Apple Vision Pro의 앱을 친구 및 가족과 함께 사용할 수 있습니다 유의해야 할 몇 가지 모범 사례가 있습니다 게스트 사용자는 건강 데이터에 대한 접근을 승인할 수 없으므로 이 오류가 적절하게 처리되도록 승인 호출을 업데이트하세요
게스트 사용자가 작성하려고 시도한 모든 건강 데이터를 삭제하는 것을 잊지 마세요 이는 게스트가 생성한 데이터와 소유자의 건강 데이터가 섞이는 것을 방지하기 위함입니다 마지막으로, 소유자가 이미 승인한 경우 앱에서 계속 건강 데이터를 읽을 수 있습니다 이러한 모범 사례를 준수하면 게스트 사용자 및 소유자 모두에게 원활한 경험을 유지하는 데 중요합니다 Sirinda, 고마워요 훌륭한 발표였습니다 요약하자면 visionOS에 HealthKit이 출시되어 새 플랫폼에서 건강 데이터의 읽기, 작성 및 관찰이 가능해집니다 HealthKit 채택 시 공간 컴퓨팅에 대한 경험을 개선하고 이에 대해 다시 생각해 볼 기회를 가지세요 또한 게스트 사용자에 대해 HealthKit이 작동하는 방식을 고려하여 코드를 조정하세요 자세한 내용은 이 세션에서 확인할 수 있으며 코드 예시를 보려면 샘플 앱을 다운로드하시면 됩니다 visionOS에 HealthKit을 제공하게 되어 매우 기쁘게 생각합니다 훌륭한 경험을 빌드하시기 바랍니다 시청해 주셔서 감사합니다
-
-
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) }) } }
-
-
찾고 계신 콘텐츠가 있나요? 위에 주제를 입력하고 원하는 내용을 바로 검색해 보세요.
쿼리를 제출하는 중에 오류가 발생했습니다. 인터넷 연결을 확인하고 다시 시도해 주세요.