1. 애플워치 추가하기
- File - New - Target 로 애플워치 앱 target를 추가해줍니다.
선택사항이 있는데 종류의 의미는 다음과 같습니다.
Watch-only App
- 이 옵션은 Apple Watch 에서만 실행되는 독립적인 watchOS 앱을 생성합니다. iOS 앱이 없어도 동작합니다
Watch App with New Companion iOS App
- 이 옵션은 watchOS 앱과 함께 새로운 iOS 앱을 동시에 생성하여 동반 앱으로 설정합니다.
Watch App for Existing iOS App (기존 iOS 앱을 위한 워치 앱)
- 이 옵션은 현재 존재하는 iOS 앱 프로젝트에 watchOS 앱 타깃을 추가하여 동반 앱으로 설정합니다.
2. Bundle Identifier, Info.plist 설정하기
XCode에서 애플워치를 사용하기 위해서는 Bundle Indentifier 를 수정해야합니다. WatchKit 앱의 번들 ID는 생성된 앱의 Bundle Identifier에 .watchkitapp 을 추가한 형태여야 합니다. (다음과 같이 말이죠)
다음으로는 Info.plist 를 설정해야 합니다.
WatchKit 앱 대상의 Info.plist 파일을 열고 WKCompanionAppBundleIdentifier 키의 값을 새 iOS 앱 번들 식별자로 업데이트합니다. 이것은 WatchKit 앱과 연동되는 iOS 앱의 Bundle Identifier를 설정하는 것 입니다.
추가로 NSHealthUpdateUsageDescription 를 설정하여 사용자한테 심박수를 측정하기 위한 요청에 대한 설명을 작성합니다.
추가로 NSHealthShareUsageDescription 를 설정하여 사용자한테 건강앱과 데이터를 공유하는 것에 대한 설명을 작성합니다.
마지막으로 Capability 에서 HealthKit 을 추가해야 합니다.
3. 권한 받기
필요한 권한을 다음과 같이 설정합니다. 현재 심박수를 가져오기 때문에 .heartRate를 통해서 심박수 권한을 요청합니다.
코드 설명
HKQuantityType(.heartRate) 를 사용하여 숫자 값인 샘플 타입을 나타냅니다.
func requestAuthorization(toShare: Set<HKSampleType>?, read: Set<HKObjectType>?, completion: (Bool, (any Error)?) -> Void)를 사용하여 toShare로 지정한 타입의 데이터를 읽거나 쓸 수 있도록 권한을 요청합니다.
func autorizeHealthKit() {
let healthKitTypes: Set = [HKQuantityType(.heartRate)]
healthStore.requestAuthorization(toShare: healthKitTypes, read: healthKitTypes) { _, _ in }
}
4. 심박수 쿼리 시작
HealthKit 프레임워크를 사용하여 심박수 데이터를 조회를 시작합니다.
코드 설명
HKQuery.predicateForObjects(from:) 메서드를 통해서 현재 데이터를 가져올 기기를 사용자 아이폰으로 설정합니다.
updateHandler 객체를 만들어 측정값을 samples에 담아내고 process 함수를 실행합니다.
HKAnchoredObjectQuery(type:, predicate, anchor, limit, resultsHandler:)메서드로 수행할 쿼리를 만들고 핸들러를 추가합니다.
healthStore.execute(HKQuery)로 제공된 쿼리를 시작하며 계속해서 결과를 Handler를 통해서 받아 작업을 수행합니다.
.execute는 healthStore.stop(HKQuery)로 중단할 수 있습니다
private func startHeartRateQuery(quantityTypeIdentifier: HKQuantityTypeIdentifier) {
let devicePredicate = HKQuery.predicateForObjects(from: [HKDevice.local()])
let updateHandler: (HKAnchoredObjectQuery, [HKSample]?, [HKDeletedObject]?, HKQueryAnchor?, Error?) -> Void = {
query, samples, deletedObjects, queryAnchor, error in
guard let samples = samples as? [HKQuantitySample] else {
return
}
self.process(samples, type: quantityTypeIdentifier)
}
let query = HKAnchoredObjectQuery(type: HKQuantityType(quantityTypeIdentifier), predicate: devicePredicate, anchor: nil, limit: HKObjectQueryNoLimit, resultsHandler: updateHandler)
query.updateHandler = updateHandler
healthStore.execute(query)
}
5. 워치로부터 받은 심박수 다루기
애플워치로부터 심박수 데이터를 측정하여 healthStore로 수행한 쿼리의 핸들러로 받은 심박수를 처리할 수 있습니다.
예제 코드
심박수는 다음과 같이 samples에 저장되어 있으며 만약 .heartRate 타입이라면 저장 후 UI에 보여줍니다.
private func process(_ samples: [HKQuantitySample], type: HKQuantityTypeIdentifier) {
var lastHeartRate = 0.0
for sample in samples {
if type == .heartRate {
lastHeartRate = sample.quantity.doubleValue(for: heartRateQuantity)
}
self.value = Int(lastHeartRate)
}
}
전체 코드
import SwiftUI
import HealthKit
struct test: View {
private var healthStore = HKHealthStore()
let heartRateQuantity = HKUnit(from: "count/min")
@State private var value = 0
var body: some View {
VStack{
HStack{
Text("❤️")
.font(.system(size: 50))
Spacer()
}
HStack{
Text("\(value)")
.fontWeight(.regular)
.font(.system(size: 70))
Text("BPM")
.font(.headline)
.fontWeight(.bold)
.foregroundColor(Color.red)
.padding(.bottom, 28.0)
Spacer()
}
}
.padding()
.onAppear(perform: start)
}
func start() {
autorizeHealthKit()
startHeartRateQuery(quantityTypeIdentifier: .heartRate)
}
func autorizeHealthKit() {
let healthKitTypes: Set = [HKQuantityType(.heartRate)]
healthStore.requestAuthorization(toShare: healthKitTypes, read: healthKitTypes) { _, _ in }
}
private func startHeartRateQuery(quantityTypeIdentifier: HKQuantityTypeIdentifier) {
let devicePredicate = HKQuery.predicateForObjects(from: [HKDevice.local()])
let updateHandler: (HKAnchoredObjectQuery, [HKSample]?, [HKDeletedObject]?, HKQueryAnchor?, Error?) -> Void = {
query, samples, deletedObjects, queryAnchor, error in
guard let samples = samples as? [HKQuantitySample] else {
return
}
self.process(samples, type: quantityTypeIdentifier)
}
let query = HKAnchoredObjectQuery(type: HKQuantityType(quantityTypeIdentifier), predicate: devicePredicate, anchor: nil, limit: HKObjectQueryNoLimit, resultsHandler: updateHandler)
query.updateHandler = updateHandler
healthStore.execute(query)
}
private func process(_ samples: [HKQuantitySample], type: HKQuantityTypeIdentifier) {
var lastHeartRate = 0.0
for sample in samples {
if type == .heartRate {
lastHeartRate = sample.quantity.doubleValue(for: heartRateQuantity)
}
self.value = Int(lastHeartRate)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
test()
}
}
'SwiftUI' 카테고리의 다른 글
SwiftData 로 영구저장하기 (0) | 2024.03.18 |
---|---|
WCSession 애플워치 아이폰 간 데이터 전송 (0) | 2024.03.14 |
사용자의 운동 경로 기록 가져오기 (0) | 2024.03.13 |
사용자의 운동 경로 기록하기 (0) | 2024.03.13 |
애플워치와 아이폰 간 데이터 통신 (0) | 2024.03.12 |