diff --git a/PrivacyInfo.xcprivacy b/PrivacyInfo.xcprivacy new file mode 100644 index 00000000..afe4868a --- /dev/null +++ b/PrivacyInfo.xcprivacy @@ -0,0 +1,92 @@ + + + + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryFileTimestamp + NSPrivacyAccessedAPITypeReasons + + C617.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategorySystemBootTime + NSPrivacyAccessedAPITypeReasons + + 35F9.1 + + + + NSPrivacyTracking + + NSPrivacyTrackingDomains + + NSPrivacyCollectedDataTypes + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypeAudioData + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAppFunctionality + + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypePhotosorVideos + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAppFunctionality + + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypeSensitiveInfo + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAppFunctionality + + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypeHealth + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAppFunctionality + + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypePreciseLocation + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAppFunctionality + + + + + diff --git a/README.md b/README.md index a01476d1..1d5fa64d 100644 --- a/README.md +++ b/README.md @@ -260,6 +260,28 @@ types of steps supported by the *ResearchKit framework* in the first tab, and di results of the last completed task in the second tab. The third tab shows some examples from the *Charts module*. +App Store Submissions +----------------------------- + +For apps that don’t use ResearchKit’s HealthKit features: +If you are looking to submit your app with ResearchKit to the App Store, you can compile out unnecessary references to HealthKit in your app by editing the following project file: +`ResearchKit/Configuration/ResearchKit/ResearchKit-Shared.xcconfig` +And changing +`ORK_FEATURE_HEALTHKIT_AUTHORIZATION=1` +To +`ORK_FEATURE_HEALTHKIT_AUTHORIZATION=0` + +With this change, the embedded ResearchKit framework in your app will not contain any HealthKit related functionality or any references to HealthKit API that might trigger AppStore review to require HealthKit entries in your app’s Info.plist. + +Similarly, if you would like to compile out references to CoreLocation. In the same `ResearchKit-Shared.xcconfig` +And changing +`ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION=1` +To +`ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION=0` + +Also please ensure that your `info.plist` does not have any HealthKit privacy messages + +Similarly, if you would like to use the ResearchKit’s HealthKit features. Make sure to enable the HealthKit Entitlement License ======= diff --git a/ResearchKit.podspec b/ResearchKit.podspec index c1637432..e69a2b9d 100644 --- a/ResearchKit.podspec +++ b/ResearchKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'ResearchKit' - s.version = '2.2.15' + s.version = '2.2.16' s.summary = 'ResearchKit is an open source software framework that makes it easy to create apps for medical research or for other research projects.' s.homepage = 'https://www.github.com/ResearchKit/ResearchKit' s.documentation_url = 'http://researchkit.github.io/docs/' diff --git a/ResearchKit.xcodeproj/project.pbxproj b/ResearchKit.xcodeproj/project.pbxproj index 7544a765..e675ea38 100644 --- a/ResearchKit.xcodeproj/project.pbxproj +++ b/ResearchKit.xcodeproj/project.pbxproj @@ -22,7 +22,7 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ - 00B1F7852241503900D022FE /* Speech.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 00B1F7842241503900D022FE /* Speech.framework */; }; + 00B1F7852241503900D022FE /* Speech.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 00B1F7842241503900D022FE /* Speech.framework */; settings = {ATTRIBUTES = (Required, ); }; }; 00C2668A2302244300337E0B /* ORKCustomStepViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 00C266882302244300337E0B /* ORKCustomStepViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; 00C2668B2302244300337E0B /* ORKCustomStepViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 00C266892302244300337E0B /* ORKCustomStepViewController.m */; }; 00C2668E23022CD400337E0B /* ORKCustomStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 00C2668C23022CD400337E0B /* ORKCustomStep.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -41,6 +41,9 @@ 03BD9EA4253E62A0008ADBE1 /* ORKBundleAsset.m in Sources */ = {isa = PBXBuildFile; fileRef = 03BD9EA2253E62A0008ADBE1 /* ORKBundleAsset.m */; }; 03EDD58024CA6B1D006245E9 /* ORKNotificationPermissionType.h in Headers */ = {isa = PBXBuildFile; fileRef = 03EDD57E24CA6B1D006245E9 /* ORKNotificationPermissionType.h */; settings = {ATTRIBUTES = (Public, ); }; }; 03EDD58124CA6B1D006245E9 /* ORKNotificationPermissionType.m in Sources */ = {isa = PBXBuildFile; fileRef = 03EDD57F24CA6B1D006245E9 /* ORKNotificationPermissionType.m */; }; + 0B0EF9C92B8FB87600E2A97B /* ResearchKitTests-Shared.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 0B0EF9C72B8FB87600E2A97B /* ResearchKitTests-Shared.xcconfig */; }; + 0B0EF9CA2B8FB87600E2A97B /* ResearchKitTests-Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 0B0EF9C82B8FB87600E2A97B /* ResearchKitTests-Debug.xcconfig */; }; + 0B4D0F982BC87AB100DCB8B2 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 0B4D0F972BC87AB100DCB8B2 /* PrivacyInfo.xcprivacy */; }; 0B59A6BF28C1738D005035B4 /* ORKPickerTestDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B59A6BE28C1738D005035B4 /* ORKPickerTestDelegate.m */; }; 0B6CD14329119C7F004BC4A0 /* ORKNormalizedReactionTimeStimulusView.h in Headers */ = {isa = PBXBuildFile; fileRef = 0BAF9797291088EE00EF138A /* ORKNormalizedReactionTimeStimulusView.h */; }; 0B6CD14429119CBA004BC4A0 /* ORKNormalizedReactionTimeContentView.h in Headers */ = {isa = PBXBuildFile; fileRef = 0BAF9798291088EE00EF138A /* ORKNormalizedReactionTimeContentView.h */; }; @@ -199,6 +202,9 @@ 2E80C1AA1FA2AA8D00399A0C /* ORKStreamingAudioRecorder.m in Sources */ = {isa = PBXBuildFile; fileRef = 2E80C1A91FA2AA8D00399A0C /* ORKStreamingAudioRecorder.m */; }; 2EBFE11D1AE1B32D00CB8254 /* ORKUIViewAccessibilityTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2EBFE11C1AE1B32D00CB8254 /* ORKUIViewAccessibilityTests.m */; }; 2EBFE1201AE1B74100CB8254 /* ORKVoiceEngineTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2EBFE11F1AE1B74100CB8254 /* ORKVoiceEngineTests.m */; }; + 3C13A8A52BACDCD8006CB592 /* ORKTouchAbilityTouchTracker.h in Headers */ = {isa = PBXBuildFile; fileRef = 511B8AAC293A767C00049947 /* ORKTouchAbilityTouchTracker.h */; }; + 3C7AEDCC29A72F83009EED4A /* CLLocationManager+ResearchKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 3C7AEDCA29A72F83009EED4A /* CLLocationManager+ResearchKit.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 3C7AEDCD29A72F83009EED4A /* CLLocationManager+ResearchKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 3C7AEDCB29A72F83009EED4A /* CLLocationManager+ResearchKit.m */; }; 51198769245CA50D004FC2C7 /* ORKUSDZModelManagerScene.h in Headers */ = {isa = PBXBuildFile; fileRef = 51198767245CA50D004FC2C7 /* ORKUSDZModelManagerScene.h */; }; 5119876A245CA50D004FC2C7 /* ORKUSDZModelManagerScene.m in Sources */ = {isa = PBXBuildFile; fileRef = 51198768245CA50D004FC2C7 /* ORKUSDZModelManagerScene.m */; }; 5119879B245FC33C004FC2C7 /* ORK3DModelManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 51198799245FC33C004FC2C7 /* ORK3DModelManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -225,7 +231,6 @@ 511B8AA8293A764900049947 /* ORKTouchAbilityTrial.m in Sources */ = {isa = PBXBuildFile; fileRef = 511B8AA6293A764900049947 /* ORKTouchAbilityTrial.m */; }; 511B8AAA293A766300049947 /* ORKTouchAbilityTrial_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 511B8AA9293A766300049947 /* ORKTouchAbilityTrial_Internal.h */; }; 511B8AAD293A767C00049947 /* ORKTouchAbilityTouchTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = 511B8AAB293A767C00049947 /* ORKTouchAbilityTouchTracker.m */; }; - 511B8AAE293A767C00049947 /* ORKTouchAbilityTouchTracker.h in Headers */ = {isa = PBXBuildFile; fileRef = 511B8AAC293A767C00049947 /* ORKTouchAbilityTouchTracker.h */; }; 511B8AB1293A76A100049947 /* ORKTouchAbilityTapStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 511B8AAF293A76A100049947 /* ORKTouchAbilityTapStep.m */; }; 511B8AB2293A76A100049947 /* ORKTouchAbilityTapStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 511B8AB0293A76A100049947 /* ORKTouchAbilityTapStep.h */; settings = {ATTRIBUTES = (Private, ); }; }; 511B8AB5293A76B200049947 /* ORKTouchAbilityTapStepViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 511B8AB3293A76B200049947 /* ORKTouchAbilityTapStepViewController.m */; }; @@ -760,7 +765,6 @@ B1A860F61A9693C400EA57B7 /* consent_06@3x.m4v in Resources */ = {isa = PBXBuildFile; fileRef = B1A860E81A9693C400EA57B7 /* consent_06@3x.m4v */; }; B1A860F71A9693C400EA57B7 /* consent_07@3x.m4v in Resources */ = {isa = PBXBuildFile; fileRef = B1A860E91A9693C400EA57B7 /* consent_07@3x.m4v */; }; B1C0F4E41A9BA65F0022C153 /* ResearchKit.strings in Resources */ = {isa = PBXBuildFile; fileRef = B1C0F4E11A9BA65F0022C153 /* ResearchKit.strings */; }; - B1C7955E1A9FBF04007279BA /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B1C7955D1A9FBF04007279BA /* HealthKit.framework */; settings = {ATTRIBUTES = (Required, ); }; }; B8760F2B1AFBEFB0007FA16F /* ORKScaleRangeDescriptionLabel.h in Headers */ = {isa = PBXBuildFile; fileRef = B8760F291AFBEFB0007FA16F /* ORKScaleRangeDescriptionLabel.h */; }; B8760F2C1AFBEFB0007FA16F /* ORKScaleRangeDescriptionLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = B8760F2A1AFBEFB0007FA16F /* ORKScaleRangeDescriptionLabel.m */; }; BA0AA6941EAEC0B600671ACE /* ORKStroopContentView.h in Headers */ = {isa = PBXBuildFile; fileRef = BA0AA68E1EAEC0B600671ACE /* ORKStroopContentView.h */; }; @@ -1046,6 +1050,9 @@ 03EDD57E24CA6B1D006245E9 /* ORKNotificationPermissionType.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ORKNotificationPermissionType.h; sourceTree = ""; }; 03EDD57F24CA6B1D006245E9 /* ORKNotificationPermissionType.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ORKNotificationPermissionType.m; sourceTree = ""; }; 05F3765923C797930068E166 /* ResearchKit.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = ResearchKit.xctestplan; sourceTree = ""; }; + 0B0EF9C72B8FB87600E2A97B /* ResearchKitTests-Shared.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "ResearchKitTests-Shared.xcconfig"; sourceTree = ""; }; + 0B0EF9C82B8FB87600E2A97B /* ResearchKitTests-Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "ResearchKitTests-Debug.xcconfig"; sourceTree = ""; }; + 0B4D0F972BC87AB100DCB8B2 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 0B59A6BD28C1738D005035B4 /* ORKPickerTestDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ORKPickerTestDelegate.h; sourceTree = ""; }; 0B59A6BE28C1738D005035B4 /* ORKPickerTestDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ORKPickerTestDelegate.m; sourceTree = ""; }; 0B7D32E92AE6F8040071C576 /* ORKQuestionStepViewController+TestingSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ORKQuestionStepViewController+TestingSupport.h"; sourceTree = ""; }; @@ -1208,6 +1215,8 @@ 2EBFE11C1AE1B32D00CB8254 /* ORKUIViewAccessibilityTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ORKUIViewAccessibilityTests.m; sourceTree = ""; }; 2EBFE11E1AE1B68800CB8254 /* ORKVoiceEngine_Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ORKVoiceEngine_Internal.h; sourceTree = ""; }; 2EBFE11F1AE1B74100CB8254 /* ORKVoiceEngineTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ORKVoiceEngineTests.m; sourceTree = ""; }; + 3C7AEDCA29A72F83009EED4A /* CLLocationManager+ResearchKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CLLocationManager+ResearchKit.h"; sourceTree = ""; }; + 3C7AEDCB29A72F83009EED4A /* CLLocationManager+ResearchKit.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "CLLocationManager+ResearchKit.m"; sourceTree = ""; }; 51198767245CA50D004FC2C7 /* ORKUSDZModelManagerScene.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ORKUSDZModelManagerScene.h; sourceTree = ""; }; 51198768245CA50D004FC2C7 /* ORKUSDZModelManagerScene.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ORKUSDZModelManagerScene.m; sourceTree = ""; }; 51198799245FC33C004FC2C7 /* ORK3DModelManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ORK3DModelManager.h; sourceTree = ""; }; @@ -1819,7 +1828,6 @@ B1B34A061AA10EE4005FAD66 /* ar */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = ar; path = ar.lproj/ResearchKit.strings; sourceTree = ""; }; B1C0F4E21A9BA65F0022C153 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/ResearchKit.strings; sourceTree = ""; }; B1C1DE4F196F541F00F75544 /* ResearchKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResearchKit.h; sourceTree = ""; }; - B1C7955D1A9FBF04007279BA /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = System/Library/Frameworks/HealthKit.framework; sourceTree = SDKROOT; }; B8760F291AFBEFB0007FA16F /* ORKScaleRangeDescriptionLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKScaleRangeDescriptionLabel.h; sourceTree = ""; }; B8760F2A1AFBEFB0007FA16F /* ORKScaleRangeDescriptionLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ORKScaleRangeDescriptionLabel.m; sourceTree = ""; }; BA0AA68E1EAEC0B600671ACE /* ORKStroopContentView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKStroopContentView.h; sourceTree = ""; }; @@ -2079,7 +2087,6 @@ buildActionMask = 2147483647; files = ( 00B1F7852241503900D022FE /* Speech.framework in Frameworks */, - B1C7955E1A9FBF04007279BA /* HealthKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2097,6 +2104,16 @@ name = "Custom Step"; sourceTree = ""; }; + 0B0EF9C62B8FB87600E2A97B /* ResearchKitTests */ = { + isa = PBXGroup; + children = ( + 0B0EF9C72B8FB87600E2A97B /* ResearchKitTests-Shared.xcconfig */, + 0B0EF9C82B8FB87600E2A97B /* ResearchKitTests-Debug.xcconfig */, + ); + name = ResearchKitTests; + path = ResearchKit/Configuration/ResearchKitTests; + sourceTree = SOURCE_ROOT; + }; 0BAF947D2910887D00EF138A /* Shake */ = { isa = PBXGroup; children = ( @@ -2375,6 +2392,8 @@ 5D04885525F19A7A0006C68B /* ORKDevice.h */, 5D04885B25F1B3AB0006C68B /* ORKDevice_Private.h */, 5D04885625F19A7A0006C68B /* ORKDevice.m */, + 3C7AEDCA29A72F83009EED4A /* CLLocationManager+ResearchKit.h */, + 3C7AEDCB29A72F83009EED4A /* CLLocationManager+ResearchKit.m */, ); name = Utilities; sourceTree = ""; @@ -2493,6 +2512,7 @@ 3FFF18341829DB1D00167070 = { isa = PBXGroup; children = ( + 0B4D0F972BC87AB100DCB8B2 /* PrivacyInfo.xcprivacy */, 05F3765923C797930068E166 /* ResearchKit.xctestplan */, 168EEAAE230B6F9E003FD2FA /* scripts */, B11DF3B21AA109C8009E76D2 /* docs */, @@ -2516,7 +2536,6 @@ isa = PBXGroup; children = ( 00B1F7842241503900D022FE /* Speech.framework */, - B1C7955D1A9FBF04007279BA /* HealthKit.framework */, ); name = Frameworks; sourceTree = ""; @@ -2872,21 +2891,6 @@ name = "HealthKit Permission Type"; sourceTree = ""; }; - 51EAFC10289B116300CFA3EB /* Configuration */ = { - isa = PBXGroup; - children = ( - 51EAFC11289B117500CFA3EB /* ResearchKit */, - ); - path = Configuration; - sourceTree = ""; - }; - 51EAFC11289B117500CFA3EB /* ResearchKit */ = { - isa = PBXGroup; - children = ( - ); - path = ResearchKit; - sourceTree = ""; - }; 5D000EC02620F27100E5442A /* Configuration */ = { isa = PBXGroup; children = ( @@ -2898,6 +2902,7 @@ 5D000EC12620F27100E5442A /* ResearchKit */ = { isa = PBXGroup; children = ( + 0B0EF9C62B8FB87600E2A97B /* ResearchKitTests */, 5D000EC22620F27100E5442A /* ResearchKit-Debug.xcconfig */, 5D000EC42620F27100E5442A /* ResearchKit-Shared.xcconfig */, 511A78992950D7030011D85D /* ResearchKit-Release.xcconfig */, @@ -3042,7 +3047,6 @@ B1C1DE4F196F541F00F75544 /* ResearchKit.h */, BC94EF2F1E962F7400143081 /* ORKDeprecated.h */, BC94EF301E962F7400143081 /* ORKDeprecated.m */, - 51EAFC10289B116300CFA3EB /* Configuration */, 86C40B511A8D7C5B00081FAC /* Common */, 86C40AF91A8D7C5B00081FAC /* Active Tasks */, 259E76FB1AFFAEAC0070F786 /* Charts */, @@ -4312,6 +4316,7 @@ BCB6E65B1B7D534C000D5B34 /* ORKDiscreteGraphChartView.h in Headers */, 86C40D8E1A8D7C5C00081FAC /* ORKStep.h in Headers */, 511987C72463316E004FC2C7 /* ORKRequestPermissionsStepViewController.h in Headers */, + 3C7AEDCC29A72F83009EED4A /* CLLocationManager+ResearchKit.h in Headers */, 618DA04E1A93D0D600E63AA8 /* ORKAccessibility.h in Headers */, BA6E1EB6209E9A3F00DA9D55 /* ORKPDFViewerStepView_Internal.h in Headers */, BAC6F1A922AF535100E31C22 /* ORKTaskReviewViewController.h in Headers */, @@ -4603,6 +4608,7 @@ 511B8AB6293A76B200049947 /* ORKTouchAbilityTapStepViewController.h in Headers */, D458520A1AF6CCFA00A2DE13 /* ORKImageCaptureCameraPreviewView.h in Headers */, D44239791AF17F5100559D96 /* ORKImageCaptureStep.h in Headers */, + 3C13A8A52BACDCD8006CB592 /* ORKTouchAbilityTouchTracker.h in Headers */, 511B8AF9293A78BB00049947 /* ORKTouchAbilityScrollStep.h in Headers */, 86C40CB21A8D7C5C00081FAC /* ORKRecorder_Private.h in Headers */, BCD192E71B81243900FCC08A /* ORKPieChartLegendView.h in Headers */, @@ -4895,6 +4901,8 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 0B0EF9CA2B8FB87600E2A97B /* ResearchKitTests-Debug.xcconfig in Resources */, + 0B0EF9C92B8FB87600E2A97B /* ResearchKitTests-Shared.xcconfig in Resources */, 22ED1848285290250052406B /* ORKAudiometryTestData.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -4921,6 +4929,7 @@ 2246E5152749350300261D5A /* frequency_dBSPL_AIRPODSV3.plist in Resources */, B1A860F31A9693C400EA57B7 /* consent_03@3x.m4v in Resources */, B1A860F11A9693C400EA57B7 /* consent_01@3x.m4v in Resources */, + 0B4D0F982BC87AB100DCB8B2 /* PrivacyInfo.xcprivacy in Resources */, E26E32D722E75FBF004F42EC /* Window.wav in Resources */, 62DF9F4728CA87DE00D13018 /* frequency_dBSPL_AIRPODSPROV2.plist in Resources */, B1A860F71A9693C400EA57B7 /* consent_07@3x.m4v in Resources */, @@ -5026,7 +5035,6 @@ 714151D0225C4A23002CA33B /* ORKPasscodeViewControllerTests.swift in Sources */, 0324C1D825439E1800BBE77B /* ORKVideoInstructionStepViewControllerTests.swift in Sources */, 14E79040226A5F72009D8083 /* ORKStepViewControllerHelpers.swift in Sources */, - 0BC803602B1578D500618E4F /* ORKTextChoiceAnswerFormat+FormStepViewControllerAdditions.m in Sources */, 86D348021AC161B0006DB02B /* ORKRecorderTests.m in Sources */, 1490DCFC224D4867003FEEDA /* ORKEnvironmentSPLMeterResultTests.swift in Sources */, 7141EA2222EFBC0C00650145 /* ORKLoggingTests.m in Sources */, @@ -5203,6 +5211,7 @@ BA8C067922EEB84B00ACDE6B /* ORKPlaybackButton.m in Sources */, 86C40C401A8D7C5C00081FAC /* ORKSpatialSpanMemoryContentView.m in Sources */, 511B8B12293A796600049947 /* ORKTouchAbilityPinchContentView.m in Sources */, + 3C7AEDCD29A72F83009EED4A /* CLLocationManager+ResearchKit.m in Sources */, BAC6F1AA22AF535100E31C22 /* ORKTaskReviewViewController.m in Sources */, BCB6E6671B7D535F000D5B34 /* ORKXAxisView.m in Sources */, 00C2668B2302244300337E0B /* ORKCustomStepViewController.m in Sources */, @@ -5561,6 +5570,7 @@ }; 86CC8EA41AC09332001CCD89 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 0B0EF9C82B8FB87600E2A97B /* ResearchKitTests-Debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ADDRESS_SANITIZER_CONTAINER_OVERFLOW = YES; @@ -5569,6 +5579,10 @@ COPY_PHASE_STRIP = NO; ENABLE_TESTABILITY = YES; GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "$(ORK_GCC_PREPROCESSOR_DEFINITIONS)", + ); INFOPLIST_FILE = ResearchKitTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/ResearchKit/ActiveTasks/HKSample+ORKJSONDictionary.h b/ResearchKit/ActiveTasks/HKSample+ORKJSONDictionary.h index 3cb4f062..e579384a 100644 --- a/ResearchKit/ActiveTasks/HKSample+ORKJSONDictionary.h +++ b/ResearchKit/ActiveTasks/HKSample+ORKJSONDictionary.h @@ -28,8 +28,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#import "ORKDefines.h" -@import HealthKit; +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION +#import NS_ASSUME_NONNULL_BEGIN @@ -60,3 +62,5 @@ typedef NS_OPTIONS(NSInteger, ORKSampleJSONOptions) { @end NS_ASSUME_NONNULL_END + +#endif diff --git a/ResearchKit/ActiveTasks/HKSample+ORKJSONDictionary.m b/ResearchKit/ActiveTasks/HKSample+ORKJSONDictionary.m index 0edddbac..2a20e511 100644 --- a/ResearchKit/ActiveTasks/HKSample+ORKJSONDictionary.m +++ b/ResearchKit/ActiveTasks/HKSample+ORKJSONDictionary.m @@ -45,7 +45,7 @@ static NSString *const HKUnitKey = @"unit"; static NSString *const HKCorrelatedObjectsKey = @"objects"; // static NSString *const HKSourceIdentifierKey = @"sourceBundleIdentifier"; - +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION @implementation HKSample (ORKJSONDictionary) - (NSMutableDictionary *)ork_JSONMutableDictionaryWithOptions:(ORKSampleJSONOptions)options unit:(HKUnit *)unit { @@ -167,3 +167,4 @@ static NSString *const HKCorrelatedObjectsKey = @"objects"; } @end +#endif // ORK_FEATURE_HEALTHKIT_AUTHORIZATION diff --git a/ResearchKit/ActiveTasks/ORKActiveStep.h b/ResearchKit/ActiveTasks/ORKActiveStep.h index 8ea323b7..26b9d21b 100644 --- a/ResearchKit/ActiveTasks/ORKActiveStep.h +++ b/ResearchKit/ActiveTasks/ORKActiveStep.h @@ -30,7 +30,6 @@ @import UIKit; -@import HealthKit; #import diff --git a/ResearchKit/ActiveTasks/ORKActiveStep.m b/ResearchKit/ActiveTasks/ORKActiveStep.m index 485c9b53..c4782e82 100644 --- a/ResearchKit/ActiveTasks/ORKActiveStep.m +++ b/ResearchKit/ActiveTasks/ORKActiveStep.m @@ -165,6 +165,7 @@ (self.shouldUseNextAsSkipButton == castObject.shouldUseNextAsSkipButton)); } +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION - (NSSet *)requestedHealthKitTypesForReading { NSMutableSet *set = [NSMutableSet set]; for (ORKRecorderConfiguration *config in self.recorderConfigurations) { @@ -175,6 +176,7 @@ } return set; } +#endif - (ORKPermissionMask)requestedPermissions { ORKPermissionMask mask = [super requestedPermissions]; diff --git a/ResearchKit/ActiveTasks/ORKHealthClinicalTypeRecorder.h b/ResearchKit/ActiveTasks/ORKHealthClinicalTypeRecorder.h index d75b8c72..12844283 100644 --- a/ResearchKit/ActiveTasks/ORKHealthClinicalTypeRecorder.h +++ b/ResearchKit/ActiveTasks/ORKHealthClinicalTypeRecorder.h @@ -33,7 +33,7 @@ #import #import - +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION NS_ASSUME_NONNULL_BEGIN /** @@ -68,5 +68,5 @@ API_AVAILABLE(ios(12.0)) @end #endif - NS_ASSUME_NONNULL_END +#endif diff --git a/ResearchKit/ActiveTasks/ORKHealthClinicalTypeRecorder.m b/ResearchKit/ActiveTasks/ORKHealthClinicalTypeRecorder.m index 58056227..469f3593 100644 --- a/ResearchKit/ActiveTasks/ORKHealthClinicalTypeRecorder.m +++ b/ResearchKit/ActiveTasks/ORKHealthClinicalTypeRecorder.m @@ -39,6 +39,7 @@ #import "ORKRecorder_Internal.h" #import "HKSample+ORKJSONDictionary.h" +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION @interface ORKHealthClinicalTypeRecorder () { ORKDataLogger *_logger; @@ -234,3 +235,4 @@ @end #endif +#endif diff --git a/ResearchKit/ActiveTasks/ORKHealthQuantityTypeRecorder.h b/ResearchKit/ActiveTasks/ORKHealthQuantityTypeRecorder.h index 8f4b76f2..79cba022 100644 --- a/ResearchKit/ActiveTasks/ORKHealthQuantityTypeRecorder.h +++ b/ResearchKit/ActiveTasks/ORKHealthQuantityTypeRecorder.h @@ -49,6 +49,7 @@ NS_ASSUME_NONNULL_BEGIN The `ORKHealthQuantityTypeRecorder` class represents a recorder for collecting real time sample data from HealthKit, such as heart rate, during an active task. */ +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION ORK_CLASS_AVAILABLE @interface ORKHealthQuantityTypeRecorder : ORKRecorder @@ -76,5 +77,5 @@ ORK_CLASS_AVAILABLE outputDirectory:(nullable NSURL *)outputDirectory NS_DESIGNATED_INITIALIZER; @end - +#endif NS_ASSUME_NONNULL_END diff --git a/ResearchKit/ActiveTasks/ORKHealthQuantityTypeRecorder.m b/ResearchKit/ActiveTasks/ORKHealthQuantityTypeRecorder.m index d005048a..86d8b1ff 100644 --- a/ResearchKit/ActiveTasks/ORKHealthQuantityTypeRecorder.m +++ b/ResearchKit/ActiveTasks/ORKHealthQuantityTypeRecorder.m @@ -36,7 +36,7 @@ #import "ORKRecorder_Internal.h" #import "HKSample+ORKJSONDictionary.h" - +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION @interface ORKHealthQuantityTypeRecorder () { ORKDataLogger *_logger; BOOL _isRecording; @@ -368,3 +368,4 @@ static const NSInteger _HealthAnchoredQueryLimit = 100; } @end +#endif diff --git a/ResearchKit/ActiveTasks/ORKLocationRecorder.m b/ResearchKit/ActiveTasks/ORKLocationRecorder.m index b5f6b71e..b480a10a 100644 --- a/ResearchKit/ActiveTasks/ORKLocationRecorder.m +++ b/ResearchKit/ActiveTasks/ORKLocationRecorder.m @@ -37,6 +37,8 @@ #import "CLLocation+ORKJSONDictionary.h" +#import + #import @@ -93,25 +95,25 @@ } self.locationManager = [self createLocationManager]; - - CLAuthorizationStatus status = kCLAuthorizationStatusNotDetermined; - - if (@available(iOS 14.0, *)) { - status = self.locationManager.authorizationStatus; - } else { - status = [CLLocationManager authorizationStatus]; - } - - if (status == kCLAuthorizationStatusRestricted || status == kCLAuthorizationStatusNotDetermined) { - [self.locationManager requestWhenInUseAuthorization]; + self.locationManager.delegate = self; + + BOOL locationManagerAuthRequestsAllowed = YES; + if ([CLLocationManager authorizationStatus] <= kCLAuthorizationStatusDenied) { + locationManagerAuthRequestsAllowed = [self.locationManager ork_requestWhenInUseAuthorization]; } self.uptime = [NSProcessInfo processInfo].systemUptime; - [self.locationManager startUpdatingLocation]; + [self.locationManager ork_startUpdatingLocation]; + + if (locationManagerAuthRequestsAllowed == NO) { + // If we weren't able to perform auth requests, then ResearchKit was compiled with auth requests disabled + // We won't be getting any callbacks about location changes, so might as well stop recording + [self stop]; + } } - (void)doStopRecording { - [self.locationManager stopUpdatingLocation]; + [self.locationManager ork_stopUpdatingLocation]; self.locationManager.delegate = nil; self.locationManager = nil; } diff --git a/ResearchKit/ActiveTasks/ORKNormalizedReactionTimeContentView.h b/ResearchKit/ActiveTasks/ORKNormalizedReactionTimeContentView.h index a5dbd1a0..f7b2cee1 100644 --- a/ResearchKit/ActiveTasks/ORKNormalizedReactionTimeContentView.h +++ b/ResearchKit/ActiveTasks/ORKNormalizedReactionTimeContentView.h @@ -31,9 +31,9 @@ @import UIKit; -#import "ORKCustomStepView_Internal.h" -#import "ORKNormalizedReactionTimeStimulusView.h" -#import "ORKRoundTappingButton.h" +#import +#import +#import NS_ASSUME_NONNULL_BEGIN diff --git a/ResearchKit/ActiveTasks/ORKNormalizedReactionTimeStimulusView.h b/ResearchKit/ActiveTasks/ORKNormalizedReactionTimeStimulusView.h index a738a500..f926b778 100644 --- a/ResearchKit/ActiveTasks/ORKNormalizedReactionTimeStimulusView.h +++ b/ResearchKit/ActiveTasks/ORKNormalizedReactionTimeStimulusView.h @@ -31,7 +31,7 @@ @import UIKit; -#import "ORKCustomStepView_Internal.h" +#import NS_ASSUME_NONNULL_BEGIN diff --git a/ResearchKit/ActiveTasks/ORKNormalizedReactionTimeViewController.h b/ResearchKit/ActiveTasks/ORKNormalizedReactionTimeViewController.h index a1090ad5..4a7e0fba 100644 --- a/ResearchKit/ActiveTasks/ORKNormalizedReactionTimeViewController.h +++ b/ResearchKit/ActiveTasks/ORKNormalizedReactionTimeViewController.h @@ -31,8 +31,8 @@ @import UIKit; -#import "ORKDefines.h" -#import "ORKActiveStepViewController.h" +#import +#import NS_ASSUME_NONNULL_BEGIN diff --git a/ResearchKit/ActiveTasks/ORKRecorder.h b/ResearchKit/ActiveTasks/ORKRecorder.h index 74b6ec0f..00c4c490 100644 --- a/ResearchKit/ActiveTasks/ORKRecorder.h +++ b/ResearchKit/ActiveTasks/ORKRecorder.h @@ -31,10 +31,13 @@ @import UIKit; -@import HealthKit; #import #import +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION +#import +#endif + NS_ASSUME_NONNULL_BEGIN @@ -106,7 +109,9 @@ ORK_CLASS_AVAILABLE If your recorder requires or would benefit from read access to HealthKit at runtime during the task, return the appropriate set of `HKSampleType` objects. */ +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION - (nullable NSSet *)requestedHealthKitTypesForReading; +#endif @end @@ -355,6 +360,8 @@ ORK_CLASS_AVAILABLE of an `ORKActiveStep` object, include that step in a task, and present it with a task view controller. */ + +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION ORK_CLASS_AVAILABLE @interface ORKHealthQuantityTypeRecorderConfiguration : ORKRecorderConfiguration @@ -433,7 +440,7 @@ API_AVAILABLE(ios(12.0)) @end #endif - +#endif /** The `ORKStreamingAudioRecorderConfiguration` class represents a configuration that records streaming audio data during an active step. diff --git a/ResearchKit/ActiveTasks/ORKRecorder.m b/ResearchKit/ActiveTasks/ORKRecorder.m index bfc0d2a7..def1d0ed 100644 --- a/ResearchKit/ActiveTasks/ORKRecorder.m +++ b/ResearchKit/ActiveTasks/ORKRecorder.m @@ -89,9 +89,12 @@ return nil; } +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION - (NSSet *)requestedHealthKitTypesForReading { return nil; } +#endif + - (ORKPermissionMask)requestedPermissionMask { return ORKPermissionNone; } diff --git a/ResearchKit/Common/CLLocationManager+ResearchKit.h b/ResearchKit/Common/CLLocationManager+ResearchKit.h new file mode 100644 index 00000000..e0c2ef90 --- /dev/null +++ b/ResearchKit/Common/CLLocationManager+ResearchKit.h @@ -0,0 +1,54 @@ +/* + Copyright (c) 2023, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface CLLocationManager (ResearchKit) + +/** + These categories on CLLocationManager provide ResearchKit code with a common way of requesting authorization that can be disabled by + an Xcode build setting. Callers don't have to add compile-time conditional #if blocks. Instead callers should interpret return value of YES to mean + the authorization request was made, and NO to mean the ResearchKit binary was built with CLLocationManager authorization request calls + compiled out. + + The impetus for this approach was to prevent apps using ResearchKit, but not ResearchKit's CoreLocation-powered features, from needlessly + defining an NSLocationWhenInUseUsageDescription Info.plist entry to silence build errors. + */ +- (BOOL)ork_requestWhenInUseAuthorization; +- (BOOL)ork_requestAlwaysAuthorization; + +- (void)ork_startUpdatingLocation; +- (void)ork_stopUpdatingLocation; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ResearchKit/Common/CLLocationManager+ResearchKit.m b/ResearchKit/Common/CLLocationManager+ResearchKit.m new file mode 100644 index 00000000..36d1554e --- /dev/null +++ b/ResearchKit/Common/CLLocationManager+ResearchKit.m @@ -0,0 +1,71 @@ +/* + Copyright (c) 2023, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "CLLocationManager+ResearchKit.h" + +#import "ORKDefines.h" + +@implementation CLLocationManager (ResearchKit) + +- (BOOL)ork_requestWhenInUseAuthorization { +#if ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION + [self requestWhenInUseAuthorization]; + return YES; +#else + return NO; +#endif +} + +- (BOOL)ork_requestAlwaysAuthorization { +#if ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION + [self requestAlwaysAuthorization]; + return YES; +#else + return NO; +#endif +} + +- (void)ork_startUpdatingLocation { +#if ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION + [self startUpdatingLocation]; +#else + // noop +#endif +} + +- (void)ork_stopUpdatingLocation { +#if ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION + [self stopUpdatingLocation]; +#else + // noop +#endif +} + +@end diff --git a/ResearchKit/Common/ORKAnswerFormat.m b/ResearchKit/Common/ORKAnswerFormat.m index 0dcbca89..6a598e8e 100644 --- a/ResearchKit/Common/ORKAnswerFormat.m +++ b/ResearchKit/Common/ORKAnswerFormat.m @@ -43,7 +43,10 @@ #import "ORKHealthAnswerFormat.h" #endif -@import HealthKit; +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION +#import +#endif + @import MapKit; @import Contacts; @@ -93,6 +96,7 @@ static NSNumberFormatterStyle ORKNumberFormattingStyleConvert(ORKNumberFormattin NSMutableDictionary *_unitsTable; } +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION @synthesize healthStore=_healthStore; + (instancetype)sourceWithHealthStore:(HKHealthStore *)healthStore { @@ -201,8 +205,10 @@ static NSNumberFormatterStyle ORKNumberFormattingStyleConvert(ORKNumberFormattin [healthStore executeQuery:sampleQuery]; }); } +#endif // ORK_FEATURE_HEALTHKIT_AUTHORIZATION - (void)fetchDefaultValueForAnswerFormat:(ORKAnswerFormat *)answerFormat handler:(void(^)(id defaultValue, NSError *error))handler { +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION HKObjectType *objectType = [answerFormat healthKitObjectType]; BOOL handled = NO; if (objectType) { @@ -223,8 +229,13 @@ static NSNumberFormatterStyle ORKNumberFormattingStyleConvert(ORKNumberFormattin if (!handled) { handler(nil, nil); } +#else + handler(nil, nil); +#endif } +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION + - (HKUnit *)defaultHealthKitUnitForAnswerFormat:(ORKAnswerFormat *)answerFormat { __block HKUnit *unit = [answerFormat healthKitUnit]; HKObjectType *objectType = [answerFormat healthKitObjectType]; @@ -267,9 +278,11 @@ static NSNumberFormatterStyle ORKNumberFormattingStyleConvert(ORKNumberFormattin } } +#endif // ORK_FEATURE_HEALTHKIT_AUTHORIZATION + @end -#endif +#endif // TARGET_OS_IOS #pragma mark - ORKAnswerFormat @@ -552,6 +565,7 @@ static NSNumberFormatterStyle ORKNumberFormattingStyleConvert(ORKNumberFormattin return NO; } +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION - (HKObjectType *)healthKitObjectType { return nil; } @@ -571,6 +585,7 @@ static NSNumberFormatterStyle ORKNumberFormattingStyleConvert(ORKNumberFormattin - (void)setHealthKitUserUnit:(HKUnit *)unit { } +#endif - (ORKQuestionType)questionType { return ORKQuestionTypeNone; diff --git a/ResearchKit/Common/ORKAnswerFormat_Internal.h b/ResearchKit/Common/ORKAnswerFormat_Internal.h index f167001f..7ef2c723 100644 --- a/ResearchKit/Common/ORKAnswerFormat_Internal.h +++ b/ResearchKit/Common/ORKAnswerFormat_Internal.h @@ -29,18 +29,28 @@ */ -@import HealthKit; -#import "ORKAnswerFormat_Private.h" +#import + +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION +#import +#endif + +#import #import "ORKChoiceAnswerFormatHelper.h" +@class ORKChoiceAnswerFormatHelper; + NS_ASSUME_NONNULL_BEGIN BOOL ORKIsAnswerEmpty(_Nullable id answer); #if TARGET_OS_IOS +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION NSString *ORKHKBiologicalSexString(HKBiologicalSex biologicalSex); NSString *ORKHKBloodTypeString(HKBloodType bloodType); -#endif +#endif // ORK_FEATURE_HEALTHKIT_AUTHORIZATION +#endif // TARGET_OS_IOS + NSString *ORKQuestionTypeString(ORKQuestionType questionType); // Need to mark these as designated initializers to avoid warnings once we designate the others. @@ -80,6 +90,7 @@ ORK_DESIGNATE_CODING_AND_SERIALIZATION_INITIALIZERS(ORKTextChoice) #if TARGET_OS_IOS - (BOOL)isHealthKitAnswerFormat; +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION - (nullable HKObjectType *)healthKitObjectType; - (nullable HKObjectType *)healthKitObjectTypeForAuthorization; @@ -87,6 +98,7 @@ ORK_DESIGNATE_CODING_AND_SERIALIZATION_INITIALIZERS(ORKTextChoice) @property (nonatomic, strong, nullable) HKUnit *healthKitUserUnit; #endif +#endif - (nullable NSString *)localizedInvalidValueStringWithAnswerString:(nullable NSString *)text; @@ -248,15 +260,17 @@ NSArray *ORKAllowableValueClasses(void); @interface ORKAnswerDefaultSource : NSObject +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION + (instancetype)sourceWithHealthStore:(HKHealthStore *)healthStore; - (instancetype)initWithHealthStore:(HKHealthStore *)healthStore NS_DESIGNATED_INITIALIZER; +- (nullable HKUnit *)defaultHealthKitUnitForAnswerFormat:(ORKAnswerFormat *)answerFormat; +- (void)updateHealthKitUnitForAnswerFormat:(ORKAnswerFormat *)answerFormat force:(BOOL)force; @property (nonatomic, strong, readonly, nullable) HKHealthStore *healthStore; +#endif - (void)fetchDefaultValueForAnswerFormat:(nullable ORKAnswerFormat *)answerFormat handler:(void(^)(id defaultValue, NSError *error))handler; -- (nullable HKUnit *)defaultHealthKitUnitForAnswerFormat:(ORKAnswerFormat *)answerFormat; -- (void)updateHealthKitUnitForAnswerFormat:(ORKAnswerFormat *)answerFormat force:(BOOL)force; @end diff --git a/ResearchKit/Common/ORKAnswerTextField.h b/ResearchKit/Common/ORKAnswerTextField.h index 2fc932f8..0092f7c0 100644 --- a/ResearchKit/Common/ORKAnswerTextField.h +++ b/ResearchKit/Common/ORKAnswerTextField.h @@ -30,10 +30,10 @@ @import UIKit; + #import "ORKDefaultFont.h" #import "ORKDefines.h" - NS_ASSUME_NONNULL_BEGIN /** diff --git a/ResearchKit/Common/ORKCollector.h b/ResearchKit/Common/ORKCollector.h index 71f05933..59e2ce1b 100644 --- a/ResearchKit/Common/ORKCollector.h +++ b/ResearchKit/Common/ORKCollector.h @@ -30,9 +30,12 @@ #import -#import #import +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION +#import +#endif + NS_ASSUME_NONNULL_BEGIN @@ -85,6 +88,8 @@ ORK_CLASS_AVAILABLE It cannot be initiated directly. Use `addHealthCollectorWithSampleType:`to add one to a `ORKDataCollectionManager`. */ + +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION ORK_CLASS_AVAILABLE @interface ORKHealthCollector : ORKCollector @@ -146,7 +151,7 @@ ORK_CLASS_AVAILABLE @property (copy, readonly) HKQueryAnchor *lastAnchor; @end - +#endif /** ORKHealthCollector collects CMMotionActivity. diff --git a/ResearchKit/Common/ORKCollector.m b/ResearchKit/Common/ORKCollector.m index d9256b92..cc018cd1 100644 --- a/ResearchKit/Common/ORKCollector.m +++ b/ResearchKit/Common/ORKCollector.m @@ -112,7 +112,7 @@ static NSString *const ItemIdentifierFormatWithTwoPlaceholders = @"org.researchk @end - +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION @implementation ORKHealthCollector : ORKCollector - (instancetype)initWithSampleType:(HKSampleType*)sampleType unit:(HKUnit*)unit startDate:(NSDate*)startDate { @@ -288,7 +288,7 @@ static NSString *const ItemIdentifierFormatWithTwoPlaceholders = @"org.researchk } @end - +#endif @implementation ORKMotionActivityCollector : ORKCollector diff --git a/ResearchKit/Common/ORKCollector_Internal.h b/ResearchKit/Common/ORKCollector_Internal.h index 40079369..0148e3e1 100644 --- a/ResearchKit/Common/ORKCollector_Internal.h +++ b/ResearchKit/Common/ORKCollector_Internal.h @@ -44,6 +44,7 @@ @end +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION @protocol ORKHealthCollectable - (HKSampleType *)sampleType; @@ -71,7 +72,7 @@ @property (copy) HKQueryAnchor *lastAnchor; @end - +#endif @interface ORKMotionActivityCollector() diff --git a/ResearchKit/Common/ORKDataCollectionManager.h b/ResearchKit/Common/ORKDataCollectionManager.h index d1036f1d..2b2d0f95 100644 --- a/ResearchKit/Common/ORKDataCollectionManager.h +++ b/ResearchKit/Common/ORKDataCollectionManager.h @@ -60,6 +60,7 @@ NS_ASSUME_NONNULL_BEGIN If NO is returned or this method is not implemented, the manager will stop the collection for the collector and repeat this same collection next time, until the data is accepted. */ +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION - (BOOL)healthCollector:(ORKHealthCollector *)collector didCollectSamples:(NSArray *)samples; /** @@ -73,7 +74,7 @@ NS_ASSUME_NONNULL_BEGIN until the data is accepted. */ - (BOOL)healthCorrelationCollector:(ORKHealthCorrelationCollector *)collector didCollectCorrelations:(NSArray *)correlations; - +#endif /** Method for delivering the collected motion activities. @@ -145,6 +146,7 @@ ORK_CLASS_AVAILABLE @return Initiated health collector. */ +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION - (ORKHealthCollector *)addHealthCollectorWithSampleType:(HKSampleType *)sampleType unit:(HKUnit *)unit startDate:(NSDate *)startDate @@ -166,6 +168,7 @@ ORK_CLASS_AVAILABLE units:(NSArray *)units startDate:(NSDate *)startDate error:(NSError * _Nullable *)error; +#endif /** Add a collector for motion activity. diff --git a/ResearchKit/Common/ORKDataCollectionManager.m b/ResearchKit/Common/ORKDataCollectionManager.m index 22f59fe1..9861a383 100644 --- a/ResearchKit/Common/ORKDataCollectionManager.m +++ b/ResearchKit/Common/ORKDataCollectionManager.m @@ -33,8 +33,10 @@ #import "ORKCollector_Internal.h" #import "ORKOperation.h" #import "ORKHelpers_Internal.h" -#import +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION +#import +#endif // The file names for persisting the state of our collectors static NSString *const ORKDataCollectionPersistenceFileNamev1 = @".dataCollection.ork.data"; // pre-secureCoding @@ -45,9 +47,11 @@ static NSString *const ORKDataCollectionPersistenceFileNamev2 = @".dataCollecti NSOperationQueue *_operationQueue; NSString * _Nonnull _managedDirectory; NSArray *_collectors; - HKHealthStore *_healthStore; CMMotionActivityManager *_activityManager; +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION + HKHealthStore *_healthStore; NSMutableArray *_completionHandlers; +#endif } - (instancetype)initWithPersistenceDirectoryURL:(NSURL *)directoryURL { @@ -119,12 +123,14 @@ static inline void dispatch_sync_if_not_on_queue(dispatch_queue_t queue, dispatc }); } +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION - (HKHealthStore *)healthStore { if (!_healthStore && [HKHealthStore isHealthDataAvailable]){ _healthStore = [[HKHealthStore alloc] init]; } return _healthStore; } +#endif - (CMMotionActivityManager *)activityManager { if (!_activityManager && [CMMotionActivityManager isActivityAvailable]) { @@ -183,6 +189,7 @@ static inline void dispatch_sync_if_not_on_queue(dispatch_queue_t queue, dispatc _collectors = [collectors copy]; } +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION - (ORKHealthCollector *)addHealthCollectorWithSampleType:(HKSampleType*)sampleType unit:(HKUnit *)unit startDate:(NSDate *)startDate error:(NSError**)error { if (!sampleType) { @@ -235,6 +242,7 @@ static inline void dispatch_sync_if_not_on_queue(dispatch_queue_t queue, dispatc return healthCorrelationCollector; } +#endif - (ORKMotionActivityCollector *)addMotionActivityCollectorWithStartDate:(NSDate *)startDate error:(NSError* __autoreleasing *)error { @@ -336,11 +344,12 @@ static inline void dispatch_sync_if_not_on_queue(dispatch_queue_t queue, dispatc [_delegate dataCollectionManagerDidCompleteCollection:self]; } +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION for (HKObserverQueryCompletionHandler handler in _completionHandlers) { handler(); } [_completionHandlers removeAllObjects]; - +#endif return NO; }]; }]; diff --git a/ResearchKit/Common/ORKDataCollectionManager_Internal.h b/ResearchKit/Common/ORKDataCollectionManager_Internal.h index 1465471e..ec6591dd 100644 --- a/ResearchKit/Common/ORKDataCollectionManager_Internal.h +++ b/ResearchKit/Common/ORKDataCollectionManager_Internal.h @@ -32,10 +32,11 @@ #import "ORKDataCollectionManager.h" #import - @interface ORKDataCollectionManager () +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION @property (nonatomic, strong, readonly) HKHealthStore *healthStore; +#endif @property (nonatomic, strong, readonly) CMMotionActivityManager *activityManager; diff --git a/ResearchKit/Common/ORKFormStep.m b/ResearchKit/Common/ORKFormStep.m index 5f5a4498..8f03016c 100644 --- a/ResearchKit/Common/ORKFormStep.m +++ b/ResearchKit/Common/ORKFormStep.m @@ -181,6 +181,7 @@ } } +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION - (NSSet *)requestedHealthKitTypesForReading { NSMutableSet *healthTypes = [NSMutableSet set]; @@ -194,7 +195,7 @@ return healthTypes.count ? healthTypes : nil; } - +#endif @end diff --git a/ResearchKit/Common/ORKFormStepViewController.m b/ResearchKit/Common/ORKFormStepViewController.m index 6a579f81..b2f222de 100644 --- a/ResearchKit/Common/ORKFormStepViewController.m +++ b/ResearchKit/Common/ORKFormStepViewController.m @@ -335,7 +335,10 @@ static const NSTimeInterval DelayBeforeAutoScroll = 0.25; } - (instancetype)ORKFormStepViewController_initWithResult:(ORKResult *)result { +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION _defaultSource = [ORKAnswerDefaultSource sourceWithHealthStore:[HKHealthStore new]]; +#endif + if (result) { NSAssert([result isKindOfClass:[ORKStepResult class]], @"Expect a ORKStepResult instance"); @@ -377,6 +380,7 @@ static const NSTimeInterval DelayBeforeAutoScroll = 0.25; - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self updateAnsweredSections]; +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION NSMutableSet *types = [NSMutableSet set]; for (ORKFormItem *item in [self formItems]) { ORKAnswerFormat *format = [item answerFormat]; @@ -404,7 +408,7 @@ static const NSTimeInterval DelayBeforeAutoScroll = 0.25; if (!refreshDefaultsPending) { [self refreshDefaults]; } - +#endif // Reset skipped flag - result can now be non-empty _skipped = NO; @@ -491,6 +495,8 @@ static const NSTimeInterval DelayBeforeAutoScroll = 0.25; } - (void)refreshDefaults { + // defaults only come from HealthKit + NSArray *formItems = [self formItems]; ORKAnswerDefaultSource *source = _defaultSource; ORKWeakTypeOf(self) weakSelf = self; @@ -516,10 +522,7 @@ static const NSTimeInterval DelayBeforeAutoScroll = 0.25; ORKStrongTypeOf(weakSelf) strongSelf = weakSelf; [strongSelf updateDefaults:defaults]; }); - - }); - - + }); } - (void)removeAnswerForIdentifier:(NSString *)identifier { diff --git a/ResearchKit/Common/ORKHealthAnswerFormat.h b/ResearchKit/Common/ORKHealthAnswerFormat.h index e2afa235..92cb595c 100644 --- a/ResearchKit/Common/ORKHealthAnswerFormat.h +++ b/ResearchKit/Common/ORKHealthAnswerFormat.h @@ -28,10 +28,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -@import HealthKit; #import +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION +#import +#endif + NS_ASSUME_NONNULL_BEGIN typedef NSString *ORKBiologicalSexIdentifier NS_STRING_ENUM; @@ -62,6 +64,8 @@ ORK_EXTERN ORKBloodTypeIdentifier const ORKBloodTypeIdentifierONegative; You can use the HealthKit characteristic answer format to let users autofill information, such as their blood type or date of birth. */ + +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION ORK_CLASS_AVAILABLE @interface ORKHealthKitCharacteristicTypeAnswerFormat : ORKAnswerFormat @@ -213,6 +217,7 @@ included in the question result generated by form items or question steps - (NSString *)localizedUnitString; @end +#endif NS_ASSUME_NONNULL_END diff --git a/ResearchKit/Common/ORKHealthAnswerFormat.m b/ResearchKit/Common/ORKHealthAnswerFormat.m index 3e9a84a8..2e2d7568 100644 --- a/ResearchKit/Common/ORKHealthAnswerFormat.m +++ b/ResearchKit/Common/ORKHealthAnswerFormat.m @@ -28,7 +28,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #import "ORKHealthAnswerFormat.h" #import "ORKAnswerFormat_Internal.h" @@ -38,6 +37,7 @@ #import "ORKQuestionResult_Private.h" #import "ORKResult.h" +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION #pragma mark - ORKHealthAnswerFormat @@ -420,3 +420,4 @@ NSString *ORKHKBloodTypeString(HKBloodType bloodType) { } @end +#endif diff --git a/ResearchKit/Common/ORKHealthKitPermissionType.m b/ResearchKit/Common/ORKHealthKitPermissionType.m index 7fc36d39..239e01c1 100644 --- a/ResearchKit/Common/ORKHealthKitPermissionType.m +++ b/ResearchKit/Common/ORKHealthKitPermissionType.m @@ -32,7 +32,10 @@ #import "ORKHealthKitPermissionType.h" #import "ORKHelpers_Internal.h" #import "ORKRequestPermissionView.h" + +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION #import +#endif static NSString *const Symbol = @"heart.fill"; static uint32_t const IconTintColor = 0xFF5E5E; @@ -77,6 +80,8 @@ static uint32_t const IconTintColor = 0xFF5E5E; } - (void)checkHealthKitAuthorizationStatus { +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION + if (![HKHealthStore isHealthDataAvailable]) { [self setState:ORKRequestPermissionsButtonStateNotSupported canContinue:YES]; return; @@ -107,9 +112,12 @@ static uint32_t const IconTintColor = 0xFF5E5E; } else { [self setState:ORKRequestPermissionsButtonStateDefault canContinue:NO]; } + +#endif // ORK_FEATURE_HEALTHKIT_AUTHORIZATION } - (void)requestPermissionButtonPressed { +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION [[HKHealthStore new] requestAuthorizationToShareTypes:_sampleTypesToWrite readTypes:_objectTypesToRead completion:^(BOOL success, NSError * _Nullable error) { dispatch_async(dispatch_get_main_queue(), ^{ @@ -121,6 +129,7 @@ static uint32_t const IconTintColor = 0xFF5E5E; [self setState:ORKRequestPermissionsButtonStateConnected canContinue:YES]; }); }]; +#endif // ORK_FEATURE_HEALTHKIT_AUTHORIZATION } - (void)setState:(ORKRequestPermissionsButtonState)state canContinue:(BOOL)canContinue { diff --git a/ResearchKit/Common/ORKHealthSampleQueryOperation.h b/ResearchKit/Common/ORKHealthSampleQueryOperation.h index b70c31c4..ca115306 100644 --- a/ResearchKit/Common/ORKHealthSampleQueryOperation.h +++ b/ResearchKit/Common/ORKHealthSampleQueryOperation.h @@ -29,8 +29,10 @@ */ -#import #import "ORKOperation.h" +#import "ORKDefines.h" + +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION @class ORKDataCollectionManager; @@ -47,3 +49,4 @@ - (instancetype)initWithCollector:(ORKCollector *)collector mananger:(ORKDataCollectionManager *)manager; @end +#endif diff --git a/ResearchKit/Common/ORKHealthSampleQueryOperation.m b/ResearchKit/Common/ORKHealthSampleQueryOperation.m index 09535efa..d6cc387b 100644 --- a/ResearchKit/Common/ORKHealthSampleQueryOperation.m +++ b/ResearchKit/Common/ORKHealthSampleQueryOperation.m @@ -35,6 +35,7 @@ #import "ORKCollector_Internal.h" #import "ORKDataCollectionManager_Internal.h" +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION static NSUInteger const QueryLimitSize = 1000; @@ -245,3 +246,4 @@ static NSUInteger const QueryLimitSize = 1000; } @end +#endif diff --git a/ResearchKit/Common/ORKLocationPermissionType.m b/ResearchKit/Common/ORKLocationPermissionType.m index cde739f3..6e8c46ac 100644 --- a/ResearchKit/Common/ORKLocationPermissionType.m +++ b/ResearchKit/Common/ORKLocationPermissionType.m @@ -33,7 +33,8 @@ #import "ORKHelpers_Internal.h" #import "ORKRequestPermissionButton.h" -@import CoreLocation; +#import +#import static NSString *const Symbol = @"location.circle"; static const uint32_t IconLightTintColor = 0x50C878; @@ -114,7 +115,11 @@ static const uint32_t IconDarkTintColor = 0x00A36C; // Request for always permission. - (void)requestPermissionButtonPressed { - [self.locationManager requestAlwaysAuthorization]; + BOOL requestWasDelivered = [self.locationManager ork_requestAlwaysAuthorization]; + + // if the auth request was not delivered, that means ResearchKit was built with CoreLocation requests disabled + // Presenting the location permission step in this case is probably programmer error + NSAssert(requestWasDelivered, @"Tried to invoke -[CLLocationManager requestAlwaysAuthorization] but ResearchKit was compiled with CoreLocation authorization requests disabled. This is a programmer error. Check build settings for ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION"); } - (void)setState:(ORKRequestPermissionsButtonState)state canContinue:(BOOL)canContinue { diff --git a/ResearchKit/Common/ORKLocationSelectionView.m b/ResearchKit/Common/ORKLocationSelectionView.m index 2ff6e9c0..6ce5a171 100644 --- a/ResearchKit/Common/ORKLocationSelectionView.m +++ b/ResearchKit/Common/ORKLocationSelectionView.m @@ -48,8 +48,9 @@ #import "ORKHelpers_Internal.h" #import "ORKSkin.h" -@import MapKit; +#import +@import MapKit; static const NSString *FormattedAddressLines = @"FormattedAddressLines"; @@ -294,7 +295,7 @@ static const NSString *FormattedAddressLines = @"FormattedAddressLines"; _locationManager.delegate = self; } if (status == kCLAuthorizationStatusNotDetermined) { - [_locationManager requestWhenInUseAuthorization]; + [_locationManager ork_requestWhenInUseAuthorization]; } } } @@ -441,8 +442,10 @@ static const NSString *FormattedAddressLines = @"FormattedAddressLines"; # pragma mark CLLocationManagerDelegate -- (void)locationManagerDidChangeAuthorization:(CLLocationManager *)manager { +- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status { + if (status == kCLAuthorizationStatusAuthorizedAlways || status == kCLAuthorizationStatusAuthorizedWhenInUse) { [self loadCurrentLocationIfNecessary]; + } } #pragma mark MKMapViewDelegate diff --git a/ResearchKit/Common/ORKOrderedTask+ORKPredefinedActiveTask.m b/ResearchKit/Common/ORKOrderedTask+ORKPredefinedActiveTask.m index d3ec5b6d..b656b7c2 100644 --- a/ResearchKit/Common/ORKOrderedTask+ORKPredefinedActiveTask.m +++ b/ResearchKit/Common/ORKOrderedTask+ORKPredefinedActiveTask.m @@ -149,10 +149,10 @@ void ORKStepArrayAddStep(NSMutableArray *array, ORKStep *step) { @implementation ORKOrderedTask (ORKMakeTaskUtilities) + (NSArray*)makeRecorderConfigurationsWithOptions:(ORKPredefinedTaskOption)options { - +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION HKUnit *bpmUnit = [[HKUnit countUnit] unitDividedByUnit:[HKUnit minuteUnit]]; HKQuantityType *heartRateType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeartRate]; - +#endif NSMutableArray *recorderConfigurations = [NSMutableArray arrayWithCapacity:5]; if (!(ORKPredefinedTaskOptionExcludePedometer & options)) { @@ -169,10 +169,13 @@ void ORKStepArrayAddStep(NSMutableArray *array, ORKStep *step) { if (!(ORKPredefinedTaskOptionExcludeLocation & options)) { [recorderConfigurations addObject:[[ORKLocationRecorderConfiguration alloc] initWithIdentifier:ORKLocationRecorderIdentifier]]; } + +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION if (!(ORKPredefinedTaskOptionExcludeHeartRate & options)) { [recorderConfigurations addObject:[[ORKHealthQuantityTypeRecorderConfiguration alloc] initWithIdentifier:ORKHeartRateRecorderIdentifier healthQuantityType:heartRateType unit:bpmUnit]]; } +#endif return [recorderConfigurations copy]; } diff --git a/ResearchKit/Common/ORKPageStep.m b/ResearchKit/Common/ORKPageStep.m index 0b4e92dd..d2964335 100644 --- a/ResearchKit/Common/ORKPageStep.m +++ b/ResearchKit/Common/ORKPageStep.m @@ -80,12 +80,14 @@ return ORKPermissionNone; } +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION - (NSSet *)requestedHealthKitTypesForReading { if ([self.pageTask respondsToSelector:@selector(requestedHealthKitTypesForReading)]) { return [self.pageTask requestedHealthKitTypesForReading]; } return nil; } +#endif #pragma mark - NSCopying diff --git a/ResearchKit/Common/ORKQuestionResult.h b/ResearchKit/Common/ORKQuestionResult.h index 4826b4fa..f950abdb 100644 --- a/ResearchKit/Common/ORKQuestionResult.h +++ b/ResearchKit/Common/ORKQuestionResult.h @@ -30,11 +30,13 @@ #import -@import CoreLocation; +#import + @import Contacts; NS_ASSUME_NONNULL_BEGIN +@class CLCircularRegion; @class ORKQuestionStep; @class ORKFormItem; @class ORKFormStep; diff --git a/ResearchKit/Common/ORKQuestionStep.m b/ResearchKit/Common/ORKQuestionStep.m index 637ba889..8f45016d 100644 --- a/ResearchKit/Common/ORKQuestionStep.m +++ b/ResearchKit/Common/ORKQuestionStep.m @@ -39,6 +39,10 @@ #import "ORKLearnMoreItem.h" #endif +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION +#import +#endif + #if TARGET_OS_IOS ORKQuestionStepPresentationStyle const ORKQuestionStepPresentationStyleDefault = @"default"; ORKQuestionStepPresentationStyle const ORKQuestionStepPresentationStylePlatter = @"platter"; @@ -110,10 +114,12 @@ ORKQuestionStepPresentationStyle const ORKQuestionStepPresentationStylePlatter = return [impliedAnswerFormat isKindOfClass:[ORKTextAnswerFormat class]] && ![(ORKTextAnswerFormat *)impliedAnswerFormat multipleLines]; } +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION - (NSSet *)requestedHealthKitTypesForReading { HKObjectType *objType = [[self answerFormat] healthKitObjectTypeForAuthorization]; return (objType != nil) ? [NSSet setWithObject:objType] : nil; } +#endif #endif diff --git a/ResearchKit/Common/ORKQuestionStepViewController.m b/ResearchKit/Common/ORKQuestionStepViewController.m index d88bd981..ce09461e 100644 --- a/ResearchKit/Common/ORKQuestionStepViewController.m +++ b/ResearchKit/Common/ORKQuestionStepViewController.m @@ -146,7 +146,9 @@ static const NSTimeInterval DelayBeforeAutoScroll = 0.25; - (instancetype)initWithStep:(ORKStep *)step { self = [super initWithStep:step]; if (self) { +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION _defaultSource = [ORKAnswerDefaultSource sourceWithHealthStore:[HKHealthStore new]]; +#endif } return self; } @@ -452,8 +454,10 @@ static const NSTimeInterval DelayBeforeAutoScroll = 0.25; } - (void)requestAndRefreshDefaults { +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION NSMutableSet *types = [NSMutableSet set]; ORKAnswerFormat *format = [[self questionStep] answerFormat]; + HKObjectType *objType = [format healthKitObjectTypeForAuthorization]; if (objType) { [types addObject:objType]; @@ -476,6 +480,10 @@ static const NSTimeInterval DelayBeforeAutoScroll = 0.25; if (!scheduledRefresh) { [self refreshDefaults]; } +#endif + if (_tableView) { + [_tableView reloadData]; + } } - (void)dealloc { diff --git a/ResearchKit/Common/ORKTask.h b/ResearchKit/Common/ORKTask.h index ae5dd15b..6bdbaae6 100644 --- a/ResearchKit/Common/ORKTask.h +++ b/ResearchKit/Common/ORKTask.h @@ -30,7 +30,12 @@ @import Foundation; -@import HealthKit; + +#import + +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION +#import +#endif #import @@ -245,6 +250,7 @@ requests access to these HealthKit types. See also: `requestedHealthKitTypesForWriting`. */ +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION @property (nonatomic, copy, readonly, nullable) NSSet *requestedHealthKitTypesForReading; /** @@ -257,6 +263,7 @@ requests access to these HealthKit types. See also: `requestedHealthKitTypesForReading`. */ @property (nonatomic, copy, readonly, nullable) NSSet *requestedHealthKitTypesForWriting; +#endif /** The set of permissions requested by the task. diff --git a/ResearchKit/Common/ORKTaskViewController.m b/ResearchKit/Common/ORKTaskViewController.m index 1d005171..fccd403a 100644 --- a/ResearchKit/Common/ORKTaskViewController.m +++ b/ResearchKit/Common/ORKTaskViewController.m @@ -61,7 +61,8 @@ @import AVFoundation; @import CoreMotion; -#import +#import +#import typedef void (^_ORKLocationAuthorizationRequestHandler)(BOOL success); @@ -100,28 +101,22 @@ typedef void (^_ORKLocationAuthorizationRequestHandler)(BOOL success); } _started = YES; - - NSString *allowedWhenInUse = (NSString *)[[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"]; - NSString *allowedAlways = (NSString *)[[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysAndWhenInUseUsageDescription"]; + NSString *whenInUseKey = (NSString *)[[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"]; + NSString *alwaysKey = (NSString *)[[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysAndWhenInUseUsageDescription"]; - if (_manager) { - CLAuthorizationStatus status = kCLAuthorizationStatusNotDetermined; - - if (@available(iOS 14.0, *)) { - status = _manager.authorizationStatus; + CLAuthorizationStatus status = [CLLocationManager authorizationStatus]; + if ((status == kCLAuthorizationStatusNotDetermined) && (whenInUseKey || alwaysKey)) { + BOOL requestWasDelivered = YES; + if (alwaysKey) { + requestWasDelivered = [_manager ork_requestAlwaysAuthorization]; } else { - status = [CLLocationManager authorizationStatus]; + requestWasDelivered = [_manager ork_requestWhenInUseAuthorization]; } - - if ((status == kCLAuthorizationStatusNotDetermined) && (allowedWhenInUse || allowedAlways)) { - if (allowedAlways) { - [_manager requestAlwaysAuthorization]; - } else { - [_manager requestWhenInUseAuthorization]; - } - } else { - [self finishWithResult:(status != kCLAuthorizationStatusDenied)]; + if (requestWasDelivered == NO) { + [self finishWithResult:NO]; } + } else { + [self finishWithResult:(status != kCLAuthorizationStatusDenied)]; } } @@ -132,18 +127,10 @@ typedef void (^_ORKLocationAuthorizationRequestHandler)(BOOL success); } } -- (void)locationManagerDidChangeAuthorization:(CLLocationManager *)manager { - CLAuthorizationStatus status = kCLAuthorizationStatusNotDetermined; - - if (@available(iOS 14.0, *)) { - status = manager.authorizationStatus; - } else { - status = [CLLocationManager authorizationStatus]; +- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status { + if (_handler && _started && status != kCLAuthorizationStatusNotDetermined) { + [self finishWithResult:(status != kCLAuthorizationStatusDenied)]; } - - if (_started && status != kCLAuthorizationStatusNotDetermined) { - [self finishWithResult:(status != kCLAuthorizationStatusDenied)]; - } } @end @@ -340,16 +327,24 @@ static NSString *const _ChildNavigationControllerRestorationKey = @"childNavigat writeTypes:(NSSet *)writeTypes handler:(void (^)(void))handler { NSParameterAssert(handler != nil); - if ((![HKHealthStore isHealthDataAvailable]) || (!readTypes && !writeTypes)) { + BOOL needsHealthKitAuthRequest = NO; + +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION + needsHealthKitAuthRequest = ([HKHealthStore isHealthDataAvailable]); + needsHealthKitAuthRequest = needsHealthKitAuthRequest && ((readTypes != nil) || (writeTypes != nil)); +#endif + + if (needsHealthKitAuthRequest == NO) { _requestedHealthTypesForRead = nil; _requestedHealthTypesForWrite = nil; - handler(); + dispatch_async(dispatch_get_main_queue(), handler); return; } _requestedHealthTypesForRead = readTypes; _requestedHealthTypesForWrite = writeTypes; +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION __block HKHealthStore *healthStore = [HKHealthStore new]; [healthStore requestAuthorizationToShareTypes:writeTypes readTypes:readTypes completion:^(BOOL success, NSError *error) { ORK_Log_Error("Health access: error=%@", error); @@ -358,6 +353,7 @@ static NSString *const _ChildNavigationControllerRestorationKey = @"childNavigat // Clear self-ref. healthStore = nil; }]; +#endif } - (void)requestPedometerAccessWithHandler:(void (^)(BOOL success))handler { @@ -443,6 +439,7 @@ static NSString *const _ChildNavigationControllerRestorationKey = @"childNavigat return; } +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION NSSet *readTypes = nil; if ([self.task respondsToSelector:@selector(requestedHealthKitTypesForReading)]) { readTypes = [self.task requestedHealthKitTypesForReading]; @@ -452,6 +449,7 @@ static NSString *const _ChildNavigationControllerRestorationKey = @"childNavigat if ([self.task respondsToSelector:@selector(requestedHealthKitTypesForWriting)]) { writeTypes = [self.task requestedHealthKitTypesForWriting]; } +#endif ORKPermissionMask permissions = [self desiredPermissions]; @@ -459,12 +457,14 @@ static NSString *const _ChildNavigationControllerRestorationKey = @"childNavigat dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ dispatch_async(dispatch_get_main_queue(), ^{ +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION ORK_Log_Debug("Requesting health access"); [self requestHealthStoreAccessWithReadTypes:readTypes writeTypes:writeTypes handler:^{ dispatch_semaphore_signal(semaphore); }]; +#endif }); dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); if (permissions & ORKPermissionCoreMotionAccelerometer) { @@ -1585,8 +1585,15 @@ static NSString *const _ORKProgressMode = @"progressMode"; } if ([_task respondsToSelector:@selector(stepWithIdentifier:)]) { + +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION _requestedHealthTypesForRead = [coder decodeObjectOfClasses:[NSSet setWithArray:@[NSSet.self, HKObjectType.self]] forKey:_ORKRequestedHealthTypesForReadRestoreKey]; _requestedHealthTypesForWrite = [coder decodeObjectOfClasses:[NSSet setWithArray:@[NSSet.self, HKObjectType.self]] forKey:_ORKRequestedHealthTypesForWriteRestoreKey]; +#else + _requestedHealthTypesForRead = nil; + _requestedHealthTypesForWrite = nil; +#endif + _presentedDate = [coder decodeObjectOfClass:[NSDate class] forKey:_ORKPresentedDate]; _lastBeginningInstructionStepIdentifier = [coder decodeObjectOfClass:[NSString class] forKey:_ORKLastBeginningInstructionStepIdentifierKey]; diff --git a/ResearchKit/Common/ORKTaskViewController_Internal.h b/ResearchKit/Common/ORKTaskViewController_Internal.h index dad7d08f..a7fa3e0e 100644 --- a/ResearchKit/Common/ORKTaskViewController_Internal.h +++ b/ResearchKit/Common/ORKTaskViewController_Internal.h @@ -32,7 +32,10 @@ #import #import #import -@import HealthKit; + +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION +#import +#endif NS_ASSUME_NONNULL_BEGIN diff --git a/ResearchKit/Common/ORKTextChoiceCellGroup.m b/ResearchKit/Common/ORKTextChoiceCellGroup.m index e56e383f..1fc78ffd 100644 --- a/ResearchKit/Common/ORKTextChoiceCellGroup.m +++ b/ResearchKit/Common/ORKTextChoiceCellGroup.m @@ -130,7 +130,7 @@ if(choiceOther.textViewStartsHidden) { if(choiceCell.textView.text.length <= 0) { - shouldHideTextView = choiceCell.isSelected == NO; + shouldHideTextView = (choiceCell.isCellSelected == NO); } } @@ -170,6 +170,9 @@ if ([textChoice isKindOfClass:[ORKTextChoiceOther class]] && [touchedCell isKindOfClass:[ORKChoiceOtherViewCell class]]) { ORKTextChoiceOther *otherTextChoice = (ORKTextChoiceOther *)textChoice; ORKChoiceOtherViewCell *touchedOtherCell = (ORKChoiceOtherViewCell *)touchedCell; + if (otherTextChoice.textViewInputOptional || touchedOtherCell.textView.text.length > 0) { + [touchedOtherCell setCellSelected:YES highlight:NO]; + } [self updateTextViewForChoiceOtherCell:touchedOtherCell withTextChoiceOther:otherTextChoice]; if (!otherTextChoice.textViewInputOptional && otherTextChoice.textViewText.length <= 0) { return; diff --git a/ResearchKit/Common/TextChoiceView.swift b/ResearchKit/Common/TextChoiceView.swift index 73e881e6..df135207 100644 --- a/ResearchKit/Common/TextChoiceView.swift +++ b/ResearchKit/Common/TextChoiceView.swift @@ -79,7 +79,6 @@ struct TextChoiceView: View { var dividerPadding: CGFloat = 20 dividerPadding += (imageWidth + 16) - return dividerPadding } diff --git a/ResearchKit/Configuration/ResearchKit/ResearchKit-Shared.xcconfig b/ResearchKit/Configuration/ResearchKit/ResearchKit-Shared.xcconfig index 3082db6b..8bbf5e67 100644 --- a/ResearchKit/Configuration/ResearchKit/ResearchKit-Shared.xcconfig +++ b/ResearchKit/Configuration/ResearchKit/ResearchKit-Shared.xcconfig @@ -25,16 +25,43 @@ SYSTEM_FRAMEWORK_SEARCH_PATHS = $(inherited) SYSTEM_HEADER_SEARCH_PATHS = $(inherited) CLANG_STATIC_ANALYZER_MODE = deep -ORK_FRAMEWORK_VERSION_NUMBER = 2.2.15 +ORK_FRAMEWORK_VERSION_NUMBER = 2.2.16 ORK_FRAMEWORK_BUILD_NUMBER = $(ORK_FRAMEWORK_BUILD_NUMBER_CI_$(CI)) // ORK_FRAMEWORK_BUILD_NUMBER_CI_TRUE or ORK_FRAMEWORK_BUILD_NUMBER_CI_ ORK_FRAMEWORK_BUILD_NUMBER_CI_TRUE = $(CI_BUILD_NUMBER) ORK_FRAMEWORK_BUILD_NUMBER_CI_FALSE = $(ORK_FRAMEWORK_VERSION_NUMBER) // When not building in CI, assume build and version number are the same ORK_FRAMEWORK_BUILD_NUMBER_CI_ = $(ORK_FRAMEWORK_BUILD_NUMBER_CI_FALSE) +// Control whether ResearchKit compiles out CLLocationManager calls that would request authorization for location +// ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION=0 compiles out the feature +// ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION=1 compiles RK with the feature intact +ORK_FEATURE_CLLOCATIONMANAGER_DEFINITIONS = $(ORK_FEATURE_CLLOCATIONMANAGER_DEFINITIONS_$(ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION)) +ORK_FEATURE_CLLOCATIONMANAGER_DEFINITIONS_0 = ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION=0 +ORK_FEATURE_CLLOCATIONMANAGER_DEFINITIONS_1 = ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION=1 +// Some CLLocationManager API calls would trigger authorization to use location. The presence of those +// API calls in ResearchKit **at compile time** mean apps that link ResearchKit also need Info.plist entries +// for NSLocationAlwaysAndWhenInUseUsageDescription and NSLocationWhenInUseUsageDescription. +// If your app doesn't use ORKLocationRecorder and doesn't specify these Info.plist strings, disable +// ResearchKit's CLLocationManager authorization +ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION = 1 + +// Some HealthKit API calls used within the framework require your app to disclose using HealthKit and provide a reason during app store review. +// If your app does not use HealthKit or any of the ResearchKit HealthKit APIs, disable ResearchKit's HealthKit authorization. +ORK_FEATURE_HEALTHKIT_AUTHORIZATION = 1 + +// GCC Preprocessor definitions +ORK_FEATURE_HEALTHKIT_DEFINITIONS_GCC = $(ORK_FEATURE_HEALTHKIT_DEFINITIONS_GCC_$(ORK_FEATURE_HEALTHKIT_AUTHORIZATION)) +ORK_FEATURE_HEALTHKIT_DEFINITIONS_GCC_0 = ORK_FEATURE_HEALTHKIT_AUTHORIZATION=0 +ORK_FEATURE_HEALTHKIT_DEFINITIONS_GCC_1 = ORK_FEATURE_HEALTHKIT_AUTHORIZATION=1 + +// Swift active compilation conditions +ORK_FEATURE_HEALTHKIT_DEFINITIONS_SWIFT = $(ORK_FEATURE_HEALTHKIT_DEFINITIONS_SWIFT_$(ORK_FEATURE_HEALTHKIT_AUTHORIZATION)) +ORK_FEATURE_HEALTHKIT_DEFINITIONS_SWIFT_0 = +ORK_FEATURE_HEALTHKIT_DEFINITIONS_SWIFT_1 = ORK_FEATURE_HEALTHKIT_AUTHORIZATION -#include? "samples/ORKCatalog/ResearchKit-Shared.xcconfig" #include? "../xcconfig/ResearchKit-Shared.xcconfig" -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) $(ORK_GCC_PREPROCESSOR_DEFINITIONS) +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) GLES_SILENCE_DEPRECATION=1 $(ORK_GCC_PREPROCESSOR_DEFINITIONS) $(ORK_FEATURE_CLLOCATIONMANAGER_DEFINITIONS) $(ORK_FEATURE_HEALTHKIT_DEFINITIONS_GCC) + +SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) $(ORK_FEATURE_HEALTHKIT_DEFINITIONS_SWIFT) diff --git a/ResearchKit/Configuration/ResearchKitTests/ResearchKitTests-Debug.xcconfig b/ResearchKit/Configuration/ResearchKitTests/ResearchKitTests-Debug.xcconfig new file mode 100644 index 00000000..8a8f4b7d --- /dev/null +++ b/ResearchKit/Configuration/ResearchKitTests/ResearchKitTests-Debug.xcconfig @@ -0,0 +1,8 @@ +// +// ResearchKitTests-Debug.xcconfig +// + +#include "ResearchKitTests-Shared.xcconfig" + +SWIFT_OBJC_BRIDGING_HEADER = ResearchKitTests/ResearchKitTests-Bridging-Header.h + diff --git a/ResearchKit/Configuration/ResearchKitTests/ResearchKitTests-Shared.xcconfig b/ResearchKit/Configuration/ResearchKitTests/ResearchKitTests-Shared.xcconfig new file mode 100644 index 00000000..b93d2153 --- /dev/null +++ b/ResearchKit/Configuration/ResearchKitTests/ResearchKitTests-Shared.xcconfig @@ -0,0 +1,46 @@ +// +// ResearchKitTests-Shared.xcconfig +// + +CLANG_CXX_LANGUAGE_STANDARD = gnu++0x +GCC_C_LANGUAGE_STANDARD = gnu99 +GCC_WARN_SHADOW = NO +INFOPLIST_FILE = ResearchKitTests/Info.plist +LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks @loader_path/Frameworks +PRODUCT_BUNDLE_IDENTIFIER = org.researchkit.$(PRODUCT_NAME:rfc1034identifier) +PRODUCT_NAME = $(TARGET_NAME) +SWIFT_VERSION = 5.0 + +// Control whether ResearchKitTests expect CLLocationManager calls requesting authorization for location +// ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION=0 compiles out the feature +// ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION=1 compiles RK with the feature intact +ORK_FEATURE_CLLOCATIONMANAGER_DEFINITIONS = $(ORK_FEATURE_CLLOCATIONMANAGER_DEFINITIONS_$(ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION)) +ORK_FEATURE_CLLOCATIONMANAGER_DEFINITIONS_0 = ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION=0 +ORK_FEATURE_CLLOCATIONMANAGER_DEFINITIONS_1 = ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION=1 + +// Some CLLocationManager API calls would trigger authorization to use location. The presence of those +// API calls in ResearchKit **at compile time** mean apps that link ResearchKit also need Info.plist entries +// for NSLocationAlwaysAndWhenInUseUsageDescription and NSLocationWhenInUseUsageDescription. +// If your app doesn't use ORKLocationRecorder and doesn't specify these Info.plist strings, disable +// ResearchKit's CLLocationManager authorization +ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION = 1 + +// Some HealthKit API calls used within the framework require your app to disclose using HealthKit and provide a reason during app store review. +// If your app does not use HealthKit or any of the ResearchKit HealthKit APIs, disable ResearchKit's HealthKit authorization. +ORK_FEATURE_HEALTHKIT_AUTHORIZATION = 1 + +// GCC Preprocessor definitions +ORK_FEATURE_HEALTHKIT_DEFINITIONS_GCC = $(ORK_FEATURE_HEALTHKIT_DEFINITIONS_GCC_$(ORK_FEATURE_HEALTHKIT_AUTHORIZATION)) +ORK_FEATURE_HEALTHKIT_DEFINITIONS_GCC_0 = ORK_FEATURE_HEALTHKIT_AUTHORIZATION=0 +ORK_FEATURE_HEALTHKIT_DEFINITIONS_GCC_1 = ORK_FEATURE_HEALTHKIT_AUTHORIZATION=1 + +// Swift active compilation conditions +ORK_FEATURE_HEALTHKIT_DEFINITIONS_SWIFT = $(ORK_FEATURE_HEALTHKIT_DEFINITIONS_SWIFT_$(ORK_FEATURE_HEALTHKIT_AUTHORIZATION)) +ORK_FEATURE_HEALTHKIT_DEFINITIONS_SWIFT_0 = +ORK_FEATURE_HEALTHKIT_DEFINITIONS_SWIFT_1 = ORK_FEATURE_HEALTHKIT_AUTHORIZATION + +#include? "../xcconfig/ResearchKit-Shared.xcconfig" + +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) GLES_SILENCE_DEPRECATION=1 $(ORK_GCC_PREPROCESSOR_DEFINITIONS) $(ORK_FEATURE_CLLOCATIONMANAGER_DEFINITIONS) $(ORK_FEATURE_HEALTHKIT_DEFINITIONS_GCC) + +SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) $(ORK_FEATURE_HEALTHKIT_DEFINITIONS_SWIFT) diff --git a/ResearchKit/PrivateHeaders/ResearchKit_Private.h b/ResearchKit/PrivateHeaders/ResearchKit_Private.h index 51f92618..c3f8185b 100644 --- a/ResearchKit/PrivateHeaders/ResearchKit_Private.h +++ b/ResearchKit/PrivateHeaders/ResearchKit_Private.h @@ -183,3 +183,6 @@ #import #import +// Class extensions +#import + diff --git a/ResearchKit/ResearchKit.h b/ResearchKit/ResearchKit.h index b913eedf..75c5a22c 100644 --- a/ResearchKit/ResearchKit.h +++ b/ResearchKit/ResearchKit.h @@ -65,7 +65,6 @@ #import #import - #import #import #import diff --git a/ResearchKitTests/ORKAnswerFormatTests.m b/ResearchKitTests/ORKAnswerFormatTests.m index 24853b0b..fa5b0f56 100644 --- a/ResearchKitTests/ORKAnswerFormatTests.m +++ b/ResearchKitTests/ORKAnswerFormatTests.m @@ -1509,6 +1509,7 @@ XCTAssertEqual([[[answerFormat textChoices] objectAtIndex:1] value], [NSNumber numberWithInteger:2]); } +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION - (void)testHealthKitCharacteristicTypeAnswerFormat { HKCharacteristicType *biologicalSex = [HKCharacteristicType characteristicTypeForIdentifier:HKCharacteristicTypeIdentifierBiologicalSex]; @@ -1596,6 +1597,7 @@ XCTAssertEqual([answerFormat numericAnswerStyle], ORKNumericAnswerStyleInteger); XCTAssertEqual([answerFormat quantityType], calories); } +#endif - (void)testDateAnswerFormat { diff --git a/ResearchKitTests/ORKHKSampleTests.m b/ResearchKitTests/ORKHKSampleTests.m index 40b93317..aaa6e252 100644 --- a/ResearchKitTests/ORKHKSampleTests.m +++ b/ResearchKitTests/ORKHKSampleTests.m @@ -42,6 +42,7 @@ @implementation ORKHKSampleTests +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION - (void)testHKSampleSerialization { NSDate *d1 = [NSDate dateWithTimeIntervalSinceReferenceDate:0]; NSDate *d2 = [NSDate dateWithTimeInterval:10 sinceDate:d1]; @@ -107,5 +108,5 @@ XCTAssertTrue([dict[@"objects"] containsObject:dd], @""); XCTAssertTrue([dict[@"objects"] containsObject:ds], @""); } - +#endif @end diff --git a/ResearchKitTests/ORKHealthKitQuestionStepViewControllerTests.swift b/ResearchKitTests/ORKHealthKitQuestionStepViewControllerTests.swift index 4e6f5ed6..e1b73eee 100644 --- a/ResearchKitTests/ORKHealthKitQuestionStepViewControllerTests.swift +++ b/ResearchKitTests/ORKHealthKitQuestionStepViewControllerTests.swift @@ -30,7 +30,7 @@ import XCTest import ResearchKit.Private - +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION final class ORKHealthKitQuestionStepViewControllerTests: XCTestCase { var testController: ORKStepViewController! var step: ORKStep! @@ -131,4 +131,4 @@ extension ORKHealthKitQuestionStepViewControllerTests: ORKStepViewControllerDele return true } } - +#endif diff --git a/ResearchKitTests/ORKRecorderTests.m b/ResearchKitTests/ORKRecorderTests.m index f7d3ffe0..f8faac07 100644 --- a/ResearchKitTests/ORKRecorderTests.m +++ b/ResearchKitTests/ORKRecorderTests.m @@ -431,8 +431,26 @@ static const NSInteger kNumberOfSamples = 5; } [recorder stop]; - [self checkResult]; + // when location features are compiled out, make sure that the "ork_" wrapper functions are + // returning false, but don't bother doing the checkResult step +#if !ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION + // when location auth is 0, the [recorder start] earlier immediately stops, which zeros + // the locationManager property. So we would expect currentLocationManager == nil + XCTAssertNil(currentLocationManager, @"ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION=0: expected recorder's location manager to be nil"); + + // make a one-time-use locationManager that we test ork_ wrappers with + { + CLLocationManager *locationManager = [(ORKMockLocationRecorder *)recorder createLocationManager]; + XCTAssertNotNil(locationManager); + XCTAssertFalse([locationManager ork_requestWhenInUseAuthorization]); + XCTAssertFalse([locationManager ork_requestAlwaysAuthorization]); + } + +#else + [self checkResult]; +#endif // ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION + for (NSDictionary *sample in _items) { XCTAssertTrue(ork_doubleEqual(altitude, ((NSNumber *)sample[@"altitude"]).doubleValue), @""); @@ -621,7 +639,7 @@ static const NSInteger kNumberOfSamples = 5; } - (void)testHealthQuantityTypeRecorder { - +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION HKUnit *bpmUnit = [[HKUnit countUnit] unitDividedByUnit:[HKUnit minuteUnit]]; HKQuantityType *hbQuantityType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeartRate]; ORKHealthQuantityTypeRecorderConfiguration *recorderConfiguration = [[ORKHealthQuantityTypeRecorderConfiguration alloc] initWithIdentifier:@"healtQuantityTypeRecorder" healthQuantityType:hbQuantityType unit:bpmUnit]; @@ -629,6 +647,7 @@ static const NSInteger kNumberOfSamples = 5; ORKHealthQuantityTypeRecorder *recorder = (ORKHealthQuantityTypeRecorder *)[self createRecorder:recorderConfiguration]; XCTAssertTrue([recorder isKindOfClass:recorderClass], @""); +#endif } @end diff --git a/ResearchKitTests/ORKResultTests.m b/ResearchKitTests/ORKResultTests.m index b41ecd2e..1938598a 100644 --- a/ResearchKitTests/ORKResultTests.m +++ b/ResearchKitTests/ORKResultTests.m @@ -204,13 +204,14 @@ // create the task as if we were to present it ORKTaskViewController *taskViewController = [[ORKTaskViewController alloc] initWithTask:task taskRunUUID:nil]; +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION // Trigger requestHealth access to fill in the read/write types ivars NSSet *readTypes = [NSSet setWithObjects:[HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeartRate], nil]; NSSet *writeTypes = [NSSet setWithObjects:[HKObjectType categoryTypeForIdentifier:HKCategoryTypeIdentifierBloating], nil]; [taskViewController requestHealthStoreAccessWithReadTypes:readTypes writeTypes:writeTypes handler:^(){ // intentionally left empty }]; - +#endif // viewWillAppear fills in the _managedStepIdentifiers in the taskViewController [taskViewController viewWillAppear:false]; @@ -244,10 +245,11 @@ ORKResultTestsHelper *taskDelegate = [[ORKResultTestsHelper alloc] init]; ORKTaskViewController *taskViewController = [[ORKTaskViewController alloc] initWithTask:task restorationData:encodedTaskViewControllerData delegate:taskDelegate error:nil]; +#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION // confirm the read/write HK type info made it across the encode/decode bridge XCTAssertEqualObjects([taskViewController requestedHealthTypesForRead], [NSSet setWithObject:[HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeartRate]]); XCTAssertEqualObjects([taskViewController requestedHealthTypesForWrite], [NSSet setWithObject:[HKObjectType categoryTypeForIdentifier:HKCategoryTypeIdentifierBloating]]); - +#endif ORKStepResult *stepResult = (ORKStepResult *)[[[taskViewController result] results] firstObject]; NSArray *questionResults = (NSArray *)[stepResult results]; XCTAssertEqual([questionResults count], 3); diff --git a/samples/ORKCatalog/ORKCatalog.xcodeproj/project.pbxproj b/samples/ORKCatalog/ORKCatalog.xcodeproj/project.pbxproj index defe55fc..31d3fa23 100644 --- a/samples/ORKCatalog/ORKCatalog.xcodeproj/project.pbxproj +++ b/samples/ORKCatalog/ORKCatalog.xcodeproj/project.pbxproj @@ -9,12 +9,12 @@ /* Begin PBXBuildFile section */ 03F7FD9F23959DD500432EF6 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F7FD9E23959DD500432EF6 /* SceneDelegate.swift */; }; 0B6CB32728B577640092E993 /* Bundle+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B6CB32628B577640092E993 /* Bundle+Extensions.swift */; }; + 0BF63B3D2BC0977A00546F58 /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0BF63B3C2BC0977A00546F58 /* HealthKit.framework */; }; 3C39E45E2950EFAD00BD96F6 /* ORKCatalog-Shared.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 3C39E45D2950EFAD00BD96F6 /* ORKCatalog-Shared.xcconfig */; }; 3E39B9FB1ABF682D00C2ABE5 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3E39B9F91ABF682D00C2ABE5 /* Main.storyboard */; }; 3E39B9FE1ABF683700C2ABE5 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3E39B9FC1ABF683700C2ABE5 /* LaunchScreen.xib */; }; 3E39BA001ABF683F00C2ABE5 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3E39B9FF1ABF683F00C2ABE5 /* Images.xcassets */; }; 3ED967B81AC13D37007E2D83 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3ED967BA1AC13D37007E2D83 /* Localizable.strings */; }; - 863CCD821ACF545E009FD3B4 /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 863CCD811ACF545E009FD3B4 /* HealthKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; 86B89AA51AB2C0A5001626A4 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86B89A9C1AB2C0A5001626A4 /* AppDelegate.swift */; }; 9622283924F04DBF0056E74D /* TaskScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9622283824F04DBF0056E74D /* TaskScreen.swift */; }; 9622283B24F04DFE0056E74D /* CommonElements.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9622283A24F04DFE0056E74D /* CommonElements.swift */; }; @@ -60,6 +60,7 @@ /* Begin PBXFileReference section */ 03F7FD9E23959DD500432EF6 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 0B6CB32628B577640092E993 /* Bundle+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+Extensions.swift"; sourceTree = ""; }; + 0BF63B3C2BC0977A00546F58 /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = System/Library/Frameworks/HealthKit.framework; sourceTree = SDKROOT; }; 246DF9FF1BEADFDF00591E9A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; 246DFA021BEAE01700591E9A /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = ""; }; 246DFA031BEAE02100591E9A /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; @@ -104,7 +105,6 @@ 3E39B9FD1ABF683700C2ABE5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 3E39B9FF1ABF683F00C2ABE5 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 3ED967B91AC13D37007E2D83 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = ""; }; - 863CCD811ACF545E009FD3B4 /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = System/Library/Frameworks/HealthKit.framework; sourceTree = SDKROOT; }; 863CCD831ACF545E009FD3B4 /* ORKCatalog.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = ORKCatalog.entitlements; sourceTree = ""; }; 869230BE1AAA890A00BFE11B /* ORKCatalog.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ORKCatalog.app; sourceTree = BUILT_PRODUCTS_DIR; }; 86B89A981AB2C0A5001626A4 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -135,7 +135,7 @@ buildActionMask = 2147483647; files = ( BC2A3CD91C58F1CA00DA64B7 /* ResearchKit.framework in Frameworks */, - 863CCD821ACF545E009FD3B4 /* HealthKit.framework in Frameworks */, + 0BF63B3D2BC0977A00546F58 /* HealthKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -254,8 +254,8 @@ BC2A3C9E1C58E81500DA64B7 /* Frameworks */ = { isa = PBXGroup; children = ( + 0BF63B3C2BC0977A00546F58 /* HealthKit.framework */, BC2A3CD81C58F1CA00DA64B7 /* ResearchKit.framework */, - 863CCD811ACF545E009FD3B4 /* HealthKit.framework */, ); name = Frameworks; sourceTree = ""; diff --git a/samples/ORKCatalog/ORKCatalog/Configuration/ORKCatalog-Shared.xcconfig b/samples/ORKCatalog/ORKCatalog/Configuration/ORKCatalog-Shared.xcconfig index ed425750..39bdcf6c 100644 --- a/samples/ORKCatalog/ORKCatalog/Configuration/ORKCatalog-Shared.xcconfig +++ b/samples/ORKCatalog/ORKCatalog/Configuration/ORKCatalog-Shared.xcconfig @@ -2,7 +2,7 @@ // ORKCatalog-Shared.xcconfig // -ORK_CATALOG_VERSION_NUMBER = 2.2.15 +ORK_CATALOG_VERSION_NUMBER = 2.2.16 ORK_CATALOG_BUILD_NUMBER = $(ORK_CATALOG_BUILD_NUMBER_CI_$(CI)) // ORK_CATALOG_BUILD_NUMBER_CI_TRUE or ORK_CATALOG_BUILD_NUMBER_CI_ ORK_CATALOG_BUILD_NUMBER_CI_TRUE = $(CI_BUILD_NUMBER) // if CI_BUILD_NUMBER is defined (presumably by CI) just use it @@ -15,3 +15,19 @@ ORK_CATALOG_BUNDLE_ID = $(ORK_CATALOG_BUNDLE_ID_CI_$(USE_CI)) // ORK_CATALOG_BUN ORK_CATALOG_BUNDLE_ID_CI_TRUE = $(USE_CI_BUNDLE_ID)$(ORK_CATALOG_CI_BUNDLE_ID_SUFFIX) // if CI_BUNDLE_ID is defined (presumably by CI) just use it, allow appending an optional suffix ORK_CATALOG_BUNDLE_ID_CI_FALSE = $(ORK_CATALOG_BUNDLE_ID_BASE) // when not in CI, use the Base bundle ID ORK_CATALOG_BUNDLE_ID_CI_ = $(ORK_CATALOG_BUNDLE_ID_CI_FALSE) + +ORK_FEATURE_HEALTHKIT_AUTHORIZATION = 1 + +// GCC Preprocessor definitions +ORK_FEATURE_HEALTHKIT_DEFINITIONS_GCC = $(ORK_FEATURE_HEALTHKIT_DEFINITIONS_GCC_$(ORK_FEATURE_HEALTHKIT_AUTHORIZATION)) +ORK_FEATURE_HEALTHKIT_DEFINITIONS_GCC_0 = ORK_FEATURE_HEALTHKIT_AUTHORIZATION=0 +ORK_FEATURE_HEALTHKIT_DEFINITIONS_GCC_1 = ORK_FEATURE_HEALTHKIT_AUTHORIZATION=1 + +// Swift active compilation conditions +ORK_FEATURE_HEALTHKIT_DEFINITIONS_SWIFT = $(ORK_FEATURE_HEALTHKIT_DEFINITIONS_SWIFT_$(ORK_FEATURE_HEALTHKIT_AUTHORIZATION)) +ORK_FEATURE_HEALTHKIT_DEFINITIONS_SWIFT_0 = +ORK_FEATURE_HEALTHKIT_DEFINITIONS_SWIFT_1 = ORK_FEATURE_HEALTHKIT_AUTHORIZATION + +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) $(ORK_FEATURE_HEALTHKIT_DEFINITIONS_GCC) + +SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) $(ORK_FEATURE_HEALTHKIT_DEFINITIONS_SWIFT) diff --git a/samples/ORKCatalog/ORKCatalog/Supporting Files/ORKCatalog.entitlements b/samples/ORKCatalog/ORKCatalog/Supporting Files/ORKCatalog.entitlements index e10f4302..b3187f1d 100644 --- a/samples/ORKCatalog/ORKCatalog/Supporting Files/ORKCatalog.entitlements +++ b/samples/ORKCatalog/ORKCatalog/Supporting Files/ORKCatalog.entitlements @@ -2,7 +2,7 @@ - com.apple.developer.healthkit - + com.apple.developer.healthkit + diff --git a/samples/ORKCatalog/ORKCatalog/Tasks/TaskListRow.swift b/samples/ORKCatalog/ORKCatalog/Tasks/TaskListRow.swift index 804f9940..c6742c78 100644 --- a/samples/ORKCatalog/ORKCatalog/Tasks/TaskListRow.swift +++ b/samples/ORKCatalog/ORKCatalog/Tasks/TaskListRow.swift @@ -140,7 +140,7 @@ enum TaskListRow: Int, CustomStringConvertible { /// Returns an array of all the task list row enum cases. static var sections: [ TaskListRowSection ] { - let defaultSections = [ + var defaultSections = [ TaskListRowSection(title: "Surveys", rows: [ .form, @@ -261,10 +261,8 @@ enum TaskListRow: Int, CustomStringConvertible { case .weightQuestion: return NSLocalizedString("Weight Question", comment: "") - case .healthQuantity: return NSLocalizedString("Health Quantity Question", comment: "") - case .imageChoiceQuestion: return NSLocalizedString("Image Choice Question", comment: "") @@ -1190,7 +1188,7 @@ enum TaskListRow: Int, CustomStringConvertible { let step3NonOptional = ORKQuestionStep(identifier: String(describing: Identifier.heightQuestionStep3) + "NonOptional", title: NSLocalizedString("Height", comment: ""), question: exampleQuestionText, answer: answerFormat3) step3NonOptional.text = "USC system (Non Optional)" step3NonOptional.isOptional = false - + let answerFormat4 = ORKHealthKitQuantityTypeAnswerFormat(quantityType: HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.height)!, unit: HKUnit.meterUnit(with: .centi), style: .decimal) let step4 = ORKQuestionStep(identifier: String(describing: Identifier.heightQuestionStep4), title: NSLocalizedString("Height", comment: ""), question: exampleQuestionText, answer: answerFormat4) @@ -1207,80 +1205,78 @@ enum TaskListRow: Int, CustomStringConvertible { /// This task demonstrates a question asking for the user weight. private var weightQuestionTask: ORKTask { let answerFormat1 = ORKAnswerFormat.weightAnswerFormat() - - let step1 = ORKQuestionStep(identifier: String(describing: Identifier.weightQuestionStep1), title: NSLocalizedString("Weight", comment: ""), question: exampleQuestionText, answer: answerFormat1) - - step1.text = "Local system, default precision" - - let step1NonOptional = ORKQuestionStep(identifier: String(describing: Identifier.weightQuestionStep1) + "NonOptional", title: NSLocalizedString("Weight", comment: ""), question: exampleQuestionText, answer: answerFormat1) - - step1NonOptional.text = "Local system, default precision (nonOptional)" - step1NonOptional.isOptional = false - - let answerFormat2 = ORKAnswerFormat.weightAnswerFormat(with: ORKMeasurementSystem.metric) - - let step2 = ORKQuestionStep(identifier: String(describing: Identifier.weightQuestionStep2), title: NSLocalizedString("Weight", comment: ""), question: exampleQuestionText, answer: answerFormat2) - - step2.text = "Metric system, default precision" - - let step2NonOptional = ORKQuestionStep(identifier: String(describing: Identifier.weightQuestionStep2) + "NonOptional", title: NSLocalizedString("Weight", comment: ""), question: exampleQuestionText, answer: answerFormat2) - - step2NonOptional.text = "Metric system, default precision (nonOptional)" - step2NonOptional.isOptional = false - - let answerFormat3 = ORKAnswerFormat.weightAnswerFormat(with: ORKMeasurementSystem.metric, numericPrecision: ORKNumericPrecision.low, minimumValue: ORKDoubleDefaultValue, maximumValue: ORKDoubleDefaultValue, defaultValue: ORKDoubleDefaultValue) - - let step3 = ORKQuestionStep(identifier: String(describing: Identifier.weightQuestionStep3), title: NSLocalizedString("Weight", comment: ""), question: exampleQuestionText, answer: answerFormat3) - - step3.text = "Metric system, low precision" + + let step1 = ORKQuestionStep(identifier: String(describing: Identifier.weightQuestionStep1), title: NSLocalizedString("Weight", comment: ""), question: exampleQuestionText, answer: answerFormat1) + + step1.text = "Local system, default precision" + + let step1NonOptional = ORKQuestionStep(identifier: String(describing: Identifier.weightQuestionStep1) + "NonOptional", title: NSLocalizedString("Weight", comment: ""), question: exampleQuestionText, answer: answerFormat1) + + step1NonOptional.text = "Local system, default precision (nonOptional)" + step1NonOptional.isOptional = false + + let answerFormat2 = ORKAnswerFormat.weightAnswerFormat(with: ORKMeasurementSystem.metric) + + let step2 = ORKQuestionStep(identifier: String(describing: Identifier.weightQuestionStep2), title: NSLocalizedString("Weight", comment: ""), question: exampleQuestionText, answer: answerFormat2) + + step2.text = "Metric system, default precision" + + let step2NonOptional = ORKQuestionStep(identifier: String(describing: Identifier.weightQuestionStep2) + "NonOptional", title: NSLocalizedString("Weight", comment: ""), question: exampleQuestionText, answer: answerFormat2) + + step2NonOptional.text = "Metric system, default precision (nonOptional)" + step2NonOptional.isOptional = false + + let answerFormat3 = ORKAnswerFormat.weightAnswerFormat(with: ORKMeasurementSystem.metric, numericPrecision: ORKNumericPrecision.low, minimumValue: ORKDoubleDefaultValue, maximumValue: ORKDoubleDefaultValue, defaultValue: ORKDoubleDefaultValue) + + let step3 = ORKQuestionStep(identifier: String(describing: Identifier.weightQuestionStep3), title: NSLocalizedString("Weight", comment: ""), question: exampleQuestionText, answer: answerFormat3) + + step3.text = "Metric system, low precision" - let step3NonOptional = ORKQuestionStep(identifier: String(describing: Identifier.weightQuestionStep3) + "NonOptional", title: NSLocalizedString("Weight", comment: ""), question: exampleQuestionText, answer: answerFormat3) - step3NonOptional.text = "Metric system, low precision (nonOptional)" - step3NonOptional.isOptional = false - - let answerFormat4 = ORKAnswerFormat.weightAnswerFormat(with: ORKMeasurementSystem.metric, numericPrecision: ORKNumericPrecision.high, minimumValue: 20.0, maximumValue: 100.0, defaultValue: 45.50) - - let step4 = ORKQuestionStep(identifier: String(describing: Identifier.weightQuestionStep4), title: NSLocalizedString("Weight", comment: ""), question: exampleQuestionText, answer: answerFormat4) - - step4.text = "Metric system, high precision" + let step3NonOptional = ORKQuestionStep(identifier: String(describing: Identifier.weightQuestionStep3) + "NonOptional", title: NSLocalizedString("Weight", comment: ""), question: exampleQuestionText, answer: answerFormat3) + step3NonOptional.text = "Metric system, low precision (nonOptional)" + step3NonOptional.isOptional = false + + let answerFormat4 = ORKAnswerFormat.weightAnswerFormat(with: ORKMeasurementSystem.metric, numericPrecision: ORKNumericPrecision.high, minimumValue: 20.0, maximumValue: 100.0, defaultValue: 45.50) + + let step4 = ORKQuestionStep(identifier: String(describing: Identifier.weightQuestionStep4), title: NSLocalizedString("Weight", comment: ""), question: exampleQuestionText, answer: answerFormat4) + + step4.text = "Metric system, high precision" - let step4NonOptional = ORKQuestionStep(identifier: String(describing: Identifier.weightQuestionStep4) + "NonOptional", title: NSLocalizedString("Weight", comment: ""), question: exampleQuestionText, answer: answerFormat4) - step4NonOptional.text = "Metric system, high precision (nonOptional)" - step4NonOptional.isOptional = false - - let answerFormat5 = ORKAnswerFormat.weightAnswerFormat(with: ORKMeasurementSystem.USC) - - let step5 = ORKQuestionStep(identifier: String(describing: Identifier.weightQuestionStep5), title: NSLocalizedString("Weight", comment: ""), question: exampleQuestionText, answer: answerFormat5) - - step5.text = "USC system, default precision" - - let step5NonOptional = ORKQuestionStep(identifier: String(describing: Identifier.weightQuestionStep5) + "NonOptional", title: NSLocalizedString("Weight", comment: ""), question: exampleQuestionText, answer: answerFormat5) - step5NonOptional.text = "USC system, default precision (nonOptional)" - step5NonOptional.isOptional = false - - let answerFormat6 = ORKAnswerFormat.weightAnswerFormat(with: ORKMeasurementSystem.USC, numericPrecision: ORKNumericPrecision.high, minimumValue: 50.0, maximumValue: 150.0, defaultValue: 100.0) - - let step6 = ORKQuestionStep(identifier: String(describing: Identifier.weightQuestionStep6), title: NSLocalizedString("Weight", comment: ""), question: exampleQuestionText, answer: answerFormat6) - - step6.text = "USC system, high precision" + let step4NonOptional = ORKQuestionStep(identifier: String(describing: Identifier.weightQuestionStep4) + "NonOptional", title: NSLocalizedString("Weight", comment: ""), question: exampleQuestionText, answer: answerFormat4) + step4NonOptional.text = "Metric system, high precision (nonOptional)" + step4NonOptional.isOptional = false + + let answerFormat5 = ORKAnswerFormat.weightAnswerFormat(with: ORKMeasurementSystem.USC) + + let step5 = ORKQuestionStep(identifier: String(describing: Identifier.weightQuestionStep5), title: NSLocalizedString("Weight", comment: ""), question: exampleQuestionText, answer: answerFormat5) + + step5.text = "USC system, default precision" + + let step5NonOptional = ORKQuestionStep(identifier: String(describing: Identifier.weightQuestionStep5) + "NonOptional", title: NSLocalizedString("Weight", comment: ""), question: exampleQuestionText, answer: answerFormat5) + step5NonOptional.text = "USC system, default precision (nonOptional)" + step5NonOptional.isOptional = false + + let answerFormat6 = ORKAnswerFormat.weightAnswerFormat(with: ORKMeasurementSystem.USC, numericPrecision: ORKNumericPrecision.high, minimumValue: 50.0, maximumValue: 150.0, defaultValue: 100.0) + + let step6 = ORKQuestionStep(identifier: String(describing: Identifier.weightQuestionStep6), title: NSLocalizedString("Weight", comment: ""), question: exampleQuestionText, answer: answerFormat6) + + step6.text = "USC system, high precision" - let step6NonOptional = ORKQuestionStep(identifier: String(describing: Identifier.weightQuestionStep6) + "NonOptional", title: NSLocalizedString("Weight", comment: ""), question: exampleQuestionText, answer: answerFormat6) - step6NonOptional.text = "USC system, high precision (nonOptional)" - step6NonOptional.isOptional = false + let step6NonOptional = ORKQuestionStep(identifier: String(describing: Identifier.weightQuestionStep6) + "NonOptional", title: NSLocalizedString("Weight", comment: ""), question: exampleQuestionText, answer: answerFormat6) + step6NonOptional.text = "USC system, high precision (nonOptional)" + step6NonOptional.isOptional = false + + let answerFormat7 = ORKHealthKitQuantityTypeAnswerFormat(quantityType: HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.bodyMass)!, unit: HKUnit.gramUnit(with: .kilo), style: .decimal) + let step7 = ORKQuestionStep(identifier: String(describing: Identifier.weightQuestionStep7), title: NSLocalizedString("Weight", comment: ""), question: exampleQuestionText, answer: answerFormat7) + step7.text = "HealthKit, body mass" - let answerFormat7 = ORKHealthKitQuantityTypeAnswerFormat(quantityType: HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.bodyMass)!, unit: HKUnit.gramUnit(with: .kilo), style: .decimal) + let step7NonOptional = ORKQuestionStep(identifier: String(describing: Identifier.weightQuestionStep7) + "NonOptional", title: NSLocalizedString("Weight", comment: ""), question: exampleQuestionText, answer: answerFormat7) + step7NonOptional.text = "HealthKit, body mass (nonOptional)" + step7NonOptional.isOptional = false - let step7 = ORKQuestionStep(identifier: String(describing: Identifier.weightQuestionStep7), title: NSLocalizedString("Weight", comment: ""), question: exampleQuestionText, answer: answerFormat7) - - step7.text = "HealthKit, body mass" - - let step7NonOptional = ORKQuestionStep(identifier: String(describing: Identifier.weightQuestionStep7) + "NonOptional", title: NSLocalizedString("Weight", comment: ""), question: exampleQuestionText, answer: answerFormat7) - step7NonOptional.text = "HealthKit, body mass (nonOptional)" - step7NonOptional.isOptional = false - - return ORKOrderedTask(identifier: String(describing: Identifier.weightQuestionTask), steps: [step1, step1NonOptional, step2, step2NonOptional, step3, step3NonOptional, step4, step4NonOptional, step5, step5NonOptional, step6, step6NonOptional, step7NonOptional, step7]) + return ORKOrderedTask(identifier: String(describing: Identifier.weightQuestionTask), steps: [step1, step1NonOptional, step2, step2NonOptional, step3, step3NonOptional, step4, step4NonOptional, step5, step5NonOptional, step6, step6NonOptional, step7NonOptional, step7]) } - + private var healthQuantityTypeTask: ORKTask { let heartRateHealthKitQuantityTypeAnswerFormat = ORKHealthKitQuantityTypeAnswerFormat(quantityType: HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate)!, unit: nil, style: .decimal) let heartRateQuestion = ORKQuestionStep(identifier: String(describing: Identifier.healthQuantityQuestion1), title: NSLocalizedString("Heart Rate", comment: ""), question: "What is your Heart Rate?", answer: heartRateHealthKitQuantityTypeAnswerFormat) @@ -1705,6 +1701,7 @@ enum TaskListRow: Int, CustomStringConvertible { let notificationsPermissionType = ORKNotificationPermissionType(authorizationOptions: [.alert, .badge, .sound]) let motionActivityPermissionType = ORKMotionActivityPermissionType() + let locationPermissionType = ORKLocationPermissionType() let healthKitTypesToWrite: Set = [ HKObjectType.quantityType(forIdentifier: .bodyMassIndex)!, @@ -1719,11 +1716,9 @@ enum TaskListRow: Int, CustomStringConvertible { let healthKitPermissionType = ORKHealthKitPermissionType(sampleTypesToWrite: healthKitTypesToWrite, objectTypesToRead: healthKitTypesToRead) - let locationPermissionType = ORKLocationPermissionType() - let requestPermissionsStep = ORKRequestPermissionsStep( - identifier: String(describing: Identifier.requestPermissionsStep), - permissionTypes: [notificationsPermissionType, motionActivityPermissionType, healthKitPermissionType, locationPermissionType]) + identifier: String(describing: Identifier.requestPermissionsStep), + permissionTypes: [notificationsPermissionType, motionActivityPermissionType, healthKitPermissionType, locationPermissionType]) requestPermissionsStep.title = "Health Data Request" requestPermissionsStep.text = "Please review the health data types below and enable sharing to contribute to the study."