HealthKitHostApiImpl.swift
2.19 KB
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import Foundation
final class HealthKitHostApiImpl: HealthKitHostApi {
private let service: HealthKitService
init(service: HealthKitService = .shared) {
self.service = service
}
func checkHealthAppAuthorization() throws -> Bool {
service.isHealthDataAvailable && !runBlocking {
await self.service.shouldRequestAuthorization()
}
}
func getHealthServerAuthUrl() throws -> String {
// Apple Health authorization is system-managed, not URL based.
""
}
func requestHealthClientAuthorization() throws -> Bool {
let result = runAuthorizationRequest()
if result.success {
service.startBackgroundObserversIfNeeded()
Task {
await service.refreshSharedWatchValues()
}
}
if let error = result.error {
throw error
}
return result.success
}
func cancelHealthAppAuthorization() throws -> Bool {
// iOS does not let apps revoke HealthKit permission programmatically.
// Users must revoke access in Settings > Health > Data Access & Devices.
false
}
func performHealthUpload() throws -> HealthUploadResult {
let summary = runBlocking {
await self.service.performLocalSync()
}
return HealthUploadResult(
commonUploadSuccess: summary.commonCount >= 0,
sleepUploadSuccess: summary.sleepCount >= 0,
errorMessage: nil
)
}
private func runAuthorizationRequest() -> (success: Bool, error: Error?) {
var result: (Bool, Error?) = (false, nil)
let semaphore = DispatchSemaphore(value: 0)
service.requestAuthorization { success, error in
result = (success, error)
semaphore.signal()
}
waitForSemaphore(semaphore)
return result
}
}
private func runBlocking<T>(_ operation: @escaping () async -> T) -> T {
let semaphore = DispatchSemaphore(value: 0)
var result: T?
Task {
result = await operation()
semaphore.signal()
}
waitForSemaphore(semaphore)
return result!
}
private func waitForSemaphore(_ semaphore: DispatchSemaphore) {
if Thread.isMainThread {
while semaphore.wait(timeout: .now() + 0.05) == .timedOut {
RunLoop.main.run(mode: .default, before: Date(timeIntervalSinceNow: 0.05))
}
} else {
semaphore.wait()
}
}