> For the complete documentation index, see [llms.txt](https://jacky-chen.gitbook.io/jackychen/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://jacky-chen.gitbook.io/jackychen/foundation/healthkit.md).

# HealthKit

### 教學連結

* [**How to Import and Export Health App Data using Swift**](https://www.youtube.com/watch?v=y1aWWC0MpNs\&t=174s)
* [**HealthKit Tutorial With Swift: Getting Started**](https://www.raywenderlich.com/159019/healthkit-tutorial-swift-getting-started)

### Memo

* 三大重點：取得資料授權、讀、寫
  * How to request permission and access HealthKit data
  * How to read HealthKit data
  * How to write data to HealthKit’s central repository   &#x20;
* 授權

```swift
//3. Prepare a list of types you want HealthKit to read and write
let healthKitTypesToWrite: Set<HKSampleType> = [bodyMassIndex,
                                                activeEnergy,
                                                HKObjectType.workoutType()]

let healthKitTypesToRead: Set<HKObjectType> = [dateOfBirth,
                                               bloodType,
                                               biologicalSex,
                                               bodyMassIndex,
                                               height,
                                               bodyMass,
                                               HKObjectType.workoutType()]
//4. Request Authorization
HKHealthStore().requestAuthorization(toShare: healthKitTypesToWrite,
                                     read: healthKitTypesToRead) { (success, error) in
  completion(success, error)
}
```

* 讀

```swift
class func getMostRecentSample(for sampleType: HKSampleType,
                               completion: @escaping (HKQuantitySample?, Error?) -> Swift.Void) {

//1. Use HKQuery to load the most recent samples.  
let mostRecentPredicate = HKQuery.predicateForSamples(withStart: Date.distantPast,
                                                      end: Date(),
                                                      options: .strictEndDate)

let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierStartDate,
                                      ascending: false)

let limit = 1

let sampleQuery = HKSampleQuery(sampleType: sampleType,
                                predicate: mostRecentPredicate,
                                limit: limit,
                                sortDescriptors: [sortDescriptor]) { (query, samples, error) in

    //2. Always dispatch to the main thread when complete.
    DispatchQueue.main.async {

      guard let samples = samples,
            let mostRecentSample = samples.first as? HKQuantitySample else {

            completion(nil, error)
            return
      }

      completion(mostRecentSample, nil)
    }
  }

HKHealthStore().execute(sampleQuery)
}
```

* 寫

```swift
class func saveBodyMassIndexSample(bodyMassIndex: Double, date: Date) {

  //1.  Make sure the body mass type exists  
  guard let bodyMassIndexType = HKQuantityType.quantityType(forIdentifier: .bodyMassIndex) else {
    fatalError("Body Mass Index Type is no longer available in HealthKit")
  }

  //2.  Use the Count HKUnit to create a body mass quantity
  let bodyMassQuantity = HKQuantity(unit: HKUnit.count(),
                                    doubleValue: bodyMassIndex)

  let bodyMassIndexSample = HKQuantitySample(type: bodyMassIndexType,
                                             quantity: bodyMassQuantity,
                                             start: date,
                                             end: date)

  //3.  Save the same to HealthKit
  HKHealthStore().save(bodyMassIndexSample) { (success, error) in

    if let error = error {
      print("Error Saving BMI Sample: \(error.localizedDescription)")
    } else {
      print("Successfully saved BMI Sample")
    }
  }
}
```

## Tell if data was user entered manually

### 教學連結

* [**Ignore manual entries from Apple Health app as Data Source**](https://stackoverflow.com/questions/31184628/ignore-manual-entries-from-apple-health-app-as-data-source/31254817#31254817)

  ```objectivec
  [NSPredicate predicateWithFormat:@"metadata.%K != YES", HKMetadataKeyWasUserEntered];
  ```
* [**Detect if HealthKit activity has been entered manually**](https://stackoverflow.com/questions/32147812/detect-if-healthkit-activity-has-been-entered-manually)
  * The bundle identifier of the entry if made manually will be **com.apple.Health**, which is the bundle identifier of the Health app. Notice the capital H. When you pull your data just ignore the data which has a bundle identifier of com.apple.Health.

    That way you will be only considering activities which are not manual
* [**Using predicates to filter out sources when doing a query**](https://stackoverflow.com/questions/29311891/using-predicates-to-filter-out-sources-when-doing-a-query)

### Memo

* 非手動輸入

```
(lldb) po samples[1].source.bundleIdentifier
"com.apple.health.6AE18D38-5413-4679-BDBA-7FBD1DA9A45C"

(lldb) po samples[1].source.name
"電商科的iPhoneX"

(lldb) po samples[1].metadata
nil
```

* 手動輸入

```
(lldb) po samples[2].source.bundleIdentifier
"com.apple.Health"

(lldb) po samples[2].source.name
"健康"

(lldb) po samples[2].metadata
▿ Optional<Dictionary<String, Any>>
  ▿ some : 1 element
    ▿ 0 : 2 elements
      - key : "HKWasUserEntered"
      - value : 1
```

## Create Multiple predicate

### 教學連結

* [**HealthKit: create a predicate for specific sources and date range**](https://stackoverflow.com/questions/34400682/healthkit-create-a-predicate-for-specific-sources-and-date-range)
  * Use NSCompoundPredicate
* [**NSCompoundPredicate**](https://developer.apple.com/documentation/foundation/nscompoundpredicate)
* [ **程式範例**](https://blog.csdn.net/kmyhy/article/details/78356497)

  ```objectivec
  //1. Get all workouts with the "Other" activity type.
  let workoutPredicate = HKQuery.predicateForWorkouts(with: .other)

  //2. Get all workouts that only came from this app.
  let sourcePredicate = HKQuery.predicateForObjects(from: HKSource.default())

  //3. Combine the predicates into a single predicate.
  let compound = NSCompoundPredicate(andPredicateWithSubpredicates: [workoutPredicate,
                                                                     sourcePredicate])
  ```

## 獲取統計數據

### 教學連結

* [**HKStatisticsCollectionQuery**](https://developer.apple.com/documentation/healthkit/hkstatisticscollectionquer)
* [**Get total step count for every date in HealthKit**](https://stackoverflow.com/questions/29582462/get-total-step-count-for-every-date-in-healthkit)

### Memo

* 心率單位

  ```swift
  HKUnit(from: "count/min")
  or
  HKUnit.count().unitDivided(by: HKUnit.minute())
  ```
