检查是否已在 Apple Watch 上授予 HealthKit 权限的最佳方法?
Best approach for checking whether HealthKit permissions have been granted on Apple Watch?
我知道即使是 Watch 应用,HealthKit 仍然需要用户通过 iPhone 授权 HealthKit 权限。事实上,Apple 自己的 Sample Code 在手表上没有任何授权码。如果您在 Watch 上打开 Speedy Sloth 而没有打开 iPhone(要求访问权限),该应用程序将不会在没有通知的情况下开始锻炼(因为它未获得授权)。
话虽如此,据我所知,我们观察开发人员必须在 Watch 上实施我们自己的授权检查,并弹出警报以提醒用户打开 iPhone 应用程序并在未授权时进行授权。
下面的代码是我对此的实现,但是用户偶尔会报告有授权问题,例如在连续开始多次锻炼时。任何人都可以看到我的逻辑中的任何缺陷吗?
import WatchKit
import Foundation
import HealthKit
class InterfaceController: WKInterfaceController {
// MARK: - Properties
private let healthStore = HKHealthStore()
private var healthKitAuthorized = false
// MARK: - Interface Controller Overrides
override func awake(withContext context: Any?) {
super.awake(withContext: context)
}
override func willActivate() {
super.willActivate()
requestAccessToHealthKit()
}
@IBAction func didTapStartButton() {
segueToWorkOutInterfaceControllerIfAuthorized()
}
private func requestAccessToHealthKit() {
let healthStore = HKHealthStore()
let healthKitTypesToWrite: Set<HKSampleType> = [
HKObjectType.workoutType(),
HKSeriesType.workoutRoute(),
HKObjectType.quantityType(forIdentifier: .activeEnergyBurned)!,
HKObjectType.quantityType(forIdentifier: .heartRate)!,
HKObjectType.quantityType(forIdentifier: .restingHeartRate)!,
HKObjectType.quantityType(forIdentifier: .bodyMass)!,
HKObjectType.quantityType(forIdentifier: .vo2Max)!,
HKObjectType.quantityType(forIdentifier: .stepCount)!,
HKObjectType.quantityType(forIdentifier: .distanceWalkingRunning)!]
let healthKitTypesToRead: Set<HKObjectType> = [
HKObjectType.workoutType(),
HKSeriesType.workoutRoute(),
HKObjectType.quantityType(forIdentifier: .activeEnergyBurned)!,
HKObjectType.quantityType(forIdentifier: .heartRate)!,
HKObjectType.quantityType(forIdentifier: .restingHeartRate)!,
HKObjectType.characteristicType(forIdentifier: .dateOfBirth)!,
HKObjectType.quantityType(forIdentifier: .bodyMass)!,
HKObjectType.quantityType(forIdentifier: .vo2Max)!,
HKObjectType.quantityType(forIdentifier: .stepCount)!,
HKObjectType.quantityType(forIdentifier: .distanceWalkingRunning)!]
healthStore.requestAuthorization(toShare: healthKitTypesToWrite, read: healthKitTypesToRead) { (success, error) in
if let unwrappedError = error {
DispatchQueue.main.async {
print("Watch Healthkit Authorization Error = \(unwrappedError.localizedDescription)")
WKInterfaceDevice.current().play(.notification)
self.showAlertWith(title: "HealthKit Permission Needed", message: "Open the App on your iPhone or go to -> Settings -> Privacy -> Health -> App and turn on permissions")
}
}
if !success {
DispatchQueue.main.async {
print("Watch Healthkit Authorization was unsuccessful no error reported")
WKInterfaceDevice.current().play(.notification)
self.showAlertWith(title: "HealthKit Permission Needed", message: "Open the App on your iPhone or go to -> Settings -> Privacy -> Health -> App and turn on permissions")
}
}
DispatchQueue.main.async {
self.healthKitAuthorized = true
print("Successful HealthKit Authorization FROM INTERFACE CONTROLLER")
}
}
}
func showAlertWith(title: String, message: String){
let action1 = WKAlertAction(title: "OK", style: .default) {
WKInterfaceController.reloadRootPageControllers(withNames: ["InterfaceController"],
contexts: nil,
orientation: .vertical,
pageIndex: 0)
}
presentAlert(withTitle: title, message: message, preferredStyle: .alert, actions: [action1])
}
func segueToWorkOutInterfaceControllerIfAuthorized() {
if healthKitAuthorized {
WKInterfaceController.reloadRootPageControllers(withNames: ["WorkoutInterfaceController"],
contexts: [contextDictionary],
orientation: .vertical,
pageIndex: 0)
} else {
self.showAlertWith(title: "HealthKit Permission Needed", message: "Open your iPhone -> Settings -> Privacy -> Health -> App and turn on permissions")
}
}
}
我决定将我的 HealthKit 授权流程放入我的应用程序的 ExtensionDelegate
而不是 InterfaceController
并且在目前的测试中它似乎 work/trigger 更可靠,而且显然更考虑到除非需要,否则我不请求授权,因此我发布的原始代码效率很高。仍然会感谢其他人对此的任何意见。
import WatchKit
import HealthKit
import WatchConnectivity
class ExtensionDelegate: NSObject, WKExtensionDelegate, WCSessionDelegate {
let healthStore = HKHealthStore()
var watchSession: WCSession?
let defaults = UserDefaults.standard
func applicationDidFinishLaunching() {
requestAccessToHealthKit()
}
private func requestAccessToHealthKit() {
let healthKitTypesToWrite: Set<HKSampleType> = [
HKObjectType.workoutType(),
HKSeriesType.workoutRoute(),
HKObjectType.quantityType(forIdentifier: .activeEnergyBurned)!,
HKObjectType.quantityType(forIdentifier: .heartRate)!,
HKObjectType.quantityType(forIdentifier: .restingHeartRate)!,
HKObjectType.quantityType(forIdentifier: .bodyMass)!,
HKObjectType.quantityType(forIdentifier: .vo2Max)!,
HKObjectType.quantityType(forIdentifier: .stepCount)!,
HKObjectType.quantityType(forIdentifier: .distanceWalkingRunning)!]
let healthKitTypesToRead: Set<HKObjectType> = [
HKObjectType.workoutType(),
HKSeriesType.workoutRoute(),
HKObjectType.quantityType(forIdentifier: .activeEnergyBurned)!,
HKObjectType.quantityType(forIdentifier: .heartRate)!,
HKObjectType.quantityType(forIdentifier: .restingHeartRate)!,
HKObjectType.characteristicType(forIdentifier: .dateOfBirth)!,
HKObjectType.quantityType(forIdentifier: .bodyMass)!,
HKObjectType.quantityType(forIdentifier: .vo2Max)!,
HKObjectType.quantityType(forIdentifier: .stepCount)!,
HKObjectType.quantityType(forIdentifier: .distanceWalkingRunning)!]
let authorizationStatus = healthStore.authorizationStatus(for: HKSampleType.workoutType())
switch authorizationStatus {
case .sharingAuthorized: print("sharing authorized")
print("sharing authorized this message is from Watch's extension delegate")
case .sharingDenied: print("sharing denied")
healthStore.requestAuthorization(toShare: healthKitTypesToWrite, read: healthKitTypesToRead) { (success, error) in
print("Successful HealthKit Authorization from Watch's extension Delegate")
}
default: print("not determined")
healthStore.requestAuthorization(toShare: healthKitTypesToWrite, read: healthKitTypesToRead) { (success, error) in
print("Successful HealthKit Authorization from Watch's extension Delegate")
}
}
}
}
然后里面InterfaceController
@IBAction func didTapStartButton() {
let authorizationStatus = healthStore.authorizationStatus(for: HKSampleType.workoutType())
switch authorizationStatus {
case .sharingAuthorized: print("sharing authorized")
segueToWorkoutInterfaceControllerWithContext()
case .sharingDenied: print("sharing denied")
self.showAlertWith(title: "HealthKit Permission Denied", message: "Please open The App on your iPhone or go to -> Settings -> Privacy -> Health -> App and turn on all permissions")
default: print("not determined")
self.showAlertWith(title: "HealthKit Permission Not Determined", message: "Please open The App on your iPhone or go to -> Settings -> Privacy -> Health -> App and turn on all permissions")
}
}
我知道即使是 Watch 应用,HealthKit 仍然需要用户通过 iPhone 授权 HealthKit 权限。事实上,Apple 自己的 Sample Code 在手表上没有任何授权码。如果您在 Watch 上打开 Speedy Sloth 而没有打开 iPhone(要求访问权限),该应用程序将不会在没有通知的情况下开始锻炼(因为它未获得授权)。
话虽如此,据我所知,我们观察开发人员必须在 Watch 上实施我们自己的授权检查,并弹出警报以提醒用户打开 iPhone 应用程序并在未授权时进行授权。
下面的代码是我对此的实现,但是用户偶尔会报告有授权问题,例如在连续开始多次锻炼时。任何人都可以看到我的逻辑中的任何缺陷吗?
import WatchKit
import Foundation
import HealthKit
class InterfaceController: WKInterfaceController {
// MARK: - Properties
private let healthStore = HKHealthStore()
private var healthKitAuthorized = false
// MARK: - Interface Controller Overrides
override func awake(withContext context: Any?) {
super.awake(withContext: context)
}
override func willActivate() {
super.willActivate()
requestAccessToHealthKit()
}
@IBAction func didTapStartButton() {
segueToWorkOutInterfaceControllerIfAuthorized()
}
private func requestAccessToHealthKit() {
let healthStore = HKHealthStore()
let healthKitTypesToWrite: Set<HKSampleType> = [
HKObjectType.workoutType(),
HKSeriesType.workoutRoute(),
HKObjectType.quantityType(forIdentifier: .activeEnergyBurned)!,
HKObjectType.quantityType(forIdentifier: .heartRate)!,
HKObjectType.quantityType(forIdentifier: .restingHeartRate)!,
HKObjectType.quantityType(forIdentifier: .bodyMass)!,
HKObjectType.quantityType(forIdentifier: .vo2Max)!,
HKObjectType.quantityType(forIdentifier: .stepCount)!,
HKObjectType.quantityType(forIdentifier: .distanceWalkingRunning)!]
let healthKitTypesToRead: Set<HKObjectType> = [
HKObjectType.workoutType(),
HKSeriesType.workoutRoute(),
HKObjectType.quantityType(forIdentifier: .activeEnergyBurned)!,
HKObjectType.quantityType(forIdentifier: .heartRate)!,
HKObjectType.quantityType(forIdentifier: .restingHeartRate)!,
HKObjectType.characteristicType(forIdentifier: .dateOfBirth)!,
HKObjectType.quantityType(forIdentifier: .bodyMass)!,
HKObjectType.quantityType(forIdentifier: .vo2Max)!,
HKObjectType.quantityType(forIdentifier: .stepCount)!,
HKObjectType.quantityType(forIdentifier: .distanceWalkingRunning)!]
healthStore.requestAuthorization(toShare: healthKitTypesToWrite, read: healthKitTypesToRead) { (success, error) in
if let unwrappedError = error {
DispatchQueue.main.async {
print("Watch Healthkit Authorization Error = \(unwrappedError.localizedDescription)")
WKInterfaceDevice.current().play(.notification)
self.showAlertWith(title: "HealthKit Permission Needed", message: "Open the App on your iPhone or go to -> Settings -> Privacy -> Health -> App and turn on permissions")
}
}
if !success {
DispatchQueue.main.async {
print("Watch Healthkit Authorization was unsuccessful no error reported")
WKInterfaceDevice.current().play(.notification)
self.showAlertWith(title: "HealthKit Permission Needed", message: "Open the App on your iPhone or go to -> Settings -> Privacy -> Health -> App and turn on permissions")
}
}
DispatchQueue.main.async {
self.healthKitAuthorized = true
print("Successful HealthKit Authorization FROM INTERFACE CONTROLLER")
}
}
}
func showAlertWith(title: String, message: String){
let action1 = WKAlertAction(title: "OK", style: .default) {
WKInterfaceController.reloadRootPageControllers(withNames: ["InterfaceController"],
contexts: nil,
orientation: .vertical,
pageIndex: 0)
}
presentAlert(withTitle: title, message: message, preferredStyle: .alert, actions: [action1])
}
func segueToWorkOutInterfaceControllerIfAuthorized() {
if healthKitAuthorized {
WKInterfaceController.reloadRootPageControllers(withNames: ["WorkoutInterfaceController"],
contexts: [contextDictionary],
orientation: .vertical,
pageIndex: 0)
} else {
self.showAlertWith(title: "HealthKit Permission Needed", message: "Open your iPhone -> Settings -> Privacy -> Health -> App and turn on permissions")
}
}
}
我决定将我的 HealthKit 授权流程放入我的应用程序的 ExtensionDelegate
而不是 InterfaceController
并且在目前的测试中它似乎 work/trigger 更可靠,而且显然更考虑到除非需要,否则我不请求授权,因此我发布的原始代码效率很高。仍然会感谢其他人对此的任何意见。
import WatchKit
import HealthKit
import WatchConnectivity
class ExtensionDelegate: NSObject, WKExtensionDelegate, WCSessionDelegate {
let healthStore = HKHealthStore()
var watchSession: WCSession?
let defaults = UserDefaults.standard
func applicationDidFinishLaunching() {
requestAccessToHealthKit()
}
private func requestAccessToHealthKit() {
let healthKitTypesToWrite: Set<HKSampleType> = [
HKObjectType.workoutType(),
HKSeriesType.workoutRoute(),
HKObjectType.quantityType(forIdentifier: .activeEnergyBurned)!,
HKObjectType.quantityType(forIdentifier: .heartRate)!,
HKObjectType.quantityType(forIdentifier: .restingHeartRate)!,
HKObjectType.quantityType(forIdentifier: .bodyMass)!,
HKObjectType.quantityType(forIdentifier: .vo2Max)!,
HKObjectType.quantityType(forIdentifier: .stepCount)!,
HKObjectType.quantityType(forIdentifier: .distanceWalkingRunning)!]
let healthKitTypesToRead: Set<HKObjectType> = [
HKObjectType.workoutType(),
HKSeriesType.workoutRoute(),
HKObjectType.quantityType(forIdentifier: .activeEnergyBurned)!,
HKObjectType.quantityType(forIdentifier: .heartRate)!,
HKObjectType.quantityType(forIdentifier: .restingHeartRate)!,
HKObjectType.characteristicType(forIdentifier: .dateOfBirth)!,
HKObjectType.quantityType(forIdentifier: .bodyMass)!,
HKObjectType.quantityType(forIdentifier: .vo2Max)!,
HKObjectType.quantityType(forIdentifier: .stepCount)!,
HKObjectType.quantityType(forIdentifier: .distanceWalkingRunning)!]
let authorizationStatus = healthStore.authorizationStatus(for: HKSampleType.workoutType())
switch authorizationStatus {
case .sharingAuthorized: print("sharing authorized")
print("sharing authorized this message is from Watch's extension delegate")
case .sharingDenied: print("sharing denied")
healthStore.requestAuthorization(toShare: healthKitTypesToWrite, read: healthKitTypesToRead) { (success, error) in
print("Successful HealthKit Authorization from Watch's extension Delegate")
}
default: print("not determined")
healthStore.requestAuthorization(toShare: healthKitTypesToWrite, read: healthKitTypesToRead) { (success, error) in
print("Successful HealthKit Authorization from Watch's extension Delegate")
}
}
}
}
然后里面InterfaceController
@IBAction func didTapStartButton() {
let authorizationStatus = healthStore.authorizationStatus(for: HKSampleType.workoutType())
switch authorizationStatus {
case .sharingAuthorized: print("sharing authorized")
segueToWorkoutInterfaceControllerWithContext()
case .sharingDenied: print("sharing denied")
self.showAlertWith(title: "HealthKit Permission Denied", message: "Please open The App on your iPhone or go to -> Settings -> Privacy -> Health -> App and turn on all permissions")
default: print("not determined")
self.showAlertWith(title: "HealthKit Permission Not Determined", message: "Please open The App on your iPhone or go to -> Settings -> Privacy -> Health -> App and turn on all permissions")
}
}