티스토리 뷰

애플 프레임워크에서 제공하는 Healthkit을 사용해 사용자의 수면정보를 가져와보겠습니다.

 

1.  Info.plist에 읽기/쓰기 권한 설정 및 표시 문구 설정


먼저, 정보 접근을 위해 사용자 동의를 얻어야합니다.

 

Health까지 치면 자동완성 됩니다.

Target > Info > Custom iOS Target Properties (or WatchOS Target Properties)

1. + 버튼을 눌러서 Privacy - Health .. 를 적어줍니다.

2. 권한은 Share(읽기권한), Update(쓰기권한) 중 필요한 것을 설정해줍니다.

3. 접근 권한을 허용할 때 뜰 문구를 Value에 써줍니다.

Record 권한은 Healthkit 샘플이 아닌 '의료기록'에 관한 접근 권한입니다. 필요에 따라 설정해주세요.

 

2. Signing & Capability에 HealthKit 추가


1. + Capability 버튼을 누릅니다.

2. HealthKit을 검색해서 추가해줍니다.

 

3. 필요에 따라 두 가지 옵션을 선택합니다. 저는 단순 읽기/쓰기 권한만 필요해서 둘 다 설정하지 않았습니다.

더보기

1. 의료 기록이 필요하다면 첫번째 옵션을 체크하고 info.plist에 record 권한을 설정해주어야합니다. 또한 코드 단 권한 설정도 다르게 해야합니다. 자세한 사항은 공식문서를 참고해주세요.
2. 백그라운드 실행에 관한 옵션입니다. Observer를 이용하여 건강 정보에 업데이트가 있을 때마다 백그라운드로 앱에 전달할 수 있습니다. 공식문서와 좋은 예제 코드를 참고하면 좋을 것 같습니다.

 

3. requestAuthorization 호출로 상세 권한 설정


import HealthKit

class HealthKitService {
    let healthStore = HKHealthStore()
    
    // 읽기 및 쓰기 권한 설정
    let read = Set([HKCategoryType.categoryType(forIdentifier: .sleepAnalysis)!])
    let share = Set([HKCategoryType.categoryType(forIdentifier: .sleepAnalysis)!])
    
    func configure() {
        // 해당 장치가 healthkit을 지원하는지 여부
        if HKHealthStore.isHealthDataAvailable() {
            requestAuthorization()
        }
    }
    
    // 권한 요청 메소드
    private func requestAuthorization() {
        self.healthStore.requestAuthorization(toShare: share, read: read) { success, error in
            if error != nil {
                print(error.debugDescription)
            }else{
                if success {
                    print("권한이 허락되었습니다")
                }else{
                    print("권한이 없습니다")
                }
            }
        }
    }
}

여러 권한을 설정할 땐 Set안에 나열해주시면 됩니다.

@main
struct MainApp: App {
    
    // Healthkit 인증 코드가 있는 객체를 선언해줍니다.
    let service = HealthKitService()
    
    var body: some Scene {
        WindowGroup {
           	ContentView()
        }
    }
    
    init() {
        setup()
    }
    
    // 첫 실행 시 Healthkit 권한 설정이 되도록 호출합니다.
    func setup() {
        service.configure()
    }
}

SwiftUI 기준으로 첫 실행시 권한 설정 함수가 호출되도록 @main이 있는 struct에 해당 함수를 호출해줍니다.

실행해보시면 이런 화면이 뜨게 됩니다.

이렇게 한번 허용을 하고 나면 이 화면은 더이상 뜨지 않습니다.

앞으로 개별 항목에 대한 권한 상태를 보고싶다면 다음과 같은 코드를 사용해야합니다.

// 수면 데이터에 대한 권한이 허용되어 있는지 확인
let sleepAnalysisType = HKCategoryType.categoryType(forIdentifier: .sleepAnalysis)!
let authorizationStatus = healthStore.authorizationStatus(for: sleepAnalysisType)

switch authorizationStatus {
case .notDetermined:
    // 권한이 아직 요청되지 않음
case .sharingDenied:
    // 권한 거부됨
case .sharingAuthorized:
    // 권한 부여됨
default:
}

 

4. 수면 데이터 가져오기


// HealthKitService.swift > class HealthKitService

func getSleepData(){
    // 수면 데이터 Type 정의
    if let sleepType = HKObjectType.categoryType(forIdentifier: HKCategoryTypeIdentifier.sleepAnalysis) {
        // 데이터를 필터링할 조건(predicate)를 설정할 수 있음. 여기선 일주일 데이터를 받아오도록 설정
        let calendar = Calendar.current
        let endDate = Date() // 현재 시간
        let startDate = calendar.date(byAdding: .day, value: -7, to: endDate) // 7일 전 시간
        let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate)
        // 최신 데이터를 먼저 가져오도록 sort 기준 정의
        let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
        // 쿼리 수행 완료시 실행할 콜백 정의
        let query = HKSampleQuery(sampleType: sleepType, predicate: predicate, limit: 20, sortDescriptors: [sortDescriptor]) { (query, tmpResult, error) -> Void in
            if error != nil {
                // 에러 처리를 수행합니다.
                print(error)
                return
            }
            if let result = tmpResult {
                for item in result {
                    if let sample = item as? HKCategorySample {
                        // 가져온 데이터 출력
                        print("Sleep value: \(sample.value)")
                        print("Start Date: \(sample.startDate)")
                        print("End Date: \(sample.endDate)")
                        print("Metadata: \(String(describing: sample.metadata))")
                        print("UUID: \(sample.uuid)")
                        print("Source: \(sample.sourceRevision)")
                        print("Device: \(String(describing: sample.device))")
                        print("---------------------------------\n")
                    }
                }
            }
        }
        // HealthKit store에서 쿼리를 실행
        healthStore.execute(query)
    }
}

HealthKit에서 정보를 가져올 때는 데이터베이스에 쿼리를 날려 조회하듯, 쿼리를 작성해야합니다.

어떤 정보를 가져올 것인지 Type을 지정한 후 조건, 정렬, 콜백 등을 작성하여 HealthStore객체의 excute메소드를 이용하면 됩니다.

 

수면데이터의 경우 시작 시간과 끝 시간을 통해 얼마나 잠을 잤는지 알 수 있습니다.

그리고 value는 수면 형태를 나타내는데요, 램수면, 깊은 수면 등등이 있습니다.

value는 Int 형태고 의미하는 바는 다음과 같습니다.

inBed = 0
asleepUnspecified = 1
awake = 2
asleepCore = 3
asleepDeep = 4
asleepREM = 5

 

참고로 수면 데이터는 위와 같이 구성되어 있습니다.

 

import SwiftUI

struct SleepTimeView: View {
    let service = HealthKitService()
    var body: some View {
        VStack{
            Button("Get sleep data") {
                service.getSleepData()
            }
        }
    }
}

간단히 뷰를 만들어서 실행해보았습니다.

 

...
Sleep value: 0
Start Date: 2023-03-27 23:01:10 +0000
End Date: 2023-03-27 23:01:15 +0000
Metadata: Optional(["HKTimeZone": Asia/Seoul])
...

디바이스 소스를 제외하면 이런 정보가 출력되네요.

Inbed 시간이 5분정도 찍혔군요

...
Sleep value: 5
Start Date: 2023-03-27 21:33:46 +0000
End Date: 2023-03-27 22:28:46 +0000
Metadata: Optional(["HKTimeZone": Asia/Seoul])
...

이날 애플워치를 차고 자서 램수면 시간을 확인할 수 있었습니다.

UTC 시간과 서울은 9시간 차이이니 3월 28일 06:33 ~ 3월 28일 07:28동안 램수면 상태였네요.

 

이제 이걸 잘 가공해서 서비스에 적용하면 될 것 같습니다.

 

 

 


읽어주셔서 감사합니다!

 

 

Ref.

공식문서

https://www.appcoda.com/sleep-analysis-healthkit/

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
글 보관함