Skip to content

Commit ec9b24b

Browse files
Request authorization request for heart rate data
1 parent 9dd2e79 commit ec9b24b

File tree

9 files changed

+132
-41
lines changed

9 files changed

+132
-41
lines changed

Heart Control WatchKit App/Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<key>CFBundleDevelopmentRegion</key>
66
<string>en</string>
77
<key>CFBundleDisplayName</key>
8-
<string>Heart Control WatchKit App</string>
8+
<string>Heart Control</string>
99
<key>CFBundleExecutable</key>
1010
<string>$(EXECUTABLE_NAME)</string>
1111
<key>CFBundleIdentifier</key>
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//
2+
// HKAuthorizationManger.swift
3+
// Heart Control
4+
//
5+
// Created by Thomas Paul Mann on 07/08/16.
6+
// Copyright © 2016 Thomas Paul Mann. All rights reserved.
7+
//
8+
9+
import HealthKit
10+
11+
class AuthorizationManager {
12+
13+
static func requestAuthorization(completionHandler: ((success: Bool) -> Void)) {
14+
defer {
15+
completionHandler(success: false)
16+
}
17+
18+
// Create health store.
19+
let healthStore = HKHealthStore()
20+
21+
// Check if there is health data available.
22+
if (!HKHealthStore.isHealthDataAvailable()) {
23+
print("No health data is available.")
24+
return
25+
}
26+
27+
// Create quantity type for heart rate.
28+
guard let heartRateQuantityType = HKQuantityType.quantityType(forIdentifier: .heartRate) else {
29+
print("Unable to create quantity type for heart rate.")
30+
return
31+
}
32+
33+
// Request authorization to read heart rate data.
34+
healthStore.requestAuthorization(toShare: nil, read: [heartRateQuantityType]) { (success, error) -> Void in
35+
// If there is an error, do nothing.
36+
guard error == nil else {
37+
print(error)
38+
return
39+
}
40+
41+
// Delegate success.
42+
completionHandler(success: success)
43+
}
44+
}
45+
46+
}

Heart Control WatchKit Extension/HeartRateManager.swift

Lines changed: 8 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88

99
import HealthKit
1010

11-
typealias HKQueryUpdateHandler = (HKAnchoredObjectQuery, [HKSample]?, [HKDeletedObject]?, HKQueryAnchor?, NSError?) -> Void
11+
typealias HKQueryUpdateHandler = ((HKAnchoredObjectQuery, [HKSample]?, [HKDeletedObject]?, HKQueryAnchor?, Error?) -> Swift.Void)
1212

13-
protocol HeartRateDelegate: class {
13+
protocol HeartRateManagerDelegate: class {
1414

1515
func heartRate(didChangeTo newHeartRate: HeartRate)
1616

@@ -20,47 +20,32 @@ class HeartRateManager {
2020

2121
// MARK: - Properties
2222

23-
private lazy var heartRateQuantityType: HKObjectType = {
24-
return HKObjectType.quantityType(forIdentifier: .heartRate)!
25-
}()
26-
2723
private let healthStore = HKHealthStore()
2824

29-
weak var delegate: HeartRateDelegate?
25+
weak var delegate: HeartRateManagerDelegate?
3026

3127
private var activeQueries = [HKQuery]()
3228

3329
// MARK: - Initialization
3430

3531
init() {
36-
if (healthStore.authorizationStatus(for: heartRateQuantityType) != .sharingAuthorized) {
37-
requestAuthorization()
32+
// Request authorization to read heart rate data.
33+
AuthorizationManager.requestAuthorization { (success) in
34+
// TODO: Export error.
35+
print(success)
3836
}
3937
}
4038

4139
// MARK: - Public API
4240

43-
func requestAuthorization() {
44-
healthStore.requestAuthorization(toShare: nil, read: [heartRateQuantityType]) { (success, error) -> Void in
45-
if success == false {
46-
fatalError("Unable to request authorization of Health.")
47-
}
48-
}
49-
}
50-
5141
func start() {
52-
// If not authorized to read heart rate, try one more time.
53-
if (healthStore.authorizationStatus(for: heartRateQuantityType) != .sharingAuthorized) {
54-
requestAuthorization()
55-
}
56-
5742
// Configure heart rate quantity type.
5843
guard let quantityType = HKObjectType.quantityType(forIdentifier: .heartRate) else { return }
5944

6045
// Create query to receive continiuous heart rate samples.
6146
let datePredicate = HKQuery.predicateForSamples(withStart: Date(), end: nil, options: .strictStartDate)
6247
let devicePredicate = HKQuery.predicateForObjects(from: [HKDevice.local()])
63-
let queryPredicate = CompoundPredicate(andPredicateWithSubpredicates:[datePredicate, devicePredicate])
48+
let queryPredicate = NSCompoundPredicate(andPredicateWithSubpredicates:[datePredicate, devicePredicate])
6449
let updateHandler: HKQueryUpdateHandler = { [weak self] query, samples, deletedObjects, queryAnchor, error in
6550
if let quantitySamples = samples as? [HKQuantitySample] {
6651
self?.process(samples: quantitySamples)

Heart Control WatchKit Extension/InterfaceController.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,12 @@ class InterfaceController: WKInterfaceController {
1919

2020
private let workoutManager = WorkoutManager()
2121

22-
// MARK: - Initialization
22+
// MARK: - Lifecycle
2323

24-
override init() {
25-
super.init()
24+
override func willActivate() {
25+
super.willActivate()
2626

27+
// Configure workout manager.
2728
workoutManager.delegate = self
2829
}
2930

Heart Control WatchKit Extension/WorkoutManager.swift

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,12 @@ class WorkoutManager: NSObject {
3939
// MARK: - Properties
4040

4141
private let healthStore = HKHealthStore()
42+
private let heartRateManager = HeartRateManager()
4243

4344
weak var delegate: WorkoutManagerDelegate?
4445

4546
private(set) var state: WorkoutState = .stopped
4647

47-
private var heartRateManager = HeartRateManager()
4848
private var session: HKWorkoutSession?
4949

5050
// MARK: - Initialization
@@ -53,7 +53,6 @@ class WorkoutManager: NSObject {
5353
super.init()
5454

5555
// Configure heart rate manager.
56-
heartRateManager.requestAuthorization()
5756
heartRateManager.delegate = self
5857
}
5958

@@ -125,7 +124,7 @@ extension WorkoutManager: HKWorkoutSessionDelegate {
125124
}
126125
}
127126

128-
func workoutSession(_ workoutSession: HKWorkoutSession, didFailWithError error: NSError) {
127+
func workoutSession(_ workoutSession: HKWorkoutSession, didFailWithError error: Error) {
129128
fatalError(error.localizedDescription)
130129
}
131130

@@ -137,7 +136,7 @@ extension WorkoutManager: HKWorkoutSessionDelegate {
137136

138137
// MARK: - Heart Rate Delegate
139138

140-
extension WorkoutManager: HeartRateDelegate {
139+
extension WorkoutManager: HeartRateManagerDelegate {
141140

142141
func heartRate(didChangeTo newHeartRate: HeartRate) {
143142
delegate?.workoutManager(self, didChangeHeartRateTo: newHeartRate)

Heart Control.xcodeproj/project.pbxproj

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,11 @@
2020
BE25B0101D4F33B100DAF094 /* ExtensionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE25B00F1D4F33B100DAF094 /* ExtensionDelegate.swift */; };
2121
BE25B0121D4F33B100DAF094 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BE25B0111D4F33B100DAF094 /* Assets.xcassets */; };
2222
BE3FDBDC1D576B8D009165A8 /* HeartRate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE3FDBDB1D576B8D009165A8 /* HeartRate.swift */; };
23-
BE496A1B1D4F467100C82C4F /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE496A1A1D4F467100C82C4F /* HealthKit.framework */; };
24-
BE496A1F1D4F467900C82C4F /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE496A1E1D4F467900C82C4F /* HealthKit.framework */; };
2523
BE496A231D4F4D9A00C82C4F /* WorkoutManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE496A221D4F4D9A00C82C4F /* WorkoutManager.swift */; };
2624
BE5AEE171D4F7CED0027C49C /* HKUnit+BeatsPerMinute.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE5AEE161D4F7CED0027C49C /* HKUnit+BeatsPerMinute.swift */; };
25+
BE7070091D57A13D00E41B95 /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE7070081D57A13D00E41B95 /* HealthKit.framework */; };
26+
BE70700B1D57A14300E41B95 /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE70700A1D57A14300E41B95 /* HealthKit.framework */; };
27+
BE70700D1D57A29500E41B95 /* AuthorizationManger.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE70700C1D57A29500E41B95 /* AuthorizationManger.swift */; };
2728
BEB5C5BB1D4F656D00BBF490 /* HeartRateManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEB5C5BA1D4F656D00BBF490 /* HeartRateManager.swift */; };
2829
/* End PBXBuildFile section */
2930

@@ -87,12 +88,13 @@
8788
BE25B0111D4F33B100DAF094 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
8889
BE25B0131D4F33B100DAF094 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
8990
BE3FDBDB1D576B8D009165A8 /* HeartRate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeartRate.swift; sourceTree = "<group>"; };
90-
BE496A1A1D4F467100C82C4F /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS3.0.sdk/System/Library/Frameworks/HealthKit.framework; sourceTree = DEVELOPER_DIR; };
9191
BE496A1C1D4F467100C82C4F /* Heart Control WatchKit Extension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = "Heart Control WatchKit Extension.entitlements"; sourceTree = "<group>"; };
9292
BE496A1D1D4F467900C82C4F /* Heart Control.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = "Heart Control.entitlements"; sourceTree = "<group>"; };
93-
BE496A1E1D4F467900C82C4F /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = System/Library/Frameworks/HealthKit.framework; sourceTree = SDKROOT; };
9493
BE496A221D4F4D9A00C82C4F /* WorkoutManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WorkoutManager.swift; sourceTree = "<group>"; };
9594
BE5AEE161D4F7CED0027C49C /* HKUnit+BeatsPerMinute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HKUnit+BeatsPerMinute.swift"; sourceTree = "<group>"; };
95+
BE7070081D57A13D00E41B95 /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = System/Library/Frameworks/HealthKit.framework; sourceTree = SDKROOT; };
96+
BE70700A1D57A14300E41B95 /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS3.0.sdk/System/Library/Frameworks/HealthKit.framework; sourceTree = DEVELOPER_DIR; };
97+
BE70700C1D57A29500E41B95 /* AuthorizationManger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthorizationManger.swift; sourceTree = "<group>"; };
9698
BEB5C5BA1D4F656D00BBF490 /* HeartRateManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeartRateManager.swift; sourceTree = "<group>"; };
9799
/* End PBXFileReference section */
98100

@@ -101,15 +103,15 @@
101103
isa = PBXFrameworksBuildPhase;
102104
buildActionMask = 2147483647;
103105
files = (
104-
BE496A1F1D4F467900C82C4F /* HealthKit.framework in Frameworks */,
106+
BE7070091D57A13D00E41B95 /* HealthKit.framework in Frameworks */,
105107
);
106108
runOnlyForDeploymentPostprocessing = 0;
107109
};
108110
BE25B0051D4F33B100DAF094 /* Frameworks */ = {
109111
isa = PBXFrameworksBuildPhase;
110112
buildActionMask = 2147483647;
111113
files = (
112-
BE496A1B1D4F467100C82C4F /* HealthKit.framework in Frameworks */,
114+
BE70700B1D57A14300E41B95 /* HealthKit.framework in Frameworks */,
113115
);
114116
runOnlyForDeploymentPostprocessing = 0;
115117
};
@@ -166,6 +168,7 @@
166168
BE25B00F1D4F33B100DAF094 /* ExtensionDelegate.swift */,
167169
BE496A221D4F4D9A00C82C4F /* WorkoutManager.swift */,
168170
BEB5C5BA1D4F656D00BBF490 /* HeartRateManager.swift */,
171+
BE70700C1D57A29500E41B95 /* AuthorizationManger.swift */,
169172
BE3FDBDB1D576B8D009165A8 /* HeartRate.swift */,
170173
BE5AEE161D4F7CED0027C49C /* HKUnit+BeatsPerMinute.swift */,
171174
);
@@ -204,8 +207,8 @@
204207
BE496A191D4F467100C82C4F /* Frameworks */ = {
205208
isa = PBXGroup;
206209
children = (
207-
BE496A1E1D4F467900C82C4F /* HealthKit.framework */,
208-
BE496A1A1D4F467100C82C4F /* HealthKit.framework */,
210+
BE70700A1D57A14300E41B95 /* HealthKit.framework */,
211+
BE7070081D57A13D00E41B95 /* HealthKit.framework */,
209212
);
210213
name = Frameworks;
211214
sourceTree = "<group>";
@@ -302,6 +305,9 @@
302305
com.apple.HealthKit = {
303306
enabled = 1;
304307
};
308+
com.apple.HealthKit.watchos = {
309+
enabled = 1;
310+
};
305311
};
306312
};
307313
};
@@ -375,6 +381,7 @@
375381
BE5AEE171D4F7CED0027C49C /* HKUnit+BeatsPerMinute.swift in Sources */,
376382
BE25B00E1D4F33B100DAF094 /* InterfaceController.swift in Sources */,
377383
BE3FDBDC1D576B8D009165A8 /* HeartRate.swift in Sources */,
384+
BE70700D1D57A29500E41B95 /* AuthorizationManger.swift in Sources */,
378385
BEB5C5BB1D4F656D00BBF490 /* HeartRateManager.swift in Sources */,
379386
);
380387
runOnlyForDeploymentPostprocessing = 0;

Heart Control/AppDelegate.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,20 @@ import HealthKit
1212
@UIApplicationMain
1313
class AppDelegate: UIResponder, UIApplicationDelegate {
1414

15+
// MARK: - Properties
16+
17+
private let healthStore = HKHealthStore()
18+
1519
var window: UIWindow?
16-
let healthStore = HKHealthStore()
20+
21+
// MARK: - Lifecycle
1722

1823
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
1924
return true
2025
}
2126

2227
func applicationShouldRequestHealthAuthorization(_ application: UIApplication) {
23-
// Authorize access to health data.
28+
// Authorize access to health data for watch.
2429
healthStore.handleAuthorizationForExtension { success, error in
2530
print(success)
2631
}

Heart Control/Assets.xcassets/AppIcon.appiconset/Contents.json

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
{
22
"images" : [
3+
{
4+
"idiom" : "iphone",
5+
"size" : "20x20",
6+
"scale" : "2x"
7+
},
8+
{
9+
"idiom" : "iphone",
10+
"size" : "20x20",
11+
"scale" : "3x"
12+
},
313
{
414
"size" : "29x29",
515
"idiom" : "iphone",
@@ -39,12 +49,37 @@
3949
"size" : "60x60",
4050
"scale" : "3x"
4151
},
52+
{
53+
"idiom" : "ipad",
54+
"size" : "20x20",
55+
"scale" : "1x"
56+
},
57+
{
58+
"idiom" : "ipad",
59+
"size" : "20x20",
60+
"scale" : "2x"
61+
},
62+
{
63+
"idiom" : "ipad",
64+
"size" : "29x29",
65+
"scale" : "1x"
66+
},
67+
{
68+
"idiom" : "ipad",
69+
"size" : "29x29",
70+
"scale" : "2x"
71+
},
4272
{
4373
"size" : "40x40",
4474
"idiom" : "ipad",
4575
"filename" : "Icon-App-40x40@1x.png",
4676
"scale" : "1x"
4777
},
78+
{
79+
"idiom" : "ipad",
80+
"size" : "40x40",
81+
"scale" : "2x"
82+
},
4883
{
4984
"size" : "76x76",
5085
"idiom" : "ipad",
@@ -63,6 +98,11 @@
6398
"filename" : "Icon-App-83.5x83.5@2x.png",
6499
"scale" : "2x"
65100
},
101+
{
102+
"idiom" : "car",
103+
"size" : "60x60",
104+
"scale" : "2x"
105+
},
66106
{
67107
"size" : "60x60",
68108
"idiom" : "car",
@@ -83,6 +123,12 @@
83123
"role" : "notificationCenter",
84124
"subtype" : "42mm"
85125
},
126+
{
127+
"size" : "29x29",
128+
"idiom" : "watch",
129+
"role" : "companionSettings",
130+
"scale" : "2x"
131+
},
86132
{
87133
"size" : "29x29",
88134
"idiom" : "watch",

Heart Control/Info.plist

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,7 @@
3535
<array>
3636
<string>UIInterfaceOrientationPortrait</string>
3737
</array>
38+
<key>NSHealthShareUsageDescription</key>
39+
<string>Read your Heart Rate and present it on your Apple Watch.</string>
3840
</dict>
3941
</plist>

0 commit comments

Comments
 (0)