1

Swift iOS HealthKit 使用案例: 获取体温列表 HKHealthStore

前言

好像国内很少有关于 swift 的教程和文章,基本都是 ObjectC 的,所以发一下。

要学 Swift 的可以去看 斯坦福大学的教程,点这里,当然,英文要好,看完就会。我就是看这个教程学会的。
另外,推荐安装 Dash 这个应用,是个查阅 API 的工具软件,学 Swift 开发必备

这个教程的开发环境:

  • XCode 11.3.1
  • iOS 13+

作一个应用,获取健康应用中的体温数据

结果如图:

body-temperature-list.JPG

关于 HealthKit 的所有实体类说明

如下图:
主要有

  1. 数据存储对象: HKObject HKSample 这两个是抽象类,使用的时候要使用其实体类,下图中说明
  2. 数据查询对象: HKQuery 用于设置各种各样的查询例子,
  3. 数据单位对象: HKUnit 可以表示所有健康数据的单位,如:英里卡路里摄氏度等等

iOS-HealthKit.png

查询数据的过程说明

  1. Info.plist 文件中添加两个值,后面的文字说明会显示在应用请求授权的窗口中

    1. 普通模式下是这样的名字 man.png
    2. 右击选择 Raw Key & Values 是这样 row.png
  2. 操作健康数据时,先判断是否支持健康数据 isHealthDataAvailable() -> Bool
  3. app 需要先向健康应用获取你所要操作的数据类型的授权,用户同意之后才能处理健康数据 requestAuthorization(toShare:, read:, completion: (Bool, Error?) -> Void)
  4. 然后需要创建查询对象,要用 HKQuery 这个抽象类下面的那几个类,在上图中有说明,这里以 HKSampleQuery(样本查询) 为例说明,不同查询类型的对应参数不同

    1. 创建的时候需要输入对应的参数
    2. 你要查询的数据类型、数据标识 sampleType 所要查询的样本类型,具体哪个类型的数据:比如,体温
    3. 查询的时间条件 predicate,这个是时间的 predicate,留空时则不筛选时间
    4. 查询的数量 limit
    5. 查询结果的排序方式 sortDescriptors
    6. 最后是回调方法 (query, results, error) query 是当前查询对象, results? 是查询到的结果, error? 是发生错误时的错误信息,最主要的操作就在此处

看代码

TemperatureTableViewController.swift

//
//  TemperatureTableViewController.swift
//  BodyTemparature
//
//  Created by Kyle on 2020/2/10.
//  Copyright © 2020 Cyan Maple. All rights reserved.
//

import UIKit
import HealthKit

/// 获取 Health 中的体温数据
class TemperatureTableViewController: UITableViewController {
        
    // 存储查询到的数据
    private var temperatureSamples: Array<HKSample> = []
    
    
    private var kit: HKHealthStore! {
        return HKHealthStore()
    }
    
    private let queryType = HKQuantityType.quantityType(forIdentifier: .bodyTemperature)!
    private let querySample = HKSampleType.quantityType(forIdentifier: .bodyTemperature)!
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        
        navigationItem.title = "体温记录 top 10"
        navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Add",
                                                            style: .plain,
                                                            target: self,
                                                            action: #selector(buttonPressed))
        
        
        // 如果 iOS 11+ 显示大标题
        if #available(iOS 11.0, *) {
            self.navigationController?.navigationBar.prefersLargeTitles = true
        }
        
        
        if HKHealthStore.isHealthDataAvailable(){
            //  Write Authorize
            let queryTypeArray: Set<HKSampleType> = [queryType]
            //  Read Authorize
            let querySampleArray: Set<HKObjectType> = [querySample]
            kit.requestAuthorization(toShare: queryTypeArray, read: querySampleArray) { (success, error) in
                if success{
                    self.getTemperatureData()
                } else {
                    self.showAlert(title: "Fail", message: "Unable to access to Health App", buttonTitle: "OK")
                }
            }
        } else {
            // show alert
            showAlert(title: "Fail", message: "设备不支持使用健康", buttonTitle: "退出")
        }
    }
    
    
    @objc func buttonPressed() {
        print("Button Pressed")
        // TODO: Add temperature in modal view
    }
    
    
    
    func getTemperatureData(){
        
        /*
        // 时间查询条件对象
        let calendar = Calendar.current
        let todayStart =  calendar.date(from: calendar.dateComponents([.year,.month,.day], from: Date()))
        let dayPredicate = HKQuery.predicateForSamples(withStart: todayStart,
                                                       end: Date(timeInterval: 24*60*60,since: todayStart!),
                                                       options: HKQueryOptions.strictStartDate) */

        // 创建查询对象
        let temperatureSampleQuery = HKSampleQuery(sampleType: querySample, // 要获取的类型对象
                                                   predicate: nil, // 时间参数,为空时则不限制时间
                                                   limit: 10, // 获取数量
                                                   sortDescriptors: [NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: false)]) // 获取到的数据排序方式
        { (query, results, error) in
            /// 获取到结果之后 results 是返回的 [HKSample]?
            if let samples = results {
                // 挨个插入到 tableView 中
                for sample in samples {
                    DispatchQueue.main.async {
                        self.temperatureSamples.append(sample)
                        self.tableView.insertRows(at: [IndexPath(row: self.temperatureSamples.firstIndex(of: sample)!, section:0)],
                                                  with: .right   )
                    }
                }
            }
        }

        // 执行查询操作
        kit.execute(temperatureSampleQuery)
    }
    
    
    /// 自定义方法:输入 HKSample 输出 日期和温度
    func getTemperatureAndDate(sample: HKSample) -> (Date, Double) {
        let quantitySample = sample as! HKQuantitySample
        let date = sample.startDate
        let temperature = quantitySample.quantity.doubleValue(for: .degreeCelsius())
        return (date, temperature)
    }
        
    // MARK: - Table view data source
    
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return temperatureSamples.count
    }
    
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "TemperatureCell", for: indexPath)
        let (date, temperature) = getTemperatureAndDate(sample: temperatureSamples[indexPath.row])
        cell.textLabel?.text = String(temperature)
        
        let dateFormatter = DateFormatter()
        dateFormatter.dateStyle = .medium
        dateFormatter.timeStyle = .short
        dateFormatter.locale = Locale(identifier: "zh_CN")
        
        cell.detailTextLabel?.text = dateFormatter.string(from: date)
        return cell
    }
    
    // MARK: - Tool Methods - Alert
    func showAlert(title: String, message: String, buttonTitle: String) {
        let alert = UIAlertController(title: title,
                                      message: message,
                                      preferredStyle: .alert)
        let okAction = UIAlertAction(title: buttonTitle, style: .default, handler: { (action) in
        })
        alert.addAction(okAction)
        DispatchQueue.main.async {
            self.present(alert, animated: true, completion: nil)
        }
    }
    
}

KyleBing
659 声望18 粉丝

前端,喜欢 Javascript scss,喜欢做一些实用的小工具