在此HealthKit文章中,您将学习如何请求访问HealthKit数据的权限,以及如何向中央HealthKit存储库读写数据。 本文使用Swift 4,iOS 11,Xcode 9版本。
HealthKit是iOS 8中引入的
API。HealthKit充当所有与健康相关的数据的中央存储库,允许用户创建生物学档案并存储锻炼数据。
阅读HealthKit文章时,您将创建最简单的培训跟踪应用程序并学习:
- 如何请求权限和访问HealthKit数据
- 如何读取HealthKit数据并将其显示在UITableView中
- 如何将数据写入中央HealthKit存储库
准备好开始使用HealthKit了吗? 继续阅读!
注意: 要学习本教程,您将需要一个有效的iOS开发人员帐户。 否则,您将无法启用HealthKit功能并无法访问HealthKit存储库。开始
入门应用程序在锻炼程序期间跟踪卡路里燃烧。 对于好莱坞内部人士和名流而言,很明显,我在谈论
Prancercise 。
下载入门项目并在Xcode中打开它 。
编译并运行该应用程序。 您将看到用户界面的“骨架”。 在接下来的两篇文章中,您将逐步为该应用程序添加功能。
分配团队
HealthKit是一个特殊的框架。 如果您没有有效的开发人员帐户,则该应用程序将无法使用它。 拥有开发者帐户后,您可以分配您的团队。
在项目浏览器中选择
PrancerciseTracker ,然后选择
PrancerciseTracker target 。 转到
常规选项卡,然后单击
团队字段。
选择与您的开发者帐户关联的命令:
权限/权利
HealthKit也具有自己的一组权限,您需要启用它们才能创建使用该框架的应用程序。
在目标编辑器中打开“
功能”选项卡并启用
HealthKit ,如以下屏幕截图所示:

等待Xcode为您配置HealthKit。 通常,这里没有问题,但是如果忘记正确指定组和捆绑包标识符,仍然会遇到一些问题。
现在一切就绪。 您只需要询问用户使用HealthKit的权限即可。
权限
HealthKit可处理机密和敏感数据。 并不是每个人都感到很舒服,以至于允许已安装的应用程序访问此信息。
这就是HealthKit具有强大的隐私系统的原因。 HealthKit仅有权访问用户同意共享的数据。 要为Prancercise Tracker的用户创建配置文件,必须首先获得访问每种数据类型的权限。
用法说明更新
首先,您需要描述为什么要求用户提供健康指标。
Xcode使您能够在应用程序的
Info.plist文件中指定此代码。
打开
Info.plist 。 然后添加以下键:
隐私 -
健康共享使用说明隐私 -运行状况
更新使用情况说明这些键存储将在HeathKit登录屏幕出现时显示的文本。
Health Share使用说明是指应从HealthKit读取的数据部分。
运行状况更新使用情况说明与写入HealthKit的数据相对应。
您可以在此处“添加”所需的所有内容。 这通常是一个描述:“我们将使用您的健康信息来更好地跟踪您的锻炼情况。”
请记住,如果未安装这些密钥,则当您尝试登录HealthKit时应用程序将崩溃。
HealthKit授权
打开
HealthKitSetupAssistant.swift文件,其中是用于在HealthKit中进行授权的类方法。
class func authorizeHealthKit(completion: @escaping (Bool, Error?) -> Swift.Void) { }
authorizeHealthKit(completion :)方法不带任何参数,并且具有返回布尔值(
成功或
失败 )和可选错误(如果万一出问题)的补全。 如果返回错误,则在两种情况下将它们传递给complition:
- HealthKit可能在您的设备上不可用。 例如,如果应用程序在iPad上运行。
- 在当前版本的HealthKit中,某些数据类型可能不可用。
让我们打破这个过程。 要授权HealthKit,
authorizeHealthKit(completion :)方法必须完成以下四个步骤:
- 检查Healthkit在此设备上是否可用。 如果不是这种情况,则将失败和错误返回至完成状态。
- 准备健康数据类型。 Prancercise Tracker将读取和写入HealthKit。
- 将此数据组织为要读取的类型和要写入的类型的列表。
- 请求授权。 如果此操作成功,则所有活动均正确并正确完成。
HealthKit可用性检查
首先,您需要检查设备上HealthKit的可用性。
将以下代码粘贴到
authorizeHealthKit(completion :)方法的开头:
您会经常与
HKHealthStore互动。 它是存储用户健康数据的中央存储库。
isHealthDataAvailable()方法将帮助您了解当前用户设备是否支持Heathkit数据。
如果HealthKit在设备上不可用,则guard语句会阻止应用程序执行其余的
authorizeHealthKit(completion :)方法。 发生这种情况时,会用
notAvailableOnDevice错误
调用完成块。 您可以将其简单地输出到控制台或主控制器中,以在发生此类错误时处理进一步的步骤。
资料准备
一旦知道HealthKit在用户的设备上可用,就该准备将要读写HealthKit的数据类型了。
HealthKit使用
HKObjectType类型。 进入或返回到中央HealthKit存储库的每种类型都是一种
HKObjectType 。 您还将看到HKSampleType和
HKWorkoutType 。 它们都继承自
HKObjectType ,因此基本上这是同一件事。
在第一个代码段之后立即粘贴以下代码段:
哇,这是个大
后卫 ! 这也是使用单个防护程序检索多个选项的一个很好的例子。
若要为这些特征创建
HKObjectType ,您需要使用
HKObjectType.characteristicType(forIdentifier :)或
HKObjectType.quantityType(forIdentifier :)特征类型和数量类型是框架定义的枚举。 HealthKit与他们一起穿靴。
您还将注意到,如果一种特性或选择类型不可用,则该方法将失败。 这是故意的。 您的应用程序应该始终确切知道可以使用哪种类型的HealthKit。
准备用于读取和写入的数据类型列表
现在该准备一份用于读取和写入的数据类型列表了。
在第二部分之后立即将第三个代码粘贴到
authorizeHealthKit(completion :)方法中:
HealthKit期望一组代表用户可以写入的数据类型的
HKSampleType对象,并且还期望为您的应用程序显示一组HKObjectType对象。
HKObjectType.workoutType()是
HKObjectType的特殊类型。 它是任何锻炼。
HealthKit授权
最后一部分是最简单的。 您只需要从HealthKit请求授权。 粘贴最后一段代码:
此代码从HealthKit请求授权,然后调用完成。 它们将变量用于成功操作以及从HKHealthStore的
requestAuthorization(toShare:read:complete :)方法传递的错误。
您可以将其视为重定向。 您无需将数据包传递给HealthKitSetupAssistant内部,而是将数据包传递给主控制器,该主控制器可能显示警告或采取其他措施。
该项目已经有一个Authorize HealthKit按钮,并在MasterViewController中调用
authorizeHealthKit()方法。 这是调用我们刚刚编写的授权方法的理想场所。
打开
MasterViewController.swift ,找到
authorizeHealthKit( )方法并粘贴以下代码:
HealthKitSetupAssistant.authorizeHealthKit { (authorized, error) in guard authorized else { let baseMessage = "HealthKit Authorization Failed" if let error = error { print("\(baseMessage). Reason: \(error.localizedDescription)") } else { print(baseMessage) } return } print("HealthKit Successfully Authorized.") }
此代码使用您刚刚实现的
authorizeHealthKit(completion :)方法。 完成后,它将在控制台中显示一条消息,指示在HealthKit中授权是否成功。
启动应用程序。 在主窗口中单击“授权HealthKit”,您将看到一个弹出授权屏幕:
打开所有开关,滚动查看所有开关,然后按
允许 。 在控制台中,您应该看到以下消息:
HealthKit Successfully Authorized.
太好了! 该应用程序可以访问HealthKit的中央存储库。 现在是时候开始跟踪项目了。
特性和样品
在本节中,您将学习:
- 如何阅读用户的生物学特征。
- 如何读写不同类型的样本(重量,高度等)
通常,生物学特性是不会改变的元素类型,就像您的血型一样。 样品是经常变化的元素,例如重量。
为了正确跟踪
Prancercise锻炼
模式的有效性,
Prancercise Tracker应用程序必须接收用户体重和身高的样本。 这些样本一起可用于计算体重指数(BMI)。
注意: 体重指数(BMI)是人体脂肪的一种广泛使用的指标,是根据一个人的体重和身高计算出来的。 在此处了解更多信息。阅读规范
Prancercise Tracker不记录生物学特征。 他从HealthKit获得它们。 这意味着这些特征必须首先存储在中央HeathKit存储库中。
如果您还没有这样做,是时候告诉
HeathKit更多有关您自己的信息。
在设备或模拟器上打开“运行状况”应用程序。 选择“健康数据”选项卡。 然后,单击右上角的配置文件图标以查看您的健康配置文件。 单击
编辑,然后输入信息的出生日期,性别,血型:
现在,
HealthKit知道您的出生日期,性别和血型,是时候在
Prancercise Tracker中阅读这些功能了。
返回
Xcode并打开
ProfileDataStore.swift 。
ProfileDataStore类代表您对用户所有与健康相关的数据的访问点。
将以下方法粘贴到
ProfileDataStore中 :
class func getAgeSexAndBloodType() throws -> (age: Int, biologicalSex: HKBiologicalSex, bloodType: HKBloodType) { let healthKitStore = HKHealthStore() do {
getAgeSexAndBloodType()方法调用
HKHealthStore ,询问用户的出生日期,性别和血型。 它还使用出生日期计算用户的年龄。
- 您可能已经注意到,此方法可能会导致错误。 只要在HealthKit中央存储库中没有存储生日,性别或血型,就会发生这种情况。 由于您刚刚在应用程序中输入了此信息,因此不会引起错误。
- 使用Calendar类,可以将任何日期转换为一组Date Components 。 如果您想获得一年的约会日期,这真的很方便。 该代码仅获取您的出生年份,当前年份,然后计算差异。
- “扩展”变量的命名方式很显然,您需要从包装类( HKBiologicalSexObject和HKBloodTypeObject )中访问基本枚举。
UI更新
如果现在编译并运行该应用程序,则您尚未在用户界面中看到任何更改,因为尚未将此逻辑连接到它。
打开
ProfileViewController.swif t并找到
loadAndDisplayAgeSexAndBloodType ( )方法
此方法将使用您的
ProfileDataStore将生物学特征加载到用户界面中。
将以下代码粘贴到
loadAndDisplayAgeSexAndBloodType()方法中:
do { let userAgeSexAndBloodType = try ProfileDataStore.getAgeSexAndBloodType() userHealthProfile.age = userAgeSexAndBloodType.age userHealthProfile.biologicalSex = userAgeSexAndBloodType.biologicalSex userHealthProfile.bloodType = userAgeSexAndBloodType.bloodType updateLabels() } catch let error { self.displayAlert(for: error) }
该代码块将年龄,性别和血液类型加载为元组。 然后,他在UserHealthProfile模型的本地实例中设置这些字段。 最后,它通过调用
updateLabels()方法,使用
UserHealthProfile中的新字段更新用户界面。
由于
ProfileDataStore的 getAgeSexAndBloodType()方法可能会引发错误,因此
ProfileViewController必须对其进行处理。 在这种情况下,您只需采取错误并将其显示为警告。
这一切都很棒,但有一个陷阱。
updateLabels()方法尚未
执行任何操作。 这只是一个空白广告。 这次,让我们进入用户界面。
找到
updateLabels()方法并将此代码粘贴到其中:
if let age = userHealthProfile.age { ageLabel.text = "\(age)" } if let biologicalSex = userHealthProfile.biologicalSex { biologicalSexLabel.text = biologicalSex.stringRepresentation } if let bloodType = userHealthProfile.bloodType { bloodTypeLabel.text = bloodType.stringRepresentation }
代码很简单。 如果用户设置了年龄,它将被格式化为标签。 生物性别和血型也是如此。 变量stringRepresentation将枚举转换为字符串以用于显示。
编译并运行该应用程序。 转到配置文件和BMI屏幕。 单击读取HealthKit数据按钮。
如果您以前在应用程序中输入了信息,则该信息应显示在此屏幕的快捷方式中。 如果您尚未这样做,则会出现一条错误消息。
哇! 您可以直接从
HealthKit读取和显示数据。
查询样本
现在是时候读取用户的体重和身高了。 它们将用于在配置文件视图中计算和显示BMI。
生物学特性很容易获得,因为它们几乎不会改变。 样品需要更复杂的方法。 他们使用
HKQuery ,更确切地说是
HKSampleQuery 。
要从HealthKit请求样品,您将需要:
- 指定您要索取的样品类型(重量,高度等),
- 一些有助于过滤和排序数据的其他选项。 为此,可以传递一个可选的NSPredicate或NSSortDescriptors数组。
注意: 如果您熟悉CoreData,您可能会注意到一些相似之处。 HKSampleQuery与对象类型的NSFetchedRequest非常相似,您可以在其中指定谓词和排序描述符,然后设置对象的上下文以执行查询以获取结果。设置查询后,只需调用
HKHealthStore ExecuteQuery()方法即可获取结果。
对于
Prancercise Tracker,您将创建一个通用功能,该功能可以下载任何类型的最新样本。 因此,您可以将其用于体重和身高。
打开
ProfileDataStore.swift并将以下方法粘贴到该类中,位于
getAgeSexAndBloodType()方法的正下方:
class func getMostRecentSample(for sampleType: HKSampleType, completion: @escaping (HKQuantitySample?, Error?) -> Swift.Void) {
此方法使用样品的类型(身高,体重,BMI等)。 然后,他创建一个查询以检索此类型的最后一个样本。 如果您查看增长样本,您将返回上一个增长记录。
这里发生了很多事情。 我将停止解释一些事情。
- HKQuery中有几种方法可以帮助您过滤出HealthKit查询示例。 在这种情况下,我们使用内置的日期谓词。
- 从HealthKit请求样品是一个异步过程。 这就是为什么在Dispatch块内找到完成处理程序中的代码的原因。 主线程上需要合规。 否则,应用程序将失败。
如果一切顺利,您的请求将被执行,并且您将在
主线程上返回一个简洁的示例,
ProfileViewController可以在该
示例中将其内容放置在标签中。 现在开始做这部分。
在用户界面中显示样本
在上一节中,您是从
HealthKit下载数据的。 将它们另存为
ProfileViewController中的模型,然后使用
ProfileViewController updateLabels()方法更新行中的内容
您所要做的就是通过添加一个函数来扩展此过程,该函数可以加载样本,为用户界面处理它们,然后调用
updateLabels()来用文本填充标签。
打开
ProfileViewController.swift文件,找到
loadAndDisplayMostRecentHeight ( )方法并粘贴以下代码:
- 此方法首先创建一种生长样本。 然后,他将这种类型的样本传递给您刚刚编写的方法,该方法将返回HealthKit中记录的最新用户增长样本。
- 一旦样本返回,增长将转换为米并存储在UserHealthProfile模型中。 然后, UI将更新。
注意: 通常您希望将数量样本转换为标准单位。 为此,以上代码使用doubleValue(for :)方法,该方法允许您将所需的适当数据(在本例中为meter )传递给HKUnit 。
您可以使用HealthKit提供的一些常见类方法来创建各种类型的HKUnit 。 要获取计数器,您只需使用HKUnit中的meter()方法,这将是您所需要的。随着增长的整理。 重量呢? 一切都非常相似,但是您需要在
ProfileViewController中填充
loadAndDisplayMostRecentWeight ()方法。
将以下代码粘贴到
loadAndDisplayMostRecentWeight()方法中:
guard let weightSampleType = HKSampleType.quantityType(forIdentifier: .bodyMass) else { print("Body Mass Sample Type is no longer available in HealthKit") return } ProfileDataStore.getMostRecentSample(for: weightSampleType) { (sample, error) in guard let sample = sample else { if let error = error { self.displayAlert(for: error) } return } let weightInKilograms = sample.quantity.doubleValue(for: HKUnit.gramUnit(with: .kilo)) self.userHealthProfile.weightInKilograms = weightInKilograms self.updateLabels() }
您创建想要接收的样本类型,为其请求
HealthKit ,进行一些单位转换,将其保存在模型中并更新用户界面。
目前,它可以表明工作已完成,但是还有其他事情。
updateLabels()函数无法
识别您已经可以使用的新数据。
让我们修复它。
将以下行添加到
updateLabels()函数中,在展开血型以在用户界面上显示它的部分的正下方:
if let weight = userHealthProfile.weightInKilograms { let weightFormatter = MassFormatter() weightFormatter.isForPersonMassUse = true weightLabel.text = weightFormatter.string(fromKilograms: weight) } if let height = userHealthProfile.heightInMeters { let heightFormatter = LengthFormatter() heightFormatter.isForPersonHeightUse = true heightLabel.text = heightFormatter.string(fromMeters: height) } if let bodyMassIndex = userHealthProfile.bodyMassIndex { bodyMassIndexLabel.text = String(format: "%.02f", bodyMassIndex) }
遵循
updateLabels()函数中的原始模板,它扩展了
UserHealthProfile模型中的身高,体重和体重指数。 如果可用,它们将生成适当的行并将其分配给用户屏幕上的标签。
MassFormatter和
LengthFormatter完成将值转换为字符串的工作。
体重指数实际上并未存储在
UserHealthProfile模型中。 这是一个为您执行计算的计算属性。
单击
bodyMassIndex属性,您将明白我的意思:
var bodyMassIndex: Double? { guard let weightInKilograms = weightInKilograms, let heightInMeters = heightInMeters, heightInMeters > 0 else { return nil } return (weightInKilograms/(heightInMeters*heightInMeters)) }
体重指数是一个可选属性,也就是说,如果您未指定身高或体重(或者将它们设置为没有意义的数字),则它可以返回nil。 实际计算只是重量除以高度的平方。
注意: 如果您还没有将数据添加到HealthKit以便应用程序读取的话,那么您很快就会沉迷于所有这些。如果尚未这样做,则至少需要创建身高和体重样本。打开运行状况应用程序,然后转到运行状况数据选项卡。在此处,选择“身体测量”参数,然后选择“权重”,然后选择“添加数据点”以添加新的体重样本。重复增长过程。此时,Prancercise Tracker应该能够读取用户体重和身高的最新样本,然后将其显示在文本中。编译并运行应用程序。转到配置文件和BMI。然后单击“ 读取HealthKit数据”按钮。太棒了!您只需从HealthKit存储库中读取第一个样本,然后使用它们来计算BMI。保存样品
在Prancercise跟踪器已经拥有一个方便的计算身体质量指数。让我们用它来记录用户的BMI示例。打开ProfileDataStore.swift并添加以下方法: class func saveBodyMassIndexSample(bodyMassIndex: Double, date: Date) {
与其他样本类型一样,您必须首先确保HealthKit中提供了样本类型。- 在这种情况下,代码检查体重指数是否存在数量类型。如果是这样,则将其用于创建数量样本。如果不是,则该应用程序停止工作。
- count() HKUnit , , . - , , .
- HKHealthStore , . , .
快完成了总结用户界面。打开ProfileViewController.swif,找到saveBodyMassIndexToHealthKit()方法。当用户单击表中的“保存BMI”按钮时,将调用此方法。将以下代码粘贴到该方法中: guard let bodyMassIndex = userHealthProfile.bodyMassIndex else { displayAlert(for: ProfileDataError.missingBodyMassIndex) return } ProfileDataStore.saveBodyMassIndexSample(bodyMassIndex: bodyMassIndex, date: Date())
您还记得,体重指数是一个计算属性,当从HealthKit加载身高和体重样本时,该属性返回一个值。这段代码正在尝试计算此属性,如果可能,它将被传递给您刚刚编写的saveBodyMassIndexSample(bodyMassIndex:date :)方法。如果由于某种原因无法计算出体重指数,也会显示一个方便的警报。编译并运行该应用程序。转到配置文件和BMI屏幕。从HeathKit下载数据,然后单击“保存BMI”按钮。查看控制台。你看到了吗? BMI
如果是这样,恭喜!您的BMI样本现在存储在HealthKit中央存储库中。让我们看看是否可以找到他。打开“健康”应用程序,点击“健康数据”选项卡,在表格视图中单击“身体测量”,然后单击“身体质量指数”。