Compare commits
4 Commits
main
...
dev/SwiftUI
| Author | SHA1 | Date | |
|---|---|---|---|
| 8b2cf8889a | |||
| 3f83f6ab4f | |||
| 7abce4c982 | |||
| eedd39d0a8 |
@@ -79,6 +79,78 @@ The height question is presented in the figure below.
|
||||
|---|---|
|
||||
|  |  |
|
||||
|
||||
### ResearchKit SwiftUI
|
||||
|
||||
We are excited to announce the release of a new beta API for surveys in ResearchKit. This API is designed to enhance the flexibility, customization, and cross-platform compatibility of surveys in your ResearchKit apps. Below are the key features and usage details.
|
||||
|
||||
New Form APIs offer an easily configurable and flexible UI, with the same look and feel of `ORKFormStep`:
|
||||
* `ResearchForm`
|
||||
* Manages the navigation between steps in a survey.
|
||||
* `ResearchFormStep`
|
||||
* Represents a step in a survey and lays out the header and questions on one page. Question numbers (e.g. 1 of 3) are automatically added at the top of each question to denote progress in a step.
|
||||
* `ResearchFormCompletion`
|
||||
* Represents the context for a survey's completion
|
||||
* `ResearchFormResult`
|
||||
* Represents responses for the different kinds of questions.
|
||||
* `StepHeader`
|
||||
* A step header containing an image, title, and subtitle.
|
||||
* `QuestionHeader`
|
||||
* A question header containing a title and detail.
|
||||
* `InstructionBodyItem`
|
||||
* Displays an image and text side by side.
|
||||
* `questionRequired` (`ViewModifier`)
|
||||
* Designates a question as required or optional.
|
||||
|
||||
#### Survey Question Types:
|
||||
* `MultipleChoiceQuestion`
|
||||
* `HeightQuestion`
|
||||
* `WeightQuestion`
|
||||
* `SliderQuestion`
|
||||
* `TextQuestion`
|
||||
* `DateTimeQuestion`
|
||||
* `NumericQuestion`
|
||||
* `ImageChoiceQuestion`
|
||||
|
||||
The example below shows how to create a `ResearchForm` to present a text question for the participant to answer, and then save their results.
|
||||
|
||||
```swift
|
||||
import ResearchKitSwiftUI
|
||||
|
||||
ResearchForm(
|
||||
id: "SurveyTask",
|
||||
steps: {
|
||||
ResearchFormStep(
|
||||
title: "Demographics",
|
||||
subtitle: "Tell us about yourself",
|
||||
content: {
|
||||
TextQuestion(
|
||||
id: "textQuestion",
|
||||
title: "What is your name?",
|
||||
prompt: "Enter your name here",
|
||||
lineLimit: .singleLine,
|
||||
characterLimit: 0
|
||||
)
|
||||
.questionRequired(true)
|
||||
}
|
||||
)
|
||||
},
|
||||
onResearchFormCompletion: { completion in
|
||||
switch completion {
|
||||
case .completed(let results):
|
||||
save(results)
|
||||
case .discarded:
|
||||
cancel()
|
||||
default:
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
#### Install as an embedded framework
|
||||
|
||||
Download the project source code and drag in the *ResearchKitSwiftUI* folder. In the dialog that pops up, choose to copy files to destination and create folders. Then hit the finish button. Finally, embed the *ResearchKitSwiftUI* framework in your app by adding it to the "Frameworks, Libraries, and Embedded Content" section for your target.
|
||||
|
||||
# Consent <a name="consent"></a>
|
||||
|
||||
The *ResearchKit framework* provides classes that you can customize to explain the
|
||||
|
||||
@@ -7,4 +7,7 @@
|
||||
<FileRef
|
||||
location = "group:samples/ORKCatalog/ORKCatalog.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:ResearchKitSwiftUI/ResearchKitSwiftUI.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 54;
|
||||
objectVersion = 60;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXAggregateTarget section */
|
||||
@@ -24,19 +24,19 @@
|
||||
/* End PBXAggregateTarget section */
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
00B1F7852241503900D022FE /* Speech.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 00B1F7842241503900D022FE /* Speech.framework */; };
|
||||
00C2668E23022CD400337E0B /* ORKCustomStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 00C2668C23022CD400337E0B /* ORKCustomStep.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
00C2668F23022CD400337E0B /* ORKCustomStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 00C2668D23022CD400337E0B /* ORKCustomStep.m */; };
|
||||
00B1F7852241503900D022FE /* Speech.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 00B1F7842241503900D022FE /* Speech.framework */; platformFilter = ios; };
|
||||
00C2668E23022CD400337E0B /* ORKCustomStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 00C2668C23022CD400337E0B /* ORKCustomStep.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
00C2668F23022CD400337E0B /* ORKCustomStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 00C2668D23022CD400337E0B /* ORKCustomStep.m */; platformFilter = ios; };
|
||||
03057F492518ECDC00C4EC5B /* ORKAudioStepViewControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 03057F482518ECDC00C4EC5B /* ORKAudioStepViewControllerTests.m */; };
|
||||
031A0FC124CF4ECD000E4455 /* ORKSensorPermissionType.h in Headers */ = {isa = PBXBuildFile; fileRef = 031A0FBF24CF4ECD000E4455 /* ORKSensorPermissionType.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
031A0FC224CF4ECD000E4455 /* ORKSensorPermissionType.m in Sources */ = {isa = PBXBuildFile; fileRef = 031A0FC024CF4ECD000E4455 /* ORKSensorPermissionType.m */; };
|
||||
031A0FC124CF4ECD000E4455 /* ORKSensorPermissionType.h in Headers */ = {isa = PBXBuildFile; fileRef = 031A0FBF24CF4ECD000E4455 /* ORKSensorPermissionType.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
031A0FC224CF4ECD000E4455 /* ORKSensorPermissionType.m in Sources */ = {isa = PBXBuildFile; fileRef = 031A0FC024CF4ECD000E4455 /* ORKSensorPermissionType.m */; platformFilter = ios; };
|
||||
0324C1D825439E1800BBE77B /* ORKVideoInstructionStepViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0324C1D725439E1800BBE77B /* ORKVideoInstructionStepViewControllerTests.swift */; };
|
||||
036B1E8D25351BAD008483DF /* ORKMotionActivityPermissionType.h in Headers */ = {isa = PBXBuildFile; fileRef = 036B1E8B25351BAD008483DF /* ORKMotionActivityPermissionType.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
036B1E8E25351BAD008483DF /* ORKMotionActivityPermissionType.m in Sources */ = {isa = PBXBuildFile; fileRef = 036B1E8C25351BAD008483DF /* ORKMotionActivityPermissionType.m */; };
|
||||
03BD9EA3253E62A0008ADBE1 /* ORKBundleAsset.h in Headers */ = {isa = PBXBuildFile; fileRef = 03BD9EA1253E62A0008ADBE1 /* ORKBundleAsset.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
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 */; };
|
||||
036B1E8D25351BAD008483DF /* ORKMotionActivityPermissionType.h in Headers */ = {isa = PBXBuildFile; fileRef = 036B1E8B25351BAD008483DF /* ORKMotionActivityPermissionType.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
036B1E8E25351BAD008483DF /* ORKMotionActivityPermissionType.m in Sources */ = {isa = PBXBuildFile; fileRef = 036B1E8C25351BAD008483DF /* ORKMotionActivityPermissionType.m */; platformFilter = ios; };
|
||||
03BD9EA3253E62A0008ADBE1 /* ORKBundleAsset.h in Headers */ = {isa = PBXBuildFile; fileRef = 03BD9EA1253E62A0008ADBE1 /* ORKBundleAsset.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
03BD9EA4253E62A0008ADBE1 /* ORKBundleAsset.m in Sources */ = {isa = PBXBuildFile; fileRef = 03BD9EA2253E62A0008ADBE1 /* ORKBundleAsset.m */; platformFilter = ios; };
|
||||
03EDD58024CA6B1D006245E9 /* ORKNotificationPermissionType.h in Headers */ = {isa = PBXBuildFile; fileRef = 03EDD57E24CA6B1D006245E9 /* ORKNotificationPermissionType.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
03EDD58124CA6B1D006245E9 /* ORKNotificationPermissionType.m in Sources */ = {isa = PBXBuildFile; fileRef = 03EDD57F24CA6B1D006245E9 /* ORKNotificationPermissionType.m */; platformFilter = ios; };
|
||||
0B0852742BD872C400149963 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 0B0852732BD872C400149963 /* PrivacyInfo.xcprivacy */; };
|
||||
0B0852762BD872D800149963 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 0B0852752BD872D800149963 /* PrivacyInfo.xcprivacy */; };
|
||||
0B0852782BD872EA00149963 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 0B0852772BD872EA00149963 /* PrivacyInfo.xcprivacy */; };
|
||||
@@ -50,11 +50,11 @@
|
||||
0BD9B6112B75992800A64EF9 /* Sentence5.wav in Resources */ = {isa = PBXBuildFile; fileRef = 7118AC6320BF6A3A00D7A6BB /* Sentence5.wav */; };
|
||||
0BD9B6122B75992C00A64EF9 /* Sentence6.wav in Resources */ = {isa = PBXBuildFile; fileRef = 7118AC6220BF6A3A00D7A6BB /* Sentence6.wav */; };
|
||||
0BD9B6132B75992F00A64EF9 /* Sentence7.wav in Resources */ = {isa = PBXBuildFile; fileRef = 7118AC6020BF6A3900D7A6BB /* Sentence7.wav */; };
|
||||
0BE9D5272947EA4900DA0625 /* ORKConsentDocument+ORKInstructionStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BE9D5252947EA4900DA0625 /* ORKConsentDocument+ORKInstructionStep.m */; };
|
||||
0BE9D5272947EA4900DA0625 /* ORKConsentDocument+ORKInstructionStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BE9D5252947EA4900DA0625 /* ORKConsentDocument+ORKInstructionStep.m */; platformFilter = ios; };
|
||||
0BFD27562B8D1D3B00B540E8 /* ORKJSONSerializationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 51AF19562B583BBA00D3B399 /* ORKJSONSerializationTests.m */; };
|
||||
10FF9ADB1B7BA78400ECB5B4 /* ORKOrderedTask_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 10FF9AD91B7BA78400ECB5B4 /* ORKOrderedTask_Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
12F339BF26A1F09A000665E4 /* ORKLocationPermissionType.m in Sources */ = {isa = PBXBuildFile; fileRef = 12F339BD26A1F09A000665E4 /* ORKLocationPermissionType.m */; };
|
||||
12F339C026A1F09A000665E4 /* ORKLocationPermissionType.h in Headers */ = {isa = PBXBuildFile; fileRef = 12F339BE26A1F09A000665E4 /* ORKLocationPermissionType.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
10FF9ADB1B7BA78400ECB5B4 /* ORKOrderedTask_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 10FF9AD91B7BA78400ECB5B4 /* ORKOrderedTask_Private.h */; platformFilter = ios; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
12F339BF26A1F09A000665E4 /* ORKLocationPermissionType.m in Sources */ = {isa = PBXBuildFile; fileRef = 12F339BD26A1F09A000665E4 /* ORKLocationPermissionType.m */; platformFilter = ios; };
|
||||
12F339C026A1F09A000665E4 /* ORKLocationPermissionType.h in Headers */ = {isa = PBXBuildFile; fileRef = 12F339BE26A1F09A000665E4 /* ORKLocationPermissionType.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
1483DBB5220125BE004C26B6 /* ORKActiveStepTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1483DBB4220125BE004C26B6 /* ORKActiveStepTests.swift */; };
|
||||
148E58BD227B36DB00EEF915 /* ORKCompletionStepViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 148E58BC227B36DB00EEF915 /* ORKCompletionStepViewControllerTests.swift */; };
|
||||
148E58C2227B753F00EEF915 /* ORKContinueButtonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 148E58C1227B753F00EEF915 /* ORKContinueButtonTests.swift */; };
|
||||
@@ -73,26 +73,26 @@
|
||||
14F7AC8B2269035200D52F41 /* ORKStepViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14F7AC8A2269035200D52F41 /* ORKStepViewControllerTests.swift */; };
|
||||
22ED1847285290250052406B /* ORKAudiometryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 22ED1845285290250052406B /* ORKAudiometryTests.m */; };
|
||||
22ED1848285290250052406B /* ORKAudiometryTestData.plist in Resources */ = {isa = PBXBuildFile; fileRef = 22ED1846285290250052406B /* ORKAudiometryTestData.plist */; };
|
||||
2429D5721BBB5397003A512F /* ORKRegistrationStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 2429D5701BBB5397003A512F /* ORKRegistrationStep.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
2429D5731BBB5397003A512F /* ORKRegistrationStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 2429D5711BBB5397003A512F /* ORKRegistrationStep.m */; };
|
||||
242C9E051BBDFDAC0088B7F4 /* ORKVerificationStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 242C9E031BBDFDAC0088B7F4 /* ORKVerificationStep.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
242C9E061BBDFDAC0088B7F4 /* ORKVerificationStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 242C9E041BBDFDAC0088B7F4 /* ORKVerificationStep.m */; };
|
||||
2433C9E31B9A506F0052D375 /* ORKKeychainWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = 2433C9E11B9A506F0052D375 /* ORKKeychainWrapper.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
2429D5721BBB5397003A512F /* ORKRegistrationStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 2429D5701BBB5397003A512F /* ORKRegistrationStep.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
2429D5731BBB5397003A512F /* ORKRegistrationStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 2429D5711BBB5397003A512F /* ORKRegistrationStep.m */; platformFilter = ios; };
|
||||
242C9E051BBDFDAC0088B7F4 /* ORKVerificationStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 242C9E031BBDFDAC0088B7F4 /* ORKVerificationStep.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
242C9E061BBDFDAC0088B7F4 /* ORKVerificationStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 242C9E041BBDFDAC0088B7F4 /* ORKVerificationStep.m */; platformFilter = ios; };
|
||||
2433C9E31B9A506F0052D375 /* ORKKeychainWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = 2433C9E11B9A506F0052D375 /* ORKKeychainWrapper.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
2433C9E41B9A506F0052D375 /* ORKKeychainWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 2433C9E21B9A506F0052D375 /* ORKKeychainWrapper.m */; };
|
||||
244EFAD21BCEFD83001850D9 /* ORKAnswerFormat_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 244EFAD11BCEFD83001850D9 /* ORKAnswerFormat_Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
248604061B4C98760010C8A0 /* ORKAnswerFormatTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 248604051B4C98760010C8A0 /* ORKAnswerFormatTests.m */; };
|
||||
2489F7B11D65214D008DEF20 /* ORKVideoCaptureStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 2489F7A91D65214D008DEF20 /* ORKVideoCaptureStep.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
2489F7B21D65214D008DEF20 /* ORKVideoCaptureStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 2489F7AA1D65214D008DEF20 /* ORKVideoCaptureStep.m */; };
|
||||
24A4DA141B8D1115009C797A /* ORKPasscodeStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 24A4DA121B8D1115009C797A /* ORKPasscodeStep.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
24A4DA151B8D1115009C797A /* ORKPasscodeStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 24A4DA131B8D1115009C797A /* ORKPasscodeStep.m */; };
|
||||
24BC5CEE1BC345D900846B43 /* ORKLoginStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 24BC5CEC1BC345D900846B43 /* ORKLoginStep.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
24BC5CEF1BC345D900846B43 /* ORKLoginStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 24BC5CED1BC345D900846B43 /* ORKLoginStep.m */; };
|
||||
24C296751BD052F800B42EF1 /* ORKVerificationStep_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 24C296741BD052F800B42EF1 /* ORKVerificationStep_Internal.h */; };
|
||||
24C296771BD055B800B42EF1 /* ORKLoginStep_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 24C296761BD055B800B42EF1 /* ORKLoginStep_Internal.h */; };
|
||||
2489F7B11D65214D008DEF20 /* ORKVideoCaptureStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 2489F7A91D65214D008DEF20 /* ORKVideoCaptureStep.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
2489F7B21D65214D008DEF20 /* ORKVideoCaptureStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 2489F7AA1D65214D008DEF20 /* ORKVideoCaptureStep.m */; platformFilter = ios; };
|
||||
24A4DA141B8D1115009C797A /* ORKPasscodeStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 24A4DA121B8D1115009C797A /* ORKPasscodeStep.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
24A4DA151B8D1115009C797A /* ORKPasscodeStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 24A4DA131B8D1115009C797A /* ORKPasscodeStep.m */; platformFilter = ios; };
|
||||
24BC5CEE1BC345D900846B43 /* ORKLoginStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 24BC5CEC1BC345D900846B43 /* ORKLoginStep.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
24BC5CEF1BC345D900846B43 /* ORKLoginStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 24BC5CED1BC345D900846B43 /* ORKLoginStep.m */; platformFilter = ios; };
|
||||
24C296751BD052F800B42EF1 /* ORKVerificationStep_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 24C296741BD052F800B42EF1 /* ORKVerificationStep_Internal.h */; platformFilter = ios; };
|
||||
24C296771BD055B800B42EF1 /* ORKLoginStep_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 24C296761BD055B800B42EF1 /* ORKLoginStep_Internal.h */; platformFilter = ios; };
|
||||
2EBFE11D1AE1B32D00CB8254 /* ORKUIViewAccessibilityTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2EBFE11C1AE1B32D00CB8254 /* ORKUIViewAccessibilityTests.m */; };
|
||||
2EBFE1201AE1B74100CB8254 /* ORKVoiceEngineTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2EBFE11F1AE1B74100CB8254 /* ORKVoiceEngineTests.m */; };
|
||||
511987C3246330CA004FC2C7 /* ORKRequestPermissionsStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 511987C1246330CA004FC2C7 /* ORKRequestPermissionsStep.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
511987C4246330CA004FC2C7 /* ORKRequestPermissionsStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 511987C2246330CA004FC2C7 /* ORKRequestPermissionsStep.m */; };
|
||||
511987C3246330CA004FC2C7 /* ORKRequestPermissionsStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 511987C1246330CA004FC2C7 /* ORKRequestPermissionsStep.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
511987C4246330CA004FC2C7 /* ORKRequestPermissionsStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 511987C2246330CA004FC2C7 /* ORKRequestPermissionsStep.m */; platformFilter = ios; };
|
||||
511BB024298DCCC200936EC0 /* ORKSpeechRecognitionStepViewController_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 511BB022298DCCC200936EC0 /* ORKSpeechRecognitionStepViewController_Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
511E8D622995C20E00A384A5 /* ORKEnvironmentSPLMeterStepViewController_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 511E8D602995C20E00A384A5 /* ORKEnvironmentSPLMeterStepViewController_Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
512741272B1557220045A449 /* ResearchKit.docc in Sources */ = {isa = PBXBuildFile; fileRef = 512741262B1557220045A449 /* ResearchKit.docc */; };
|
||||
@@ -180,20 +180,20 @@
|
||||
5192BEED2AE043D3006E43FB /* ORKTimedWalkStepViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 5192BEE92AE043D3006E43FB /* ORKTimedWalkStepViewController.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
5192BEEE2AE043D3006E43FB /* ORKTimedWalkContentView.h in Headers */ = {isa = PBXBuildFile; fileRef = 5192BEEA2AE043D3006E43FB /* ORKTimedWalkContentView.h */; };
|
||||
5192BEEF2AE043D3006E43FB /* ORKTimedWalkStepViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5192BEEB2AE043D3006E43FB /* ORKTimedWalkStepViewController.m */; };
|
||||
5192BF502AE09673006E43FB /* ORKPredicateFormItemVisibilityRule.h in Headers */ = {isa = PBXBuildFile; fileRef = 5192BF4D2AE09672006E43FB /* ORKPredicateFormItemVisibilityRule.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
5192BF512AE09673006E43FB /* ORKPredicateFormItemVisibilityRule.m in Sources */ = {isa = PBXBuildFile; fileRef = 5192BF4E2AE09672006E43FB /* ORKPredicateFormItemVisibilityRule.m */; };
|
||||
5192BF522AE09673006E43FB /* ORKPredicateFormItemVisibilityRule_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 5192BF4F2AE09672006E43FB /* ORKPredicateFormItemVisibilityRule_Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
5192BF502AE09673006E43FB /* ORKPredicateFormItemVisibilityRule.h in Headers */ = {isa = PBXBuildFile; fileRef = 5192BF4D2AE09672006E43FB /* ORKPredicateFormItemVisibilityRule.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
5192BF512AE09673006E43FB /* ORKPredicateFormItemVisibilityRule.m in Sources */ = {isa = PBXBuildFile; fileRef = 5192BF4E2AE09672006E43FB /* ORKPredicateFormItemVisibilityRule.m */; platformFilter = ios; };
|
||||
5192BF522AE09673006E43FB /* ORKPredicateFormItemVisibilityRule_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 5192BF4F2AE09672006E43FB /* ORKPredicateFormItemVisibilityRule_Private.h */; platformFilter = ios; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
5192BF592AE09794006E43FB /* ORKFormItemVisibilityRule.h in Headers */ = {isa = PBXBuildFile; fileRef = 5192BF572AE09793006E43FB /* ORKFormItemVisibilityRule.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
5192BF5A2AE09794006E43FB /* ORKFormItemVisibilityRule.m in Sources */ = {isa = PBXBuildFile; fileRef = 5192BF582AE09794006E43FB /* ORKFormItemVisibilityRule.m */; };
|
||||
5192BF5D2AE19036006E43FB /* frequency_dBSPL_AIRPODSPROV2.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5192BF5C2AE19036006E43FB /* frequency_dBSPL_AIRPODSPROV2.plist */; };
|
||||
5192BF7D2AE1A87D006E43FB /* retspl_AIRPODSPROV2.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5192BF7C2AE1A87D006E43FB /* retspl_AIRPODSPROV2.plist */; };
|
||||
5192BF7F2AE1A9BA006E43FB /* volume_curve_AIRPODSPROV2.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5192BF7E2AE1A9BA006E43FB /* volume_curve_AIRPODSPROV2.plist */; };
|
||||
5192BF842AE1BA47006E43FB /* ORKFrontFacingCameraStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 5192BF802AE1BA47006E43FB /* ORKFrontFacingCameraStep.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
5192BF852AE1BA47006E43FB /* ORKFrontFacingCameraStepResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 5192BF812AE1BA47006E43FB /* ORKFrontFacingCameraStepResult.m */; };
|
||||
5192BF862AE1BA47006E43FB /* ORKFrontFacingCameraStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 5192BF822AE1BA47006E43FB /* ORKFrontFacingCameraStep.m */; };
|
||||
5192BF872AE1BA47006E43FB /* ORKFrontFacingCameraStepResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 5192BF832AE1BA47006E43FB /* ORKFrontFacingCameraStepResult.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
5192BF8A2AE1BBB0006E43FB /* ORKSecondaryTaskStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 5192BF882AE1BBB0006E43FB /* ORKSecondaryTaskStep.m */; };
|
||||
5192BF8B2AE1BBB0006E43FB /* ORKSecondaryTaskStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 5192BF892AE1BBB0006E43FB /* ORKSecondaryTaskStep.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
5192BF842AE1BA47006E43FB /* ORKFrontFacingCameraStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 5192BF802AE1BA47006E43FB /* ORKFrontFacingCameraStep.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
5192BF852AE1BA47006E43FB /* ORKFrontFacingCameraStepResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 5192BF812AE1BA47006E43FB /* ORKFrontFacingCameraStepResult.m */; platformFilter = ios; };
|
||||
5192BF862AE1BA47006E43FB /* ORKFrontFacingCameraStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 5192BF822AE1BA47006E43FB /* ORKFrontFacingCameraStep.m */; platformFilter = ios; };
|
||||
5192BF872AE1BA47006E43FB /* ORKFrontFacingCameraStepResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 5192BF832AE1BA47006E43FB /* ORKFrontFacingCameraStepResult.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
5192BF8A2AE1BBB0006E43FB /* ORKSecondaryTaskStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 5192BF882AE1BBB0006E43FB /* ORKSecondaryTaskStep.m */; platformFilter = ios; };
|
||||
5192BF8B2AE1BBB0006E43FB /* ORKSecondaryTaskStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 5192BF892AE1BBB0006E43FB /* ORKSecondaryTaskStep.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
5192BF8F2AE1C051006E43FB /* ORKAgePicker.h in Headers */ = {isa = PBXBuildFile; fileRef = 5192BF8D2AE1C051006E43FB /* ORKAgePicker.h */; };
|
||||
5192BF902AE1C051006E43FB /* ORKAgePicker.m in Sources */ = {isa = PBXBuildFile; fileRef = 5192BF8E2AE1C051006E43FB /* ORKAgePicker.m */; };
|
||||
5192BF932AE1C2F8006E43FB /* ORKChoiceViewCell+ORKColorChoice.h in Headers */ = {isa = PBXBuildFile; fileRef = 5192BF912AE1C2F7006E43FB /* ORKChoiceViewCell+ORKColorChoice.h */; };
|
||||
@@ -204,19 +204,19 @@
|
||||
5192BF9A2AE1D8A4006E43FB /* ORKChoiceViewCell+ORKTextChoice.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B8444642A79C25000292DEA /* ORKChoiceViewCell+ORKTextChoice.m */; };
|
||||
5192BF9D2AE1DE62006E43FB /* ORKColorChoiceCellGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 5192BF9B2AE1DE62006E43FB /* ORKColorChoiceCellGroup.m */; };
|
||||
5192BF9E2AE1DE62006E43FB /* ORKColorChoiceCellGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = 5192BF9C2AE1DE62006E43FB /* ORKColorChoiceCellGroup.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
5192BFA12AEAD7B5006E43FB /* Artwork.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 861610BF1A8D8EDD00245F7A /* Artwork.xcassets */; };
|
||||
519CE8222C6582BE003BB584 /* ORKHealthCondition.m in Sources */ = {isa = PBXBuildFile; fileRef = 519CE8162C6582BD003BB584 /* ORKHealthCondition.m */; };
|
||||
519CE8232C6582BE003BB584 /* ORKFamilyHistoryStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 519CE8172C6582BD003BB584 /* ORKFamilyHistoryStep.m */; };
|
||||
519CE8242C6582BE003BB584 /* ORKHealthCondition.h in Headers */ = {isa = PBXBuildFile; fileRef = 519CE8182C6582BD003BB584 /* ORKHealthCondition.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
519CE8252C6582BE003BB584 /* ORKFamilyHistoryResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 519CE8192C6582BD003BB584 /* ORKFamilyHistoryResult.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
519CE8262C6582BE003BB584 /* ORKRelatedPerson.m in Sources */ = {isa = PBXBuildFile; fileRef = 519CE81A2C6582BD003BB584 /* ORKRelatedPerson.m */; };
|
||||
519CE8272C6582BE003BB584 /* ORKRelativeGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = 519CE81B2C6582BD003BB584 /* ORKRelativeGroup.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
519CE8282C6582BE003BB584 /* ORKRelativeGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 519CE81C2C6582BD003BB584 /* ORKRelativeGroup.m */; };
|
||||
519CE8292C6582BE003BB584 /* ORKConditionStepConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 519CE81D2C6582BE003BB584 /* ORKConditionStepConfiguration.m */; };
|
||||
519CE82A2C6582BE003BB584 /* ORKConditionStepConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 519CE81E2C6582BE003BB584 /* ORKConditionStepConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
519CE82B2C6582BE003BB584 /* ORKFamilyHistoryStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 519CE81F2C6582BE003BB584 /* ORKFamilyHistoryStep.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
519CE82C2C6582BE003BB584 /* ORKRelatedPerson.h in Headers */ = {isa = PBXBuildFile; fileRef = 519CE8202C6582BE003BB584 /* ORKRelatedPerson.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
519CE82D2C6582BE003BB584 /* ORKFamilyHistoryResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 519CE8212C6582BE003BB584 /* ORKFamilyHistoryResult.m */; };
|
||||
5192BFA12AEAD7B5006E43FB /* Artwork.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 861610BF1A8D8EDD00245F7A /* Artwork.xcassets */; platformFilter = ios; };
|
||||
519CE8222C6582BE003BB584 /* ORKHealthCondition.m in Sources */ = {isa = PBXBuildFile; fileRef = 519CE8162C6582BD003BB584 /* ORKHealthCondition.m */; platformFilter = ios; };
|
||||
519CE8232C6582BE003BB584 /* ORKFamilyHistoryStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 519CE8172C6582BD003BB584 /* ORKFamilyHistoryStep.m */; platformFilter = ios; };
|
||||
519CE8242C6582BE003BB584 /* ORKHealthCondition.h in Headers */ = {isa = PBXBuildFile; fileRef = 519CE8182C6582BD003BB584 /* ORKHealthCondition.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
519CE8252C6582BE003BB584 /* ORKFamilyHistoryResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 519CE8192C6582BD003BB584 /* ORKFamilyHistoryResult.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
519CE8262C6582BE003BB584 /* ORKRelatedPerson.m in Sources */ = {isa = PBXBuildFile; fileRef = 519CE81A2C6582BD003BB584 /* ORKRelatedPerson.m */; platformFilter = ios; };
|
||||
519CE8272C6582BE003BB584 /* ORKRelativeGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = 519CE81B2C6582BD003BB584 /* ORKRelativeGroup.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
519CE8282C6582BE003BB584 /* ORKRelativeGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 519CE81C2C6582BD003BB584 /* ORKRelativeGroup.m */; platformFilter = ios; };
|
||||
519CE8292C6582BE003BB584 /* ORKConditionStepConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 519CE81D2C6582BE003BB584 /* ORKConditionStepConfiguration.m */; platformFilter = ios; };
|
||||
519CE82A2C6582BE003BB584 /* ORKConditionStepConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 519CE81E2C6582BE003BB584 /* ORKConditionStepConfiguration.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
519CE82B2C6582BE003BB584 /* ORKFamilyHistoryStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 519CE81F2C6582BE003BB584 /* ORKFamilyHistoryStep.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
519CE82C2C6582BE003BB584 /* ORKRelatedPerson.h in Headers */ = {isa = PBXBuildFile; fileRef = 519CE8202C6582BE003BB584 /* ORKRelatedPerson.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
519CE82D2C6582BE003BB584 /* ORKFamilyHistoryResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 519CE8212C6582BE003BB584 /* ORKFamilyHistoryResult.m */; platformFilter = ios; };
|
||||
519CE8352C658617003BB584 /* ORKFamilyHistoryStepViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 519CE8302C658617003BB584 /* ORKFamilyHistoryStepViewController.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
519CE8382C658617003BB584 /* ORKFamilyHistoryStepViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 519CE8332C658617003BB584 /* ORKFamilyHistoryStepViewController.m */; };
|
||||
519CE8412C658654003BB584 /* ORKFamilyHistoryRelatedPersonCell.h in Headers */ = {isa = PBXBuildFile; fileRef = 519CE83B2C658653003BB584 /* ORKFamilyHistoryRelatedPersonCell.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
@@ -227,13 +227,12 @@
|
||||
519CE8462C658654003BB584 /* ORKFamilyHistoryTableHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 519CE8402C658654003BB584 /* ORKFamilyHistoryTableHeaderView.m */; };
|
||||
519CE85C2C6AD858003BB584 /* ORKFamilyHistoryStepViewController_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 519CE85B2C6AD858003BB584 /* ORKFamilyHistoryStepViewController_Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
519CE85F2C6BC1DB003BB584 /* ORKFamilyHistoryResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519CE85E2C6BC1DB003BB584 /* ORKFamilyHistoryResultTests.swift */; };
|
||||
51A11F172BD08D5E0060C07E /* HKSample+ORKJSONDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 51A11F132BD08D5D0060C07E /* HKSample+ORKJSONDictionary.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
51A11F182BD08D5E0060C07E /* CMMotionActivity+ORKJSONDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = 51A11F142BD08D5D0060C07E /* CMMotionActivity+ORKJSONDictionary.m */; };
|
||||
51A11F192BD08D5E0060C07E /* HKSample+ORKJSONDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = 51A11F152BD08D5D0060C07E /* HKSample+ORKJSONDictionary.m */; };
|
||||
51A11F1A2BD08D5E0060C07E /* CMMotionActivity+ORKJSONDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 51A11F162BD08D5E0060C07E /* CMMotionActivity+ORKJSONDictionary.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
51A11F172BD08D5E0060C07E /* HKSample+ORKJSONDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 51A11F132BD08D5D0060C07E /* HKSample+ORKJSONDictionary.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
51A11F182BD08D5E0060C07E /* CMMotionActivity+ORKJSONDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = 51A11F142BD08D5D0060C07E /* CMMotionActivity+ORKJSONDictionary.m */; platformFilter = ios; };
|
||||
51A11F192BD08D5E0060C07E /* HKSample+ORKJSONDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = 51A11F152BD08D5D0060C07E /* HKSample+ORKJSONDictionary.m */; platformFilter = ios; };
|
||||
51A11F1A2BD08D5E0060C07E /* CMMotionActivity+ORKJSONDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 51A11F162BD08D5E0060C07E /* CMMotionActivity+ORKJSONDictionary.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
51A11F222BD152660060C07E /* ORKActiveStepCustomView.h in Headers */ = {isa = PBXBuildFile; fileRef = 51A11F202BD152660060C07E /* ORKActiveStepCustomView.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
51A11F232BD152660060C07E /* ORKActiveStepCustomView.m in Sources */ = {isa = PBXBuildFile; fileRef = 51A11F212BD152660060C07E /* ORKActiveStepCustomView.m */; };
|
||||
51A11F242BD1548C0060C07E /* UIImageView+ResearchKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B9CC5662A68C02C00080E29 /* UIImageView+ResearchKit.m */; };
|
||||
51AEAAB22B744AC200F4D107 /* ORKQuestionStepViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 51AEAAA82B743FA500F4D107 /* ORKQuestionStepViewController.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
51AEAAB32B744ACC00F4D107 /* ORKQuestionStepViewController_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 51AEAAA72B743FA500F4D107 /* ORKQuestionStepViewController_Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
51AEAAB42B744B5700F4D107 /* ORKQuestionStepViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 51AEAAA62B743FA500F4D107 /* ORKQuestionStepViewController.m */; };
|
||||
@@ -242,15 +241,15 @@
|
||||
51AF19582B583BBA00D3B399 /* ORKDataCollectionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 51AF19532B583BB900D3B399 /* ORKDataCollectionTests.m */; };
|
||||
51AF19592B583BBA00D3B399 /* samples.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 51AF19542B583BB900D3B399 /* samples.bundle */; };
|
||||
51AF19662B583D0F00D3B399 /* ORKESerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = 51AF19552B583BB900D3B399 /* ORKESerialization.m */; };
|
||||
51AF1B152B67F30500D3B399 /* ORKSignatureFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = 51AF1B132B67F30500D3B399 /* ORKSignatureFormatter.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
51AF1B162B67F30500D3B399 /* ORKSignatureFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 51AF1B142B67F30500D3B399 /* ORKSignatureFormatter.m */; };
|
||||
51AF1B202B683C3400D3B399 /* ORKWebViewStepResult_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 51AF1B1F2B683C3400D3B399 /* ORKWebViewStepResult_Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
51AF1B152B67F30500D3B399 /* ORKSignatureFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = 51AF1B132B67F30500D3B399 /* ORKSignatureFormatter.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
51AF1B162B67F30500D3B399 /* ORKSignatureFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 51AF1B142B67F30500D3B399 /* ORKSignatureFormatter.m */; platformFilter = ios; };
|
||||
51AF1B202B683C3400D3B399 /* ORKWebViewStepResult_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 51AF1B1F2B683C3400D3B399 /* ORKWebViewStepResult_Private.h */; platformFilter = ios; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
51AF1B232B69803A00D3B399 /* Window.wav in Resources */ = {isa = PBXBuildFile; fileRef = 51AF1B212B69803A00D3B399 /* Window.wav */; };
|
||||
51AF1B242B69803A00D3B399 /* Noise.wav in Resources */ = {isa = PBXBuildFile; fileRef = 51AF1B222B69803A00D3B399 /* Noise.wav */; };
|
||||
51B76DFB2CB5B5A30061698A /* ResearchKitActiveTask.docc in Sources */ = {isa = PBXBuildFile; fileRef = 51B76DFA2CB5B5A30061698A /* ResearchKitActiveTask.docc */; };
|
||||
51B76DFD2CB5C73C0061698A /* ResearchKitUI.docc in Sources */ = {isa = PBXBuildFile; fileRef = 51B76DFC2CB5C73C0061698A /* ResearchKitUI.docc */; };
|
||||
51B94DBD2B311E810039B0E7 /* CLLocationManager+ResearchKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 51B94DBC2B311E810039B0E7 /* CLLocationManager+ResearchKit.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
51B94DC72B3254FE0039B0E7 /* CLLocationManager+ResearchKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 51B94DC62B3254FD0039B0E7 /* CLLocationManager+ResearchKit.m */; };
|
||||
51B94DBD2B311E810039B0E7 /* CLLocationManager+ResearchKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 51B94DBC2B311E810039B0E7 /* CLLocationManager+ResearchKit.h */; platformFilter = ios; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
51B94DC72B3254FE0039B0E7 /* CLLocationManager+ResearchKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 51B94DC62B3254FD0039B0E7 /* CLLocationManager+ResearchKit.m */; platformFilter = ios; };
|
||||
51BD8FC929D60FB60001D54E /* frequency_dBSPL_AIRPODSMAX.plist in Resources */ = {isa = PBXBuildFile; fileRef = E293655D25757CC700092A7C /* frequency_dBSPL_AIRPODSMAX.plist */; };
|
||||
51BD8FCA29D60FB60001D54E /* frequency_dBSPL_EARPODS.plist in Resources */ = {isa = PBXBuildFile; fileRef = 713D4B1B20FE5464002BE28D /* frequency_dBSPL_EARPODS.plist */; };
|
||||
51BD8FCB29D60FB60001D54E /* frequency_dBSPL_AIRPODS.plist in Resources */ = {isa = PBXBuildFile; fileRef = 71B7B4D820AA91D400C5768A /* frequency_dBSPL_AIRPODS.plist */; };
|
||||
@@ -269,12 +268,12 @@
|
||||
51CB80D62AFEBE7E00A1F410 /* ORKFormStepViewControllerConditionalFormItemsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51CB80D52AFEBE7E00A1F410 /* ORKFormStepViewControllerConditionalFormItemsTests.swift */; };
|
||||
51CB80D82AFEBEC600A1F410 /* ORKPredicateFormItemVisibilityRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51CB80D72AFEBEC600A1F410 /* ORKPredicateFormItemVisibilityRuleTests.swift */; };
|
||||
51CB80DA2AFEBF3800A1F410 /* ORKFormItemVisibilityRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51CB80D92AFEBF3800A1F410 /* ORKFormItemVisibilityRuleTests.swift */; };
|
||||
51E03D6324919711008F8406 /* ORKPermissionType.h in Headers */ = {isa = PBXBuildFile; fileRef = 51E03D6124919711008F8406 /* ORKPermissionType.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
51E03D6424919711008F8406 /* ORKPermissionType.m in Sources */ = {isa = PBXBuildFile; fileRef = 51E03D6224919711008F8406 /* ORKPermissionType.m */; };
|
||||
51E03D682491A601008F8406 /* ORKHealthKitPermissionType.h in Headers */ = {isa = PBXBuildFile; fileRef = 51E03D662491A601008F8406 /* ORKHealthKitPermissionType.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
51E03D692491A601008F8406 /* ORKHealthKitPermissionType.m in Sources */ = {isa = PBXBuildFile; fileRef = 51E03D672491A601008F8406 /* ORKHealthKitPermissionType.m */; };
|
||||
51EB9A532B8408D50064A515 /* ORKInstructionStepHTMLFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = 51EB9A512B8408D50064A515 /* ORKInstructionStepHTMLFormatter.h */; };
|
||||
51EB9A542B8408D50064A515 /* ORKInstructionStepHTMLFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 51EB9A522B8408D50064A515 /* ORKInstructionStepHTMLFormatter.m */; };
|
||||
51E03D6324919711008F8406 /* ORKPermissionType.h in Headers */ = {isa = PBXBuildFile; fileRef = 51E03D6124919711008F8406 /* ORKPermissionType.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
51E03D6424919711008F8406 /* ORKPermissionType.m in Sources */ = {isa = PBXBuildFile; fileRef = 51E03D6224919711008F8406 /* ORKPermissionType.m */; platformFilter = ios; };
|
||||
51E03D682491A601008F8406 /* ORKHealthKitPermissionType.h in Headers */ = {isa = PBXBuildFile; fileRef = 51E03D662491A601008F8406 /* ORKHealthKitPermissionType.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
51E03D692491A601008F8406 /* ORKHealthKitPermissionType.m in Sources */ = {isa = PBXBuildFile; fileRef = 51E03D672491A601008F8406 /* ORKHealthKitPermissionType.m */; platformFilter = ios; };
|
||||
51EB9A532B8408D50064A515 /* ORKInstructionStepHTMLFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = 51EB9A512B8408D50064A515 /* ORKInstructionStepHTMLFormatter.h */; platformFilter = ios; };
|
||||
51EB9A542B8408D50064A515 /* ORKInstructionStepHTMLFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 51EB9A522B8408D50064A515 /* ORKInstructionStepHTMLFormatter.m */; platformFilter = ios; };
|
||||
51EB9A5E2B8D3BA70064A515 /* ORKInstructionStepHTMLFormatterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 51EB9A5D2B8D3BA70064A515 /* ORKInstructionStepHTMLFormatterTests.m */; };
|
||||
51F716C1297E288A00D8ACF7 /* ORKNormalizedReactionTimeStimulusView.m in Sources */ = {isa = PBXBuildFile; fileRef = 51F716B7297E288900D8ACF7 /* ORKNormalizedReactionTimeStimulusView.m */; };
|
||||
51F716C2297E288A00D8ACF7 /* ORKNormalizedReactionTimeStimulusView.h in Headers */ = {isa = PBXBuildFile; fileRef = 51F716B8297E288900D8ACF7 /* ORKNormalizedReactionTimeStimulusView.h */; };
|
||||
@@ -286,7 +285,7 @@
|
||||
51F716C8297E288A00D8ACF7 /* ORKNormalizedReactionTimeStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 51F716BE297E288900D8ACF7 /* ORKNormalizedReactionTimeStep.m */; };
|
||||
51F716C9297E288A00D8ACF7 /* ORKNormalizedReactionTimeViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 51F716BF297E288A00D8ACF7 /* ORKNormalizedReactionTimeViewController.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
51F716CA297E288A00D8ACF7 /* ORKNormalizedReactionTimeContentView.h in Headers */ = {isa = PBXBuildFile; fileRef = 51F716C0297E288A00D8ACF7 /* ORKNormalizedReactionTimeContentView.h */; };
|
||||
51F716CB297E2A1100D8ACF7 /* ORKConsentDocument+ORKInstructionStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 0BE9D5242947EA4900DA0625 /* ORKConsentDocument+ORKInstructionStep.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
51F716CB297E2A1100D8ACF7 /* ORKConsentDocument+ORKInstructionStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 0BE9D5242947EA4900DA0625 /* ORKConsentDocument+ORKInstructionStep.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
51F716D1297E2CC400D8ACF7 /* ORKConsentLearnMoreViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 51F716CD297E2CB600D8ACF7 /* ORKConsentLearnMoreViewController.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
51F716D3297E2FB600D8ACF7 /* ORKConsentLearnMoreViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 51F716CE297E2CB600D8ACF7 /* ORKConsentLearnMoreViewController.m */; };
|
||||
51F716EB2981B49000D8ACF7 /* ORKSpeechInNoiseStepViewController_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 51F716E82981AF1200D8ACF7 /* ORKSpeechInNoiseStepViewController_Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
@@ -295,7 +294,7 @@
|
||||
5D04884C25EF4CC30006C68B /* ORKQuestionStep_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D04884B25EF4CC30006C68B /* ORKQuestionStep_Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
5D04885725F19A7A0006C68B /* ORKDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D04885525F19A7A0006C68B /* ORKDevice.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
5D04885825F19A7A0006C68B /* ORKDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D04885625F19A7A0006C68B /* ORKDevice.m */; };
|
||||
5D04885C25F1B3AB0006C68B /* ORKDevice_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D04885B25F1B3AB0006C68B /* ORKDevice_Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
5D04885C25F1B3AB0006C68B /* ORKDevice_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D04885B25F1B3AB0006C68B /* ORKDevice_Private.h */; platformFilter = ios; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
5D43C5D424255675006F4084 /* ORKBodyItem_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D43C5D324255675006F4084 /* ORKBodyItem_Internal.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
5DABE5AE24DA173F00570C57 /* ResearchKit_Prefix.pch in Headers */ = {isa = PBXBuildFile; fileRef = 5DABE5AD24DA16E600570C57 /* ResearchKit_Prefix.pch */; };
|
||||
5E6AB7DF2BC86900009ED0D5 /* ORKTaskViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E6AB7DE2BC86900009ED0D5 /* ORKTaskViewControllerTests.swift */; };
|
||||
@@ -304,26 +303,26 @@
|
||||
5EB91CC82BCE2EFD00BBF23E /* splMeter_sensitivity_offset.plist in Resources */ = {isa = PBXBuildFile; fileRef = 71F3B27F21001DEC00FB1C41 /* splMeter_sensitivity_offset.plist */; };
|
||||
5EB91CC92BCE2F1D00BBF23E /* SentencesList.txt in Resources */ = {isa = PBXBuildFile; fileRef = BA22F76C20C4F884006E6E11 /* SentencesList.txt */; };
|
||||
622774402C5CF7DC00F2E741 /* retspl_dBFS_AIRPODSPROV2.plist in Resources */ = {isa = PBXBuildFile; fileRef = 6227743F2C5CF7DC00F2E741 /* retspl_dBFS_AIRPODSPROV2.plist */; };
|
||||
714080DB235FD14700281E04 /* ResearchKit.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 714080D9235FD14700281E04 /* ResearchKit.stringsdict */; };
|
||||
714080DB235FD14700281E04 /* ResearchKit.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 714080D9235FD14700281E04 /* ResearchKit.stringsdict */; platformFilter = ios; };
|
||||
714151D0225C4A23002CA33B /* ORKPasscodeViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14A92C6922444F93007547F2 /* ORKPasscodeViewControllerTests.swift */; };
|
||||
7141EA2222EFBC0C00650145 /* ORKLoggingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7141EA2122EFBC0C00650145 /* ORKLoggingTests.m */; };
|
||||
7167D028231B1EAA00AAB4DD /* ORKFormStep_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 7167D027231B1EAA00AAB4DD /* ORKFormStep_Internal.h */; };
|
||||
8419D66E1FB73CC80088D7E5 /* ORKWebViewStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 8419D66C1FB73CC80088D7E5 /* ORKWebViewStep.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
8419D66F1FB73CC80088D7E5 /* ORKWebViewStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 8419D66D1FB73CC80088D7E5 /* ORKWebViewStep.m */; };
|
||||
7167D028231B1EAA00AAB4DD /* ORKFormStep_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 7167D027231B1EAA00AAB4DD /* ORKFormStep_Internal.h */; platformFilter = ios; };
|
||||
8419D66E1FB73CC80088D7E5 /* ORKWebViewStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 8419D66C1FB73CC80088D7E5 /* ORKWebViewStep.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
8419D66F1FB73CC80088D7E5 /* ORKWebViewStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 8419D66D1FB73CC80088D7E5 /* ORKWebViewStep.m */; platformFilter = ios; };
|
||||
861D11AD1AA7951F003C98A7 /* ORKChoiceAnswerFormatHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = 861D11AB1AA7951F003C98A7 /* ORKChoiceAnswerFormatHelper.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
861D11AE1AA7951F003C98A7 /* ORKChoiceAnswerFormatHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 861D11AC1AA7951F003C98A7 /* ORKChoiceAnswerFormatHelper.m */; };
|
||||
866DA51F1D63D04700C9AF3F /* ORKCollector_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 866DA5131D63D04700C9AF3F /* ORKCollector_Internal.h */; };
|
||||
866DA5201D63D04700C9AF3F /* ORKCollector.h in Headers */ = {isa = PBXBuildFile; fileRef = 866DA5141D63D04700C9AF3F /* ORKCollector.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
866DA5211D63D04700C9AF3F /* ORKCollector.m in Sources */ = {isa = PBXBuildFile; fileRef = 866DA5151D63D04700C9AF3F /* ORKCollector.m */; };
|
||||
866DA5221D63D04700C9AF3F /* ORKDataCollectionManager_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 866DA5161D63D04700C9AF3F /* ORKDataCollectionManager_Internal.h */; };
|
||||
866DA5231D63D04700C9AF3F /* ORKDataCollectionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 866DA5171D63D04700C9AF3F /* ORKDataCollectionManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
866DA5241D63D04700C9AF3F /* ORKDataCollectionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 866DA5181D63D04700C9AF3F /* ORKDataCollectionManager.m */; };
|
||||
866DA5251D63D04700C9AF3F /* ORKHealthSampleQueryOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 866DA5191D63D04700C9AF3F /* ORKHealthSampleQueryOperation.h */; };
|
||||
866DA5261D63D04700C9AF3F /* ORKHealthSampleQueryOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 866DA51A1D63D04700C9AF3F /* ORKHealthSampleQueryOperation.m */; };
|
||||
866DA5271D63D04700C9AF3F /* ORKMotionActivityQueryOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 866DA51B1D63D04700C9AF3F /* ORKMotionActivityQueryOperation.h */; };
|
||||
866DA5281D63D04700C9AF3F /* ORKMotionActivityQueryOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 866DA51C1D63D04700C9AF3F /* ORKMotionActivityQueryOperation.m */; };
|
||||
866DA5291D63D04700C9AF3F /* ORKOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 866DA51D1D63D04700C9AF3F /* ORKOperation.h */; };
|
||||
866DA52A1D63D04700C9AF3F /* ORKOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 866DA51E1D63D04700C9AF3F /* ORKOperation.m */; };
|
||||
866DA51F1D63D04700C9AF3F /* ORKCollector_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 866DA5131D63D04700C9AF3F /* ORKCollector_Internal.h */; platformFilter = ios; };
|
||||
866DA5201D63D04700C9AF3F /* ORKCollector.h in Headers */ = {isa = PBXBuildFile; fileRef = 866DA5141D63D04700C9AF3F /* ORKCollector.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
866DA5211D63D04700C9AF3F /* ORKCollector.m in Sources */ = {isa = PBXBuildFile; fileRef = 866DA5151D63D04700C9AF3F /* ORKCollector.m */; platformFilter = ios; };
|
||||
866DA5221D63D04700C9AF3F /* ORKDataCollectionManager_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 866DA5161D63D04700C9AF3F /* ORKDataCollectionManager_Internal.h */; platformFilter = ios; };
|
||||
866DA5231D63D04700C9AF3F /* ORKDataCollectionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 866DA5171D63D04700C9AF3F /* ORKDataCollectionManager.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
866DA5241D63D04700C9AF3F /* ORKDataCollectionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 866DA5181D63D04700C9AF3F /* ORKDataCollectionManager.m */; platformFilter = ios; };
|
||||
866DA5251D63D04700C9AF3F /* ORKHealthSampleQueryOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 866DA5191D63D04700C9AF3F /* ORKHealthSampleQueryOperation.h */; platformFilter = ios; };
|
||||
866DA5261D63D04700C9AF3F /* ORKHealthSampleQueryOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 866DA51A1D63D04700C9AF3F /* ORKHealthSampleQueryOperation.m */; platformFilter = ios; };
|
||||
866DA5271D63D04700C9AF3F /* ORKMotionActivityQueryOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 866DA51B1D63D04700C9AF3F /* ORKMotionActivityQueryOperation.h */; platformFilter = ios; };
|
||||
866DA5281D63D04700C9AF3F /* ORKMotionActivityQueryOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 866DA51C1D63D04700C9AF3F /* ORKMotionActivityQueryOperation.m */; platformFilter = ios; };
|
||||
866DA5291D63D04700C9AF3F /* ORKOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 866DA51D1D63D04700C9AF3F /* ORKOperation.h */; platformFilter = ios; };
|
||||
866DA52A1D63D04700C9AF3F /* ORKOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 866DA51E1D63D04700C9AF3F /* ORKOperation.m */; platformFilter = ios; };
|
||||
86C40CC01A8D7C5C00081FAC /* ORKCompletionStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40B521A8D7C5B00081FAC /* ORKCompletionStep.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
86C40CC21A8D7C5C00081FAC /* ORKCompletionStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40B531A8D7C5B00081FAC /* ORKCompletionStep.m */; };
|
||||
86C40CE41A8D7C5C00081FAC /* ORKAnswerFormat.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40B641A8D7C5B00081FAC /* ORKAnswerFormat.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
@@ -333,11 +332,11 @@
|
||||
86C40D141A8D7C5C00081FAC /* ORKHelpers_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40B7C1A8D7C5C00081FAC /* ORKHelpers_Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
86C40D161A8D7C5C00081FAC /* ORKErrors.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40B7D1A8D7C5C00081FAC /* ORKErrors.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
86C40D181A8D7C5C00081FAC /* ORKErrors.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40B7E1A8D7C5C00081FAC /* ORKErrors.m */; };
|
||||
86C40D1A1A8D7C5C00081FAC /* ORKFormItem_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40B7F1A8D7C5C00081FAC /* ORKFormItem_Internal.h */; };
|
||||
86C40D1A1A8D7C5C00081FAC /* ORKFormItem_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40B7F1A8D7C5C00081FAC /* ORKFormItem_Internal.h */; platformFilter = ios; };
|
||||
86C40D201A8D7C5C00081FAC /* ORKFormStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40B821A8D7C5C00081FAC /* ORKFormStep.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
86C40D221A8D7C5C00081FAC /* ORKFormStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40B831A8D7C5C00081FAC /* ORKFormStep.m */; };
|
||||
86C40D301A8D7C5C00081FAC /* ORKHealthAnswerFormat.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40B8A1A8D7C5C00081FAC /* ORKHealthAnswerFormat.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
86C40D321A8D7C5C00081FAC /* ORKHealthAnswerFormat.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40B8B1A8D7C5C00081FAC /* ORKHealthAnswerFormat.m */; };
|
||||
86C40D301A8D7C5C00081FAC /* ORKHealthAnswerFormat.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40B8A1A8D7C5C00081FAC /* ORKHealthAnswerFormat.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
86C40D321A8D7C5C00081FAC /* ORKHealthAnswerFormat.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40B8B1A8D7C5C00081FAC /* ORKHealthAnswerFormat.m */; platformFilter = ios; };
|
||||
86C40D361A8D7C5C00081FAC /* ORKHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40B8D1A8D7C5C00081FAC /* ORKHelpers.m */; };
|
||||
86C40D401A8D7C5C00081FAC /* ORKInstructionStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40B921A8D7C5C00081FAC /* ORKInstructionStep.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
86C40D421A8D7C5C00081FAC /* ORKInstructionStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40B931A8D7C5C00081FAC /* ORKInstructionStep.m */; };
|
||||
@@ -345,7 +344,7 @@
|
||||
86C40D581A8D7C5C00081FAC /* ORKOrderedTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40B9E1A8D7C5C00081FAC /* ORKOrderedTask.m */; };
|
||||
86C40D5E1A8D7C5C00081FAC /* ORKQuestionStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40BA11A8D7C5C00081FAC /* ORKQuestionStep.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
86C40D601A8D7C5C00081FAC /* ORKQuestionStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40BA21A8D7C5C00081FAC /* ORKQuestionStep.m */; };
|
||||
86C40D621A8D7C5C00081FAC /* ORKQuestionStep_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40BA31A8D7C5C00081FAC /* ORKQuestionStep_Internal.h */; };
|
||||
86C40D621A8D7C5C00081FAC /* ORKQuestionStep_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40BA31A8D7C5C00081FAC /* ORKQuestionStep_Internal.h */; platformFilter = ios; };
|
||||
86C40D6A1A8D7C5C00081FAC /* ORKResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40BA71A8D7C5C00081FAC /* ORKResult.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
86C40D6C1A8D7C5C00081FAC /* ORKResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40BA81A8D7C5C00081FAC /* ORKResult.m */; };
|
||||
86C40D6E1A8D7C5C00081FAC /* ORKResult_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40BA91A8D7C5C00081FAC /* ORKResult_Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
@@ -353,16 +352,16 @@
|
||||
86C40D901A8D7C5C00081FAC /* ORKStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40BBA1A8D7C5C00081FAC /* ORKStep.m */; };
|
||||
86C40D921A8D7C5C00081FAC /* ORKStep_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40BBB1A8D7C5C00081FAC /* ORKStep_Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
86C40DC61A8D7C5C00081FAC /* ORKTask.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40BD51A8D7C5C00081FAC /* ORKTask.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
86C40DFE1A8D7C5C00081FAC /* ORKConsentDocument.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40BF21A8D7C5C00081FAC /* ORKConsentDocument.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
86C40E001A8D7C5C00081FAC /* ORKConsentDocument.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40BF31A8D7C5C00081FAC /* ORKConsentDocument.m */; };
|
||||
86C40E021A8D7C5C00081FAC /* ORKConsentDocument_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40BF41A8D7C5C00081FAC /* ORKConsentDocument_Internal.h */; };
|
||||
86C40E081A8D7C5C00081FAC /* ORKConsentReviewStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40BF71A8D7C5C00081FAC /* ORKConsentReviewStep.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
86C40E0A1A8D7C5C00081FAC /* ORKConsentReviewStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40BF81A8D7C5C00081FAC /* ORKConsentReviewStep.m */; };
|
||||
86C40E181A8D7C5C00081FAC /* ORKConsentSection.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40BFF1A8D7C5C00081FAC /* ORKConsentSection.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
86C40E1A1A8D7C5C00081FAC /* ORKConsentSection.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40C001A8D7C5C00081FAC /* ORKConsentSection.m */; };
|
||||
86C40E1C1A8D7C5C00081FAC /* ORKConsentSection_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40C011A8D7C5C00081FAC /* ORKConsentSection_Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
86C40E1E1A8D7C5C00081FAC /* ORKConsentSignature.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40C021A8D7C5C00081FAC /* ORKConsentSignature.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
86C40E201A8D7C5C00081FAC /* ORKConsentSignature.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40C031A8D7C5C00081FAC /* ORKConsentSignature.m */; };
|
||||
86C40DFE1A8D7C5C00081FAC /* ORKConsentDocument.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40BF21A8D7C5C00081FAC /* ORKConsentDocument.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
86C40E001A8D7C5C00081FAC /* ORKConsentDocument.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40BF31A8D7C5C00081FAC /* ORKConsentDocument.m */; platformFilter = ios; };
|
||||
86C40E021A8D7C5C00081FAC /* ORKConsentDocument_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40BF41A8D7C5C00081FAC /* ORKConsentDocument_Internal.h */; platformFilter = ios; };
|
||||
86C40E081A8D7C5C00081FAC /* ORKConsentReviewStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40BF71A8D7C5C00081FAC /* ORKConsentReviewStep.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
86C40E0A1A8D7C5C00081FAC /* ORKConsentReviewStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40BF81A8D7C5C00081FAC /* ORKConsentReviewStep.m */; platformFilter = ios; };
|
||||
86C40E181A8D7C5C00081FAC /* ORKConsentSection.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40BFF1A8D7C5C00081FAC /* ORKConsentSection.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
86C40E1A1A8D7C5C00081FAC /* ORKConsentSection.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40C001A8D7C5C00081FAC /* ORKConsentSection.m */; platformFilter = ios; };
|
||||
86C40E1C1A8D7C5C00081FAC /* ORKConsentSection_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40C011A8D7C5C00081FAC /* ORKConsentSection_Private.h */; platformFilter = ios; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
86C40E1E1A8D7C5C00081FAC /* ORKConsentSignature.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40C021A8D7C5C00081FAC /* ORKConsentSignature.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
86C40E201A8D7C5C00081FAC /* ORKConsentSignature.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40C031A8D7C5C00081FAC /* ORKConsentSignature.m */; platformFilter = ios; };
|
||||
86CC8EB31AC09383001CCD89 /* ORKAccessibilityTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 86CC8EA81AC09383001CCD89 /* ORKAccessibilityTests.m */; };
|
||||
86CC8EB41AC09383001CCD89 /* ORKChoiceAnswerFormatHelperTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 86CC8EA91AC09383001CCD89 /* ORKChoiceAnswerFormatHelperTests.m */; };
|
||||
86CC8EB51AC09383001CCD89 /* ORKConsentTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 86CC8EAA1AC09383001CCD89 /* ORKConsentTests.m */; };
|
||||
@@ -372,21 +371,21 @@
|
||||
86D348021AC161B0006DB02B /* ORKRecorderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 86D348001AC16175006DB02B /* ORKRecorderTests.m */; };
|
||||
AE75433A24E32CCC00E4C7CF /* ORKEarlyTerminationConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = AE75433824E32CCC00E4C7CF /* ORKEarlyTerminationConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
AE75433B24E32CCC00E4C7CF /* ORKEarlyTerminationConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = AE75433924E32CCC00E4C7CF /* ORKEarlyTerminationConfiguration.m */; };
|
||||
B11C54991A9EEF8800265E61 /* ORKConsentSharingStep.h in Headers */ = {isa = PBXBuildFile; fileRef = B11C54961A9EEF8800265E61 /* ORKConsentSharingStep.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
B11C549B1A9EEF8800265E61 /* ORKConsentSharingStep.m in Sources */ = {isa = PBXBuildFile; fileRef = B11C54971A9EEF8800265E61 /* ORKConsentSharingStep.m */; };
|
||||
B11C54991A9EEF8800265E61 /* ORKConsentSharingStep.h in Headers */ = {isa = PBXBuildFile; fileRef = B11C54961A9EEF8800265E61 /* ORKConsentSharingStep.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
B11C549B1A9EEF8800265E61 /* ORKConsentSharingStep.m in Sources */ = {isa = PBXBuildFile; fileRef = B11C54971A9EEF8800265E61 /* ORKConsentSharingStep.m */; platformFilter = ios; };
|
||||
B183A4A21A8535D100C76870 /* ResearchKit_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = B1B894391A00345200C5CF2D /* ResearchKit_Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
B183A4DD1A8535D100C76870 /* ResearchKit.h in Headers */ = {isa = PBXBuildFile; fileRef = B1C1DE4F196F541F00F75544 /* ResearchKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
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, ); }; };
|
||||
B1C0F4E41A9BA65F0022C153 /* ResearchKit.strings in Resources */ = {isa = PBXBuildFile; fileRef = B1C0F4E11A9BA65F0022C153 /* ResearchKit.strings */; platformFilter = ios; };
|
||||
B1C7955E1A9FBF04007279BA /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B1C7955D1A9FBF04007279BA /* HealthKit.framework */; platformFilter = ios; settings = {ATTRIBUTES = (Required, ); }; };
|
||||
BA473FE8224DB38900A362E3 /* ORKBodyItem.h in Headers */ = {isa = PBXBuildFile; fileRef = BA473FE6224DB38900A362E3 /* ORKBodyItem.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
BA473FE9224DB38900A362E3 /* ORKBodyItem.m in Sources */ = {isa = PBXBuildFile; fileRef = BA473FE7224DB38900A362E3 /* ORKBodyItem.m */; };
|
||||
BA8C5021226FFB04001896D0 /* ORKLearnMoreItem.h in Headers */ = {isa = PBXBuildFile; fileRef = BA8C501F226FFB04001896D0 /* ORKLearnMoreItem.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
BAB96755226E5D67006AAC56 /* ORKStepContainerViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BAB96754226E5D67006AAC56 /* ORKStepContainerViewTests.m */; };
|
||||
BABBB1AE2097D97200CB29E5 /* ORKPDFViewerStep.h in Headers */ = {isa = PBXBuildFile; fileRef = BABBB1AC2097D97200CB29E5 /* ORKPDFViewerStep.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
BABBB1AF2097D97200CB29E5 /* ORKPDFViewerStep.m in Sources */ = {isa = PBXBuildFile; fileRef = BABBB1AD2097D97200CB29E5 /* ORKPDFViewerStep.m */; };
|
||||
BABBB1AE2097D97200CB29E5 /* ORKPDFViewerStep.h in Headers */ = {isa = PBXBuildFile; fileRef = BABBB1AC2097D97200CB29E5 /* ORKPDFViewerStep.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
BABBB1AF2097D97200CB29E5 /* ORKPDFViewerStep.m in Sources */ = {isa = PBXBuildFile; fileRef = BABBB1AD2097D97200CB29E5 /* ORKPDFViewerStep.m */; platformFilter = ios; };
|
||||
BAD9E9122255E9750014FA29 /* ORKLearnMoreInstructionStep.h in Headers */ = {isa = PBXBuildFile; fileRef = BAD9E9102255E9750014FA29 /* ORKLearnMoreInstructionStep.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
BAD9E9132255E9750014FA29 /* ORKLearnMoreInstructionStep.m in Sources */ = {isa = PBXBuildFile; fileRef = BAD9E9112255E9750014FA29 /* ORKLearnMoreInstructionStep.m */; };
|
||||
BC081DE224CBC4DE00AD92AA /* ORKTypes_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = BC081DE124CBC4DE00AD92AA /* ORKTypes_Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
BC081DE224CBC4DE00AD92AA /* ORKTypes_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = BC081DE124CBC4DE00AD92AA /* ORKTypes_Private.h */; platformFilter = ios; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
BC13CE391B0660220044153C /* ORKNavigableOrderedTask.h in Headers */ = {isa = PBXBuildFile; fileRef = BC13CE371B0660220044153C /* ORKNavigableOrderedTask.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
BC13CE3A1B0660220044153C /* ORKNavigableOrderedTask.m in Sources */ = {isa = PBXBuildFile; fileRef = BC13CE381B0660220044153C /* ORKNavigableOrderedTask.m */; };
|
||||
BC13CE3C1B0662990044153C /* ORKStepNavigationRule_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = BC13CE3B1B0662990044153C /* ORKStepNavigationRule_Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
@@ -395,28 +394,28 @@
|
||||
BC2908BC1FBD628F0030AB89 /* ORKTypes.m in Sources */ = {isa = PBXBuildFile; fileRef = BC2908BB1FBD628F0030AB89 /* ORKTypes.m */; };
|
||||
BC40696121104DDF00A4100C /* ResearchKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B183A5951A8535D100C76870 /* ResearchKit.framework */; };
|
||||
BC4D521F27B326EA0099DC18 /* ORKSecureCodingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC4D521E27B326EA0099DC18 /* ORKSecureCodingTests.swift */; };
|
||||
BC94EF311E962F7400143081 /* ORKDeprecated.h in Headers */ = {isa = PBXBuildFile; fileRef = BC94EF2F1E962F7400143081 /* ORKDeprecated.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
BC94EF361E96394C00143081 /* ORKRegistrationStep_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = BC94EF351E96394C00143081 /* ORKRegistrationStep_Internal.h */; };
|
||||
BC94EF311E962F7400143081 /* ORKDeprecated.h in Headers */ = {isa = PBXBuildFile; fileRef = BC94EF2F1E962F7400143081 /* ORKDeprecated.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
BC94EF361E96394C00143081 /* ORKRegistrationStep_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = BC94EF351E96394C00143081 /* ORKRegistrationStep_Internal.h */; platformFilter = ios; };
|
||||
BCA5C0351AEC05F20092AC8D /* ORKStepNavigationRule.h in Headers */ = {isa = PBXBuildFile; fileRef = BCA5C0331AEC05F20092AC8D /* ORKStepNavigationRule.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
BCA5C0361AEC05F20092AC8D /* ORKStepNavigationRule.m in Sources */ = {isa = PBXBuildFile; fileRef = BCA5C0341AEC05F20092AC8D /* ORKStepNavigationRule.m */; };
|
||||
BCAD50E81B0201EE0034806A /* ORKTaskTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BCAD50E71B0201EE0034806A /* ORKTaskTests.m */; };
|
||||
BCB080A11B83EFB900A3F400 /* ORKStepNavigationRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB080A01B83EFB900A3F400 /* ORKStepNavigationRule.swift */; };
|
||||
BCB8133C1C98367A00346561 /* ORKTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = BCB8133B1C98367A00346561 /* ORKTypes.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
BCB96C131B19C0EC002A0B96 /* ORKStepTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BCB96C121B19C0EC002A0B96 /* ORKStepTests.m */; };
|
||||
BCCE9EC121104B2200B809F8 /* ORKConsentDocument_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = BCCE9EC021104B2200B809F8 /* ORKConsentDocument_Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
BCCE9EC121104B2200B809F8 /* ORKConsentDocument_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = BCCE9EC021104B2200B809F8 /* ORKConsentDocument_Private.h */; platformFilter = ios; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
BCFF24BD1B0798D10044EC35 /* ORKResultPredicate.m in Sources */ = {isa = PBXBuildFile; fileRef = BCFF24BC1B0798D10044EC35 /* ORKResultPredicate.m */; };
|
||||
BF1D43851D4904C6007EE90B /* ORKVideoInstructionStep.h in Headers */ = {isa = PBXBuildFile; fileRef = BF1D43831D4904C6007EE90B /* ORKVideoInstructionStep.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
BF1D43861D4904C6007EE90B /* ORKVideoInstructionStep.m in Sources */ = {isa = PBXBuildFile; fileRef = BF1D43841D4904C6007EE90B /* ORKVideoInstructionStep.m */; };
|
||||
BF5161501BE9C53D00174DDD /* ORKWaitStep.h in Headers */ = {isa = PBXBuildFile; fileRef = BF9155A11BDE8DA9007FA459 /* ORKWaitStep.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
BF91559B1BDE8D7D007FA459 /* ORKReviewStep_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = BF9155951BDE8D7D007FA459 /* ORKReviewStep_Internal.h */; };
|
||||
BF91559C1BDE8D7D007FA459 /* ORKReviewStep.h in Headers */ = {isa = PBXBuildFile; fileRef = BF9155961BDE8D7D007FA459 /* ORKReviewStep.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
BF91559D1BDE8D7D007FA459 /* ORKReviewStep.m in Sources */ = {isa = PBXBuildFile; fileRef = BF9155971BDE8D7D007FA459 /* ORKReviewStep.m */; };
|
||||
BF9155A81BDE8DA9007FA459 /* ORKWaitStep.m in Sources */ = {isa = PBXBuildFile; fileRef = BF9155A21BDE8DA9007FA459 /* ORKWaitStep.m */; };
|
||||
BF1D43851D4904C6007EE90B /* ORKVideoInstructionStep.h in Headers */ = {isa = PBXBuildFile; fileRef = BF1D43831D4904C6007EE90B /* ORKVideoInstructionStep.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
BF1D43861D4904C6007EE90B /* ORKVideoInstructionStep.m in Sources */ = {isa = PBXBuildFile; fileRef = BF1D43841D4904C6007EE90B /* ORKVideoInstructionStep.m */; platformFilter = ios; };
|
||||
BF5161501BE9C53D00174DDD /* ORKWaitStep.h in Headers */ = {isa = PBXBuildFile; fileRef = BF9155A11BDE8DA9007FA459 /* ORKWaitStep.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
BF91559B1BDE8D7D007FA459 /* ORKReviewStep_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = BF9155951BDE8D7D007FA459 /* ORKReviewStep_Internal.h */; platformFilter = ios; };
|
||||
BF91559C1BDE8D7D007FA459 /* ORKReviewStep.h in Headers */ = {isa = PBXBuildFile; fileRef = BF9155961BDE8D7D007FA459 /* ORKReviewStep.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
BF91559D1BDE8D7D007FA459 /* ORKReviewStep.m in Sources */ = {isa = PBXBuildFile; fileRef = BF9155971BDE8D7D007FA459 /* ORKReviewStep.m */; platformFilter = ios; };
|
||||
BF9155A81BDE8DA9007FA459 /* ORKWaitStep.m in Sources */ = {isa = PBXBuildFile; fileRef = BF9155A21BDE8DA9007FA459 /* ORKWaitStep.m */; platformFilter = ios; };
|
||||
CA08054028AD7CC8001695EF /* ORKViewControllerProviding.h in Headers */ = {isa = PBXBuildFile; fileRef = CA08053F28AD7CC8001695EF /* ORKViewControllerProviding.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
CA0AC56928BD4FAC00E80040 /* ORKStepViewControllerHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA0AC56828BD4FAB00E80040 /* ORKStepViewControllerHelpers.swift */; };
|
||||
CA1C7A44288B0C68004DAB3A /* ResearchKitUI.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D06632F24FEF272005D9B40 /* ResearchKitUI.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
CA1C7A5C288B0CA2004DAB3A /* ResearchKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B183A5951A8535D100C76870 /* ResearchKit.framework */; };
|
||||
CA23A31D2899941700EC6E91 /* ORKSkin_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = CA23A31C2899941700EC6E91 /* ORKSkin_Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
CA23A31D2899941700EC6E91 /* ORKSkin_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = CA23A31C2899941700EC6E91 /* ORKSkin_Private.h */; platformFilter = ios; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
CA2616D32894649A008B1425 /* ORKRequestPermissionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 51E03D6C2491B6DB008F8406 /* ORKRequestPermissionView.m */; };
|
||||
CA2616D42894649A008B1425 /* ORKRequestPermissionButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 5170009C254A197300CACE12 /* ORKRequestPermissionButton.m */; };
|
||||
CA2B8F6128A16B050025B773 /* ORKSpeechInNoiseStepViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7118AC6E20BF6A7700D7A6BB /* ORKSpeechInNoiseStepViewController.m */; };
|
||||
@@ -538,20 +537,20 @@
|
||||
CA2B8FF928A177C30025B773 /* ORKTrailmakingStepViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 781D540E1DF886AB00223305 /* ORKTrailmakingStepViewController.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
CA2B8FFA28A177DE0025B773 /* ORKWalkingTaskStepViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40B211A8D7C5B00081FAC /* ORKWalkingTaskStepViewController.m */; };
|
||||
CA2B8FFB28A177E40025B773 /* ORKWalkingTaskStepViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40B201A8D7C5B00081FAC /* ORKWalkingTaskStepViewController.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
CA2B900928A17AD90025B773 /* ORKActiveStep_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40B321A8D7C5B00081FAC /* ORKActiveStep_Internal.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
CA2B900A28A17ADE0025B773 /* ORKActiveStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40B311A8D7C5B00081FAC /* ORKActiveStep.m */; };
|
||||
CA2B900B28A17AE10025B773 /* ORKActiveStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40B301A8D7C5B00081FAC /* ORKActiveStep.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
CA2B900928A17AD90025B773 /* ORKActiveStep_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40B321A8D7C5B00081FAC /* ORKActiveStep_Internal.h */; platformFilter = ios; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
CA2B900A28A17ADE0025B773 /* ORKActiveStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40B311A8D7C5B00081FAC /* ORKActiveStep.m */; platformFilter = ios; };
|
||||
CA2B900B28A17AE10025B773 /* ORKActiveStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40B301A8D7C5B00081FAC /* ORKActiveStep.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
CA2B901028A17BEE0025B773 /* ORKActiveTaskResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FF919A261E81A87B005C2A1E /* ORKActiveTaskResult.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
CA2B901128A17D120025B773 /* ORKFileResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FF919A391E81AF1D005C2A1E /* ORKFileResult.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
CA2B901228A17D170025B773 /* ORKFileResult.m in Sources */ = {isa = PBXBuildFile; fileRef = FF919A3A1E81AF1D005C2A1E /* ORKFileResult.m */; };
|
||||
CA2B901128A17D120025B773 /* ORKFileResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FF919A391E81AF1D005C2A1E /* ORKFileResult.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
CA2B901228A17D170025B773 /* ORKFileResult.m in Sources */ = {isa = PBXBuildFile; fileRef = FF919A3A1E81AF1D005C2A1E /* ORKFileResult.m */; platformFilter = ios; };
|
||||
CA2B901628A1802C0025B773 /* ORKOrderedTask+ORKPredefinedActiveTask.m in Sources */ = {isa = PBXBuildFile; fileRef = FF154FB31E82EF5E004ED908 /* ORKOrderedTask+ORKPredefinedActiveTask.m */; };
|
||||
CA2B901728A180480025B773 /* ORKOrderedTask+ORKPredefinedActiveTask.h in Headers */ = {isa = PBXBuildFile; fileRef = FF154FB21E82EF5E004ED908 /* ORKOrderedTask+ORKPredefinedActiveTask.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
CA2B901F28A1864A0025B773 /* ORKRecorder.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40B471A8D7C5B00081FAC /* ORKRecorder.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
CA2B902028A186500025B773 /* ORKRecorder_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40B491A8D7C5B00081FAC /* ORKRecorder_Internal.h */; };
|
||||
CA2B902128A186550025B773 /* ORKRecorder_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40B4A1A8D7C5B00081FAC /* ORKRecorder_Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
CA2B902228A1867E0025B773 /* ORKRecorder.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40B481A8D7C5B00081FAC /* ORKRecorder.m */; };
|
||||
CA2B902328A186A80025B773 /* ORKDataLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40B3C1A8D7C5B00081FAC /* ORKDataLogger.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
CA2B902428A186AF0025B773 /* ORKDataLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40B3D1A8D7C5B00081FAC /* ORKDataLogger.m */; };
|
||||
CA2B901F28A1864A0025B773 /* ORKRecorder.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40B471A8D7C5B00081FAC /* ORKRecorder.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
CA2B902028A186500025B773 /* ORKRecorder_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40B491A8D7C5B00081FAC /* ORKRecorder_Internal.h */; platformFilter = ios; };
|
||||
CA2B902128A186550025B773 /* ORKRecorder_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40B4A1A8D7C5B00081FAC /* ORKRecorder_Private.h */; platformFilter = ios; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
CA2B902228A1867E0025B773 /* ORKRecorder.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40B481A8D7C5B00081FAC /* ORKRecorder.m */; platformFilter = ios; };
|
||||
CA2B902328A186A80025B773 /* ORKDataLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40B3C1A8D7C5B00081FAC /* ORKDataLogger.h */; platformFilter = ios; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
CA2B902428A186AF0025B773 /* ORKDataLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40B3D1A8D7C5B00081FAC /* ORKDataLogger.m */; platformFilter = ios; };
|
||||
CA2B902628A187390025B773 /* ORKTask_Util.m in Sources */ = {isa = PBXBuildFile; fileRef = CA2B902528A187390025B773 /* ORKTask_Util.m */; };
|
||||
CA2B902728A18EA60025B773 /* ORKActiveStepViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40B381A8D7C5B00081FAC /* ORKActiveStepViewController.m */; };
|
||||
CA2B902828A18EAD0025B773 /* ORKActiveStepViewController_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40B391A8D7C5B00081FAC /* ORKActiveStepViewController_Internal.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
@@ -567,15 +566,15 @@
|
||||
CA6A0D7F288B51D30048C1EF /* ORKSkin.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40BB81A8D7C5C00081FAC /* ORKSkin.m */; };
|
||||
CA6A0D81288B54650048C1EF /* ORKConsentReviewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40BEC1A8D7C5C00081FAC /* ORKConsentReviewController.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
CA6A0D82288B54650048C1EF /* ORKConsentReviewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40BED1A8D7C5C00081FAC /* ORKConsentReviewController.m */; };
|
||||
CA6A0D83288B5B370048C1EF /* ORKHTMLPDFWriter.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40B8E1A8D7C5C00081FAC /* ORKHTMLPDFWriter.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
CA6A0D84288B5B370048C1EF /* ORKHTMLPDFPageRenderer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8DE27B3E1D5BC072009A26E3 /* ORKHTMLPDFPageRenderer.h */; };
|
||||
CA6A0D85288B5B370048C1EF /* ORKHTMLPDFPageRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8DE27B3F1D5BC0B9009A26E3 /* ORKHTMLPDFPageRenderer.m */; };
|
||||
CA6A0D86288B5B370048C1EF /* ORKHTMLPDFWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40B8F1A8D7C5C00081FAC /* ORKHTMLPDFWriter.m */; };
|
||||
CA6A0D83288B5B370048C1EF /* ORKHTMLPDFWriter.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40B8E1A8D7C5C00081FAC /* ORKHTMLPDFWriter.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
CA6A0D84288B5B370048C1EF /* ORKHTMLPDFPageRenderer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8DE27B3E1D5BC072009A26E3 /* ORKHTMLPDFPageRenderer.h */; platformFilter = ios; };
|
||||
CA6A0D85288B5B370048C1EF /* ORKHTMLPDFPageRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8DE27B3F1D5BC0B9009A26E3 /* ORKHTMLPDFPageRenderer.m */; platformFilter = ios; };
|
||||
CA6A0D86288B5B370048C1EF /* ORKHTMLPDFWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40B8F1A8D7C5C00081FAC /* ORKHTMLPDFWriter.m */; platformFilter = ios; };
|
||||
CA6A0D9E288F1CA20048C1EF /* ResearchKitUI_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = CA6A0D9D288F1BDC0048C1EF /* ResearchKitUI_Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
CA6A0DA0288F2C1C0048C1EF /* ResearchKitUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CA1C7A5A288B0C68004DAB3A /* ResearchKitUI.framework */; };
|
||||
CA954B6E28AD8A8C0020A35C /* ORKStep+ResearchKitActiveTask.m in Sources */ = {isa = PBXBuildFile; fileRef = CA954B6D28AD8A8C0020A35C /* ORKStep+ResearchKitActiveTask.m */; };
|
||||
CA954B6F28AD8DA50020A35C /* ORKStep+ResearchKitUI.m in Sources */ = {isa = PBXBuildFile; fileRef = CA08054328AD7EBA001695EF /* ORKStep+ResearchKitUI.m */; };
|
||||
CA994D5628AB08410019DEA4 /* ORKDeprecated.m in Sources */ = {isa = PBXBuildFile; fileRef = BC94EF301E962F7400143081 /* ORKDeprecated.m */; };
|
||||
CA994D5628AB08410019DEA4 /* ORKDeprecated.m in Sources */ = {isa = PBXBuildFile; fileRef = BC94EF301E962F7400143081 /* ORKDeprecated.m */; platformFilter = ios; };
|
||||
CAA20D4E288B3D5700EDC764 /* ORKRecordButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D43C5CF24217393006F4084 /* ORKRecordButton.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
CAA20D4F288B3D5700EDC764 /* ORKRecordButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D43C5D024217393006F4084 /* ORKRecordButton.m */; };
|
||||
CAA20D50288B3D6400EDC764 /* ORKCheckmarkView.h in Headers */ = {isa = PBXBuildFile; fileRef = BAD6FAAF2332A2FD006647E7 /* ORKCheckmarkView.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
@@ -989,41 +988,42 @@
|
||||
CAFAA6C228A198BD0010BBDE /* ResearchKitActiveTask_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = CAFAA6C128A198BD0010BBDE /* ResearchKitActiveTask_Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
CAFAA6C428A19E200010BBDE /* ResearchKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B183A5951A8535D100C76870 /* ResearchKit.framework */; };
|
||||
CAFAA6C528A19E260010BBDE /* ResearchKitUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CA1C7A5A288B0C68004DAB3A /* ResearchKitUI.framework */; };
|
||||
D44239791AF17F5100559D96 /* ORKImageCaptureStep.h in Headers */ = {isa = PBXBuildFile; fileRef = D44239771AF17F5100559D96 /* ORKImageCaptureStep.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
D442397A1AF17F5100559D96 /* ORKImageCaptureStep.m in Sources */ = {isa = PBXBuildFile; fileRef = D44239781AF17F5100559D96 /* ORKImageCaptureStep.m */; };
|
||||
D44239791AF17F5100559D96 /* ORKImageCaptureStep.h in Headers */ = {isa = PBXBuildFile; fileRef = D44239771AF17F5100559D96 /* ORKImageCaptureStep.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
D442397A1AF17F5100559D96 /* ORKImageCaptureStep.m in Sources */ = {isa = PBXBuildFile; fileRef = D44239781AF17F5100559D96 /* ORKImageCaptureStep.m */; platformFilter = ios; };
|
||||
E454438D2CC1C78E0004E385 /* ResearchKit_Prefix.pch in Headers */ = {isa = PBXBuildFile; fileRef = 5DABE5AD24DA16E600570C57 /* ResearchKit_Prefix.pch */; };
|
||||
FA7A9D2B1B082688005A2BEA /* ORKConsentDocumentTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FA7A9D2A1B082688005A2BEA /* ORKConsentDocumentTests.m */; };
|
||||
FA7A9D2F1B083DD3005A2BEA /* ORKConsentSectionFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = FA7A9D2D1B083DD3005A2BEA /* ORKConsentSectionFormatter.h */; };
|
||||
FA7A9D301B083DD3005A2BEA /* ORKConsentSectionFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = FA7A9D2E1B083DD3005A2BEA /* ORKConsentSectionFormatter.m */; };
|
||||
FA7A9D331B0843A9005A2BEA /* ORKConsentSignatureFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = FA7A9D311B0843A9005A2BEA /* ORKConsentSignatureFormatter.h */; };
|
||||
FA7A9D341B0843A9005A2BEA /* ORKConsentSignatureFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = FA7A9D321B0843A9005A2BEA /* ORKConsentSignatureFormatter.m */; };
|
||||
FA7A9D2F1B083DD3005A2BEA /* ORKConsentSectionFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = FA7A9D2D1B083DD3005A2BEA /* ORKConsentSectionFormatter.h */; platformFilter = ios; };
|
||||
FA7A9D301B083DD3005A2BEA /* ORKConsentSectionFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = FA7A9D2E1B083DD3005A2BEA /* ORKConsentSectionFormatter.m */; platformFilter = ios; };
|
||||
FA7A9D331B0843A9005A2BEA /* ORKConsentSignatureFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = FA7A9D311B0843A9005A2BEA /* ORKConsentSignatureFormatter.h */; platformFilter = ios; };
|
||||
FA7A9D341B0843A9005A2BEA /* ORKConsentSignatureFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = FA7A9D321B0843A9005A2BEA /* ORKConsentSignatureFormatter.m */; platformFilter = ios; };
|
||||
FA7A9D371B09365F005A2BEA /* ORKConsentSectionFormatterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FA7A9D361B09365F005A2BEA /* ORKConsentSectionFormatterTests.m */; };
|
||||
FA7A9D391B0969A7005A2BEA /* ORKConsentSignatureFormatterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FA7A9D381B0969A7005A2BEA /* ORKConsentSignatureFormatterTests.m */; };
|
||||
FF0CB38A1FD5C4C3002D838C /* ORKWebViewStepResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FF0CB3881FD5C4C3002D838C /* ORKWebViewStepResult.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
FF0CB38B1FD5C4C3002D838C /* ORKWebViewStepResult.m in Sources */ = {isa = PBXBuildFile; fileRef = FF0CB3891FD5C4C3002D838C /* ORKWebViewStepResult.m */; };
|
||||
FF5051ED1D668FF80065E677 /* ORKPageStep_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = FF5051EC1D668FF80065E677 /* ORKPageStep_Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
FF5051F01D66908C0065E677 /* ORKNavigablePageStep.h in Headers */ = {isa = PBXBuildFile; fileRef = FF5051EE1D66908C0065E677 /* ORKNavigablePageStep.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
FF5051F11D66908C0065E677 /* ORKNavigablePageStep.m in Sources */ = {isa = PBXBuildFile; fileRef = FF5051EF1D66908C0065E677 /* ORKNavigablePageStep.m */; };
|
||||
FF5CA6121D2C2670001660A3 /* ORKTableStep.h in Headers */ = {isa = PBXBuildFile; fileRef = FF5CA6101D2C2670001660A3 /* ORKTableStep.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
FF5CA6131D2C2670001660A3 /* ORKTableStep.m in Sources */ = {isa = PBXBuildFile; fileRef = FF5CA6111D2C2670001660A3 /* ORKTableStep.m */; };
|
||||
FF5CA61B1D2C6453001660A3 /* ORKSignatureStep.h in Headers */ = {isa = PBXBuildFile; fileRef = FF5CA6191D2C6453001660A3 /* ORKSignatureStep.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
FF5CA61C1D2C6453001660A3 /* ORKSignatureStep.m in Sources */ = {isa = PBXBuildFile; fileRef = FF5CA61A1D2C6453001660A3 /* ORKSignatureStep.m */; };
|
||||
FF0CB38A1FD5C4C3002D838C /* ORKWebViewStepResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FF0CB3881FD5C4C3002D838C /* ORKWebViewStepResult.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
FF0CB38B1FD5C4C3002D838C /* ORKWebViewStepResult.m in Sources */ = {isa = PBXBuildFile; fileRef = FF0CB3891FD5C4C3002D838C /* ORKWebViewStepResult.m */; platformFilter = ios; };
|
||||
FF5051ED1D668FF80065E677 /* ORKPageStep_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = FF5051EC1D668FF80065E677 /* ORKPageStep_Private.h */; platformFilter = ios; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
FF5051F01D66908C0065E677 /* ORKNavigablePageStep.h in Headers */ = {isa = PBXBuildFile; fileRef = FF5051EE1D66908C0065E677 /* ORKNavigablePageStep.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
FF5051F11D66908C0065E677 /* ORKNavigablePageStep.m in Sources */ = {isa = PBXBuildFile; fileRef = FF5051EF1D66908C0065E677 /* ORKNavigablePageStep.m */; platformFilter = ios; };
|
||||
FF5CA6121D2C2670001660A3 /* ORKTableStep.h in Headers */ = {isa = PBXBuildFile; fileRef = FF5CA6101D2C2670001660A3 /* ORKTableStep.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
FF5CA6131D2C2670001660A3 /* ORKTableStep.m in Sources */ = {isa = PBXBuildFile; fileRef = FF5CA6111D2C2670001660A3 /* ORKTableStep.m */; platformFilter = ios; };
|
||||
FF5CA61B1D2C6453001660A3 /* ORKSignatureStep.h in Headers */ = {isa = PBXBuildFile; fileRef = FF5CA6191D2C6453001660A3 /* ORKSignatureStep.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
FF5CA61C1D2C6453001660A3 /* ORKSignatureStep.m in Sources */ = {isa = PBXBuildFile; fileRef = FF5CA61A1D2C6453001660A3 /* ORKSignatureStep.m */; platformFilter = ios; };
|
||||
FF919A531E81BEB5005C2A1E /* ORKCollectionResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FF919A511E81BEB5005C2A1E /* ORKCollectionResult.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
FF919A541E81BEB5005C2A1E /* ORKCollectionResult.m in Sources */ = {isa = PBXBuildFile; fileRef = FF919A521E81BEB5005C2A1E /* ORKCollectionResult.m */; };
|
||||
FF919A561E81BEE0005C2A1E /* ORKCollectionResult_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = FF919A551E81BEDD005C2A1E /* ORKCollectionResult_Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
FF919A591E81C628005C2A1E /* ORKQuestionResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FF919A571E81C628005C2A1E /* ORKQuestionResult.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
FF919A5A1E81C628005C2A1E /* ORKQuestionResult.m in Sources */ = {isa = PBXBuildFile; fileRef = FF919A581E81C628005C2A1E /* ORKQuestionResult.m */; };
|
||||
FF919A5C1E81C641005C2A1E /* ORKQuestionResult_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = FF919A5B1E81C63B005C2A1E /* ORKQuestionResult_Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
FF919A5F1E81CF07005C2A1E /* ORKVideoInstructionStepResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FF919A5D1E81CF07005C2A1E /* ORKVideoInstructionStepResult.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
FF919A601E81CF07005C2A1E /* ORKVideoInstructionStepResult.m in Sources */ = {isa = PBXBuildFile; fileRef = FF919A5E1E81CF07005C2A1E /* ORKVideoInstructionStepResult.m */; };
|
||||
FF919A631E81D04D005C2A1E /* ORKSignatureResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FF919A611E81D04D005C2A1E /* ORKSignatureResult.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
FF919A641E81D04D005C2A1E /* ORKSignatureResult.m in Sources */ = {isa = PBXBuildFile; fileRef = FF919A621E81D04D005C2A1E /* ORKSignatureResult.m */; };
|
||||
FF919A661E81D168005C2A1E /* ORKSignatureResult_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = FF919A651E81D164005C2A1E /* ORKSignatureResult_Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
FF919A691E81D255005C2A1E /* ORKConsentSignatureResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FF919A671E81D255005C2A1E /* ORKConsentSignatureResult.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
FF919A6A1E81D255005C2A1E /* ORKConsentSignatureResult.m in Sources */ = {isa = PBXBuildFile; fileRef = FF919A681E81D255005C2A1E /* ORKConsentSignatureResult.m */; };
|
||||
FF919A6D1E81D3B0005C2A1E /* ORKPasscodeResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FF919A6B1E81D3B0005C2A1E /* ORKPasscodeResult.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
FF919A6E1E81D3B0005C2A1E /* ORKPasscodeResult.m in Sources */ = {isa = PBXBuildFile; fileRef = FF919A6C1E81D3B0005C2A1E /* ORKPasscodeResult.m */; };
|
||||
FFDDD8491D3555EA00446806 /* ORKPageStep.h in Headers */ = {isa = PBXBuildFile; fileRef = FFDDD8471D3555EA00446806 /* ORKPageStep.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
FFDDD84A1D3555EA00446806 /* ORKPageStep.m in Sources */ = {isa = PBXBuildFile; fileRef = FFDDD8481D3555EA00446806 /* ORKPageStep.m */; };
|
||||
FF919A5F1E81CF07005C2A1E /* ORKVideoInstructionStepResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FF919A5D1E81CF07005C2A1E /* ORKVideoInstructionStepResult.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
FF919A601E81CF07005C2A1E /* ORKVideoInstructionStepResult.m in Sources */ = {isa = PBXBuildFile; fileRef = FF919A5E1E81CF07005C2A1E /* ORKVideoInstructionStepResult.m */; platformFilter = ios; };
|
||||
FF919A631E81D04D005C2A1E /* ORKSignatureResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FF919A611E81D04D005C2A1E /* ORKSignatureResult.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
FF919A641E81D04D005C2A1E /* ORKSignatureResult.m in Sources */ = {isa = PBXBuildFile; fileRef = FF919A621E81D04D005C2A1E /* ORKSignatureResult.m */; platformFilter = ios; };
|
||||
FF919A661E81D168005C2A1E /* ORKSignatureResult_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = FF919A651E81D164005C2A1E /* ORKSignatureResult_Private.h */; platformFilter = ios; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
FF919A691E81D255005C2A1E /* ORKConsentSignatureResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FF919A671E81D255005C2A1E /* ORKConsentSignatureResult.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
FF919A6A1E81D255005C2A1E /* ORKConsentSignatureResult.m in Sources */ = {isa = PBXBuildFile; fileRef = FF919A681E81D255005C2A1E /* ORKConsentSignatureResult.m */; platformFilter = ios; };
|
||||
FF919A6D1E81D3B0005C2A1E /* ORKPasscodeResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FF919A6B1E81D3B0005C2A1E /* ORKPasscodeResult.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
FF919A6E1E81D3B0005C2A1E /* ORKPasscodeResult.m in Sources */ = {isa = PBXBuildFile; fileRef = FF919A6C1E81D3B0005C2A1E /* ORKPasscodeResult.m */; platformFilter = ios; };
|
||||
FFDDD8491D3555EA00446806 /* ORKPageStep.h in Headers */ = {isa = PBXBuildFile; fileRef = FFDDD8471D3555EA00446806 /* ORKPageStep.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
FFDDD84A1D3555EA00446806 /* ORKPageStep.m in Sources */ = {isa = PBXBuildFile; fileRef = FFDDD8481D3555EA00446806 /* ORKPageStep.m */; platformFilter = ios; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -4669,20 +4669,20 @@
|
||||
866DA5201D63D04700C9AF3F /* ORKCollector.h in Headers */,
|
||||
51A11F1A2BD08D5E0060C07E /* CMMotionActivity+ORKJSONDictionary.h in Headers */,
|
||||
BF91559B1BDE8D7D007FA459 /* ORKReviewStep_Internal.h in Headers */,
|
||||
FF919A561E81BEE0005C2A1E /* ORKCollectionResult_Private.h in Headers */,
|
||||
FF919A661E81D168005C2A1E /* ORKSignatureResult_Private.h in Headers */,
|
||||
FF5CA61B1D2C6453001660A3 /* ORKSignatureStep.h in Headers */,
|
||||
BC081DE224CBC4DE00AD92AA /* ORKTypes_Private.h in Headers */,
|
||||
5D04885C25F1B3AB0006C68B /* ORKDevice_Private.h in Headers */,
|
||||
00C2668E23022CD400337E0B /* ORKCustomStep.h in Headers */,
|
||||
BCCE9EC121104B2200B809F8 /* ORKConsentDocument_Private.h in Headers */,
|
||||
86C40D201A8D7C5C00081FAC /* ORKFormStep.h in Headers */,
|
||||
CA2B901F28A1864A0025B773 /* ORKRecorder.h in Headers */,
|
||||
BA473FE8224DB38900A362E3 /* ORKBodyItem.h in Headers */,
|
||||
2429D5721BBB5397003A512F /* ORKRegistrationStep.h in Headers */,
|
||||
5192BF842AE1BA47006E43FB /* ORKFrontFacingCameraStep.h in Headers */,
|
||||
FF919A561E81BEE0005C2A1E /* ORKCollectionResult_Private.h in Headers */,
|
||||
FF919A661E81D168005C2A1E /* ORKSignatureResult_Private.h in Headers */,
|
||||
BC081DE224CBC4DE00AD92AA /* ORKTypes_Private.h in Headers */,
|
||||
5D04885C25F1B3AB0006C68B /* ORKDevice_Private.h in Headers */,
|
||||
BCCE9EC121104B2200B809F8 /* ORKConsentDocument_Private.h in Headers */,
|
||||
86C40D201A8D7C5C00081FAC /* ORKFormStep.h in Headers */,
|
||||
51B94DBD2B311E810039B0E7 /* CLLocationManager+ResearchKit.h in Headers */,
|
||||
B183A4A21A8535D100C76870 /* ResearchKit_Private.h in Headers */,
|
||||
5192BF842AE1BA47006E43FB /* ORKFrontFacingCameraStep.h in Headers */,
|
||||
CA2B900B28A17AE10025B773 /* ORKActiveStep.h in Headers */,
|
||||
AE75433A24E32CCC00E4C7CF /* ORKEarlyTerminationConfiguration.h in Headers */,
|
||||
86C40CE81A8D7C5C00081FAC /* ORKAnswerFormat_Internal.h in Headers */,
|
||||
@@ -4707,6 +4707,16 @@
|
||||
519CE82A2C6582BE003BB584 /* ORKConditionStepConfiguration.h in Headers */,
|
||||
51AF1B202B683C3400D3B399 /* ORKWebViewStepResult_Private.h in Headers */,
|
||||
5D04884C25EF4CC30006C68B /* ORKQuestionStep_Private.h in Headers */,
|
||||
8419D66E1FB73CC80088D7E5 /* ORKWebViewStep.h in Headers */,
|
||||
FFDDD8491D3555EA00446806 /* ORKPageStep.h in Headers */,
|
||||
D44239791AF17F5100559D96 /* ORKImageCaptureStep.h in Headers */,
|
||||
FF919A6D1E81D3B0005C2A1E /* ORKPasscodeResult.h in Headers */,
|
||||
5192BF502AE09673006E43FB /* ORKPredicateFormItemVisibilityRule.h in Headers */,
|
||||
FF0CB38A1FD5C4C3002D838C /* ORKWebViewStepResult.h in Headers */,
|
||||
86C40D301A8D7C5C00081FAC /* ORKHealthAnswerFormat.h in Headers */,
|
||||
FF5CA6121D2C2670001660A3 /* ORKTableStep.h in Headers */,
|
||||
51A11F172BD08D5E0060C07E /* HKSample+ORKJSONDictionary.h in Headers */,
|
||||
BC13CE401B0666FD0044153C /* ORKResultPredicate.h in Headers */,
|
||||
86C40D621A8D7C5C00081FAC /* ORKQuestionStep_Internal.h in Headers */,
|
||||
B183A4DD1A8535D100C76870 /* ResearchKit.h in Headers */,
|
||||
BAD9E9122255E9750014FA29 /* ORKLearnMoreInstructionStep.h in Headers */,
|
||||
@@ -4729,25 +4739,16 @@
|
||||
86C40D5E1A8D7C5C00081FAC /* ORKQuestionStep.h in Headers */,
|
||||
519CE82C2C6582BE003BB584 /* ORKRelatedPerson.h in Headers */,
|
||||
FF919A591E81C628005C2A1E /* ORKQuestionResult.h in Headers */,
|
||||
8419D66E1FB73CC80088D7E5 /* ORKWebViewStep.h in Headers */,
|
||||
FFDDD8491D3555EA00446806 /* ORKPageStep.h in Headers */,
|
||||
D44239791AF17F5100559D96 /* ORKImageCaptureStep.h in Headers */,
|
||||
FF919A6D1E81D3B0005C2A1E /* ORKPasscodeResult.h in Headers */,
|
||||
24C296771BD055B800B42EF1 /* ORKLoginStep_Internal.h in Headers */,
|
||||
86C40CC01A8D7C5C00081FAC /* ORKCompletionStep.h in Headers */,
|
||||
5192BF502AE09673006E43FB /* ORKPredicateFormItemVisibilityRule.h in Headers */,
|
||||
519CE8272C6582BE003BB584 /* ORKRelativeGroup.h in Headers */,
|
||||
14A92C4822440195007547F2 /* ORKHelpers_Internal.h in Headers */,
|
||||
FF0CB38A1FD5C4C3002D838C /* ORKWebViewStepResult.h in Headers */,
|
||||
5D43C5D424255675006F4084 /* ORKBodyItem_Internal.h in Headers */,
|
||||
86C40D301A8D7C5C00081FAC /* ORKHealthAnswerFormat.h in Headers */,
|
||||
7167D028231B1EAA00AAB4DD /* ORKFormStep_Internal.h in Headers */,
|
||||
FF5CA6121D2C2670001660A3 /* ORKTableStep.h in Headers */,
|
||||
FA7A9D2F1B083DD3005A2BEA /* ORKConsentSectionFormatter.h in Headers */,
|
||||
51A11F172BD08D5E0060C07E /* HKSample+ORKJSONDictionary.h in Headers */,
|
||||
86C40D561A8D7C5C00081FAC /* ORKOrderedTask.h in Headers */,
|
||||
FF5051F01D66908C0065E677 /* ORKNavigablePageStep.h in Headers */,
|
||||
BC13CE401B0666FD0044153C /* ORKResultPredicate.h in Headers */,
|
||||
51EB9A532B8408D50064A515 /* ORKInstructionStepHTMLFormatter.h in Headers */,
|
||||
86C40E081A8D7C5C00081FAC /* ORKConsentReviewStep.h in Headers */,
|
||||
519CE82B2C6582BE003BB584 /* ORKFamilyHistoryStep.h in Headers */,
|
||||
@@ -4890,6 +4891,7 @@
|
||||
CAA20D7C288B3D9100EDC764 /* ORKSurveyAnswerCellForSES.h in Headers */,
|
||||
CAA20F3B288B3F6D00EDC764 /* UIView+ORKAccessibility.h in Headers */,
|
||||
CAA20D61288B3D9100EDC764 /* ORKScaleSlider.h in Headers */,
|
||||
E454438D2CC1C78E0004E385 /* ResearchKit_Prefix.pch in Headers */,
|
||||
CAA20DEF288B3DB400EDC764 /* ORKCaption1Label.h in Headers */,
|
||||
CAA20F31288B3F5B00EDC764 /* ORKConsentReviewStepViewController.h in Headers */,
|
||||
CAA20DC8288B3DB400EDC764 /* ORKRoundTappingButton.h in Headers */,
|
||||
@@ -5237,7 +5239,7 @@
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = YES;
|
||||
CLASSPREFIX = ORK;
|
||||
LastSwiftUpdateCheck = 1530;
|
||||
LastSwiftUpdateCheck = 1600;
|
||||
LastUpgradeCheck = 1500;
|
||||
ORGANIZATIONNAME = researchkit.org;
|
||||
TargetAttributes = {
|
||||
@@ -5259,7 +5261,7 @@
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 3FFF18381829DB1D00167070 /* Build configuration list for PBXProject "ResearchKit" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
compatibilityVersion = "Xcode 15.0";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
@@ -5530,18 +5532,18 @@
|
||||
519CE8292C6582BE003BB584 /* ORKConditionStepConfiguration.m in Sources */,
|
||||
86C40D6C1A8D7C5C00081FAC /* ORKResult.m in Sources */,
|
||||
86C40D181A8D7C5C00081FAC /* ORKErrors.m in Sources */,
|
||||
CA2B902428A186AF0025B773 /* ORKDataLogger.m in Sources */,
|
||||
5192BF852AE1BA47006E43FB /* ORKFrontFacingCameraStepResult.m in Sources */,
|
||||
031A0FC224CF4ECD000E4455 /* ORKSensorPermissionType.m in Sources */,
|
||||
FF919A6A1E81D255005C2A1E /* ORKConsentSignatureResult.m in Sources */,
|
||||
CA6A0D85288B5B370048C1EF /* ORKHTMLPDFPageRenderer.m in Sources */,
|
||||
519CE8232C6582BE003BB584 /* ORKFamilyHistoryStep.m in Sources */,
|
||||
51A11F182BD08D5E0060C07E /* CMMotionActivity+ORKJSONDictionary.m in Sources */,
|
||||
24A4DA151B8D1115009C797A /* ORKPasscodeStep.m in Sources */,
|
||||
BCA5C0361AEC05F20092AC8D /* ORKStepNavigationRule.m in Sources */,
|
||||
FF919A6A1E81D255005C2A1E /* ORKConsentSignatureResult.m in Sources */,
|
||||
5192BF5A2AE09794006E43FB /* ORKFormItemVisibilityRule.m in Sources */,
|
||||
86C40CC21A8D7C5C00081FAC /* ORKCompletionStep.m in Sources */,
|
||||
BF9155A81BDE8DA9007FA459 /* ORKWaitStep.m in Sources */,
|
||||
BCB080A11B83EFB900A3F400 /* ORKStepNavigationRule.swift in Sources */,
|
||||
AE75433B24E32CCC00E4C7CF /* ORKEarlyTerminationConfiguration.m in Sources */,
|
||||
519CE8282C6582BE003BB584 /* ORKRelativeGroup.m in Sources */,
|
||||
86C40D361A8D7C5C00081FAC /* ORKHelpers.m in Sources */,
|
||||
@@ -5561,6 +5563,7 @@
|
||||
51A11F192BD08D5E0060C07E /* HKSample+ORKJSONDictionary.m in Sources */,
|
||||
86C40D581A8D7C5C00081FAC /* ORKOrderedTask.m in Sources */,
|
||||
86C40D601A8D7C5C00081FAC /* ORKQuestionStep.m in Sources */,
|
||||
BCB080A11B83EFB900A3F400 /* ORKStepNavigationRule.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -5692,7 +5695,6 @@
|
||||
CAA20F3A288B3F6D00EDC764 /* ORKAccessibilityFunctions.m in Sources */,
|
||||
5192BF982AE1C5A1006E43FB /* ORKAnswerFormat+FormStepViewControllerAdditions.m in Sources */,
|
||||
CAA20E01288B3E8100EDC764 /* ORKRequestPermissionsStepContainerView.m in Sources */,
|
||||
51A11F242BD1548C0060C07E /* UIImageView+ResearchKit.m in Sources */,
|
||||
CAA20F38288B3F6700EDC764 /* ORKVerificationStepView.m in Sources */,
|
||||
CAA20D6C288B3D9100EDC764 /* ORKSurveyAnswerCellForScale.m in Sources */,
|
||||
CAA20E2A288B3E8200EDC764 /* ORKStepView.m in Sources */,
|
||||
@@ -6018,11 +6020,29 @@
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
0BC672DA2BD9C52D005798AC /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
0BC672DC2BD9C52D005798AC /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
3FFF18691829DB1E00167070 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 5D000ED12620F27100E5442A /* Project-Debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ENABLE_MODULE_VERIFIER = YES;
|
||||
ENABLE_MODULE_VERIFIER = NO;
|
||||
ENABLE_TESTING_SEARCH_PATHS = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
};
|
||||
@@ -6076,9 +6096,11 @@
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
MODULEMAP_PRIVATE_FILE = ResearchKit/ResearchKit_Private.modulemap;
|
||||
"OTHER_SWIFT_FLAGS[arch=*]" = "$(inherited) -runtime-compatibility-version none";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator watchos watchsimulator xros xrsimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TARGETED_DEVICE_FAMILY = "1,2,4,7";
|
||||
WATCHOS_DEPLOYMENT_TARGET = 10.0;
|
||||
XROS_DEPLOYMENT_TARGET = 1.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -6088,9 +6110,11 @@
|
||||
buildSettings = {
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
MODULEMAP_PRIVATE_FILE = ResearchKit/ResearchKit_Private.modulemap;
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator watchos watchsimulator xros xrsimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TARGETED_DEVICE_FAMILY = "1,2,4,7";
|
||||
WATCHOS_DEPLOYMENT_TARGET = 10.0;
|
||||
XROS_DEPLOYMENT_TARGET = 1.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
@@ -6102,6 +6126,7 @@
|
||||
CURRENT_PROJECT_VERSION = "";
|
||||
ENABLE_MODULE_VERIFIER = YES;
|
||||
ENABLE_TESTING_SEARCH_PATHS = YES;
|
||||
GCC_PREFIX_HEADER = ResearchKit/ResearchKit_Prefix.pch;
|
||||
MODULEMAP_PRIVATE_FILE = ResearchKitUI/ResearchKitUI_Private.modulemap;
|
||||
PRODUCT_NAME = ResearchKitUI;
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
@@ -6118,6 +6143,7 @@
|
||||
CURRENT_PROJECT_VERSION = "";
|
||||
ENABLE_MODULE_VERIFIER = YES;
|
||||
ENABLE_TESTING_SEARCH_PATHS = YES;
|
||||
GCC_PREFIX_HEADER = ResearchKit/ResearchKit_Prefix.pch;
|
||||
MODULEMAP_PRIVATE_FILE = ResearchKitUI/ResearchKitUI_Private.modulemap;
|
||||
PRODUCT_NAME = ResearchKitUI;
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
|
||||
@@ -54,6 +54,36 @@
|
||||
<Test
|
||||
Identifier = "ORKDataCollectionTests">
|
||||
</Test>
|
||||
<Test
|
||||
Identifier = "ORKJSONSerializationTests">
|
||||
</Test>
|
||||
<Test
|
||||
Identifier = "ORKJSONSerializationTests/testAddResult">
|
||||
</Test>
|
||||
<Test
|
||||
Identifier = "ORKJSONSerializationTests/testDateComponentsSerialization">
|
||||
</Test>
|
||||
<Test
|
||||
Identifier = "ORKJSONSerializationTests/testEquality">
|
||||
</Test>
|
||||
<Test
|
||||
Identifier = "ORKJSONSerializationTests/testInvalidDBHLValue">
|
||||
</Test>
|
||||
<Test
|
||||
Identifier = "ORKJSONSerializationTests/testMissingDefaultValueKeyInScaleAnswerFormat">
|
||||
</Test>
|
||||
<Test
|
||||
Identifier = "ORKJSONSerializationTests/testORKSampleDeserialization">
|
||||
</Test>
|
||||
<Test
|
||||
Identifier = "ORKJSONSerializationTests/testORKSerialization">
|
||||
</Test>
|
||||
<Test
|
||||
Identifier = "ORKJSONSerializationTests/testSecureCoding">
|
||||
</Test>
|
||||
<Test
|
||||
Identifier = "ORKJSONSerializationTests/testTaskModel">
|
||||
</Test>
|
||||
</SkippedTests>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
|
||||
@@ -14,7 +14,11 @@
|
||||
"testTargets" : [
|
||||
{
|
||||
"skippedTests" : [
|
||||
"ORKDataCollectionTests"
|
||||
"ORKDataCollectionTests",
|
||||
"ORKJSONSerializationTests\/testEquality",
|
||||
"ORKJSONSerializationTests\/testORKSampleDeserialization",
|
||||
"ORKJSONSerializationTests\/testORKSerialization",
|
||||
"ORKJSONSerializationTests\/testSecureCoding"
|
||||
],
|
||||
"target" : {
|
||||
"containerPath" : "container:ResearchKit.xcodeproj",
|
||||
|
||||
@@ -32,11 +32,7 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import <ResearchKit/ORKTypes.h>
|
||||
|
||||
@class ORKScaleAnswerFormat;
|
||||
@@ -56,7 +52,6 @@
|
||||
@class ORKLocationAnswerFormat;
|
||||
@class ORKSESAnswerFormat;
|
||||
@class ORKImageChoice;
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@@ -439,10 +434,6 @@ ORK_CLASS_AVAILABLE
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#pragma mark - iOS
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
@interface ORKAnswerFormat()
|
||||
|
||||
/// @name Factory methods
|
||||
@@ -532,7 +523,7 @@ ORK_CLASS_AVAILABLE
|
||||
minimumValue:(double)minimumValue
|
||||
maximumValue:(double)maximumValue
|
||||
defaultValue:(double)defaultValue;
|
||||
#if ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION
|
||||
#if ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION && TARGET_OS_IOS
|
||||
+ (ORKLocationAnswerFormat *)locationAnswerFormat;
|
||||
#endif
|
||||
|
||||
@@ -1790,6 +1781,7 @@ This By default, the value of this property is `NO`.
|
||||
*/
|
||||
@property (copy, nullable) NSString *placeholder;
|
||||
|
||||
#if !TARGET_OS_WATCH
|
||||
/**
|
||||
The autocapitalization type that applies to the user's input.
|
||||
|
||||
@@ -1832,6 +1824,7 @@ This By default, the value of this property is `NO`.
|
||||
If specified, overrides the default password generation rules for fields with secureTextEntry.
|
||||
*/
|
||||
@property (nonatomic, copy, nullable) UITextInputPasswordRules *passwordRules API_AVAILABLE(ios(12));
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
@@ -2214,7 +2207,7 @@ ORK_CLASS_AVAILABLE
|
||||
|
||||
An `ORKLocationAnswerFormat` object produces an `ORKLocationQuestionResult` object.
|
||||
*/
|
||||
#if ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION
|
||||
#if ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION && TARGET_OS_IOS
|
||||
ORK_CLASS_AVAILABLE
|
||||
@interface ORKLocationAnswerFormat : ORKAnswerFormat
|
||||
|
||||
@@ -2250,6 +2243,4 @@ ORK_CLASS_AVAILABLE
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -90,11 +90,11 @@ NSString *ORKQuestionTypeString(ORKQuestionType questionType) {
|
||||
}
|
||||
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
static NSNumberFormatterStyle ORKNumberFormattingStyleConvert(ORKNumberFormattingStyle style) {
|
||||
return style == ORKNumberFormattingStylePercent ? NSNumberFormatterPercentStyle : NSNumberFormatterDecimalStyle;
|
||||
}
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
@implementation ORKAnswerDefaultSource {
|
||||
NSMutableDictionary *_unitsTable;
|
||||
}
|
||||
@@ -289,8 +289,6 @@ static NSNumberFormatterStyle ORKNumberFormattingStyleConvert(ORKNumberFormattin
|
||||
|
||||
@implementation ORKAnswerFormat
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
|
||||
+ (ORKScaleAnswerFormat *)scaleAnswerFormatWithMaximumValue:(NSInteger)scaleMaximum
|
||||
minimumValue:(NSInteger)scaleMinimum
|
||||
defaultValue:(NSInteger)defaultValue
|
||||
@@ -424,7 +422,7 @@ static NSNumberFormatterStyle ORKNumberFormattingStyleConvert(ORKNumberFormattin
|
||||
calendar:calendar];
|
||||
[answerFormat setDaysBeforeCurrentDateToSetMinimumDate:daysBefore];
|
||||
[answerFormat setDaysAfterCurrentDateToSetMinimumDate:daysAfter];
|
||||
|
||||
|
||||
return answerFormat;
|
||||
}
|
||||
|
||||
@@ -489,7 +487,7 @@ static NSNumberFormatterStyle ORKNumberFormattingStyleConvert(ORKNumberFormattin
|
||||
defaultValue:defaultValue];
|
||||
}
|
||||
|
||||
#if ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION
|
||||
#if ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION && TARGET_OS_IOS
|
||||
+ (ORKLocationAnswerFormat *)locationAnswerFormat {
|
||||
return [ORKLocationAnswerFormat new];
|
||||
}
|
||||
@@ -501,8 +499,6 @@ static NSNumberFormatterStyle ORKNumberFormattingStyleConvert(ORKNumberFormattin
|
||||
bottomRungText:bottomRungText];
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+ (ORKBooleanAnswerFormat *)booleanAnswerFormat {
|
||||
return [ORKBooleanAnswerFormat new];
|
||||
}
|
||||
@@ -688,7 +684,6 @@ static NSArray *ork_processTextChoices(NSArray<ORKTextChoice *> *textChoices) {
|
||||
return choices;
|
||||
}
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
@implementation ORKValuePickerAnswerFormat {
|
||||
ORKChoiceAnswerFormatHelper *_helper;
|
||||
ORKTextChoice *_nullTextChoice;
|
||||
@@ -841,7 +836,7 @@ static NSArray *ork_processTextChoices(NSArray<ORKTextChoice *> *textChoices) {
|
||||
|
||||
- (BOOL)isEqual:(id)object {
|
||||
BOOL isParentSame = [super isEqual:object];
|
||||
|
||||
|
||||
__typeof(self) castObject = object;
|
||||
return (isParentSame &&
|
||||
ORKEqualObjects(self.valuePickers, castObject.valuePickers));
|
||||
@@ -890,7 +885,7 @@ static NSArray *ork_processTextChoices(NSArray<ORKTextChoice *> *textChoices) {
|
||||
if (![answer isKindOfClass:[NSArray class]] || ([(NSArray*)answer count] != self.valuePickers.count)) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
NSArray *answers = (NSArray*)answer;
|
||||
__block NSMutableArray <NSString *> *answerTexts = [NSMutableArray new];
|
||||
[answers enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
@@ -901,11 +896,11 @@ static NSArray *ork_processTextChoices(NSArray<ORKTextChoice *> *textChoices) {
|
||||
*stop = YES;
|
||||
}
|
||||
}];
|
||||
|
||||
|
||||
if (answerTexts.count != self.valuePickers.count) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
return [answerTexts componentsJoinedByString:self.separator];
|
||||
}
|
||||
|
||||
@@ -1027,8 +1022,6 @@ static NSArray *ork_processTextChoices(NSArray<ORKTextChoice *> *textChoices) {
|
||||
}
|
||||
|
||||
@end
|
||||
#endif
|
||||
|
||||
|
||||
#pragma mark - ORKTextChoiceAnswerFormat
|
||||
|
||||
@@ -1488,7 +1481,6 @@ NSArray<Class> *ORKAllowableValueClasses(void) {
|
||||
|
||||
|
||||
#pragma mark - ORKTextChoiceOther
|
||||
#if TARGET_OS_IOS
|
||||
@implementation ORKTextChoiceOther
|
||||
|
||||
+ (instancetype)new {
|
||||
@@ -1712,7 +1704,6 @@ NSArray<Class> *ORKAllowableValueClasses(void) {
|
||||
}
|
||||
|
||||
@end
|
||||
#endif
|
||||
|
||||
|
||||
#pragma mark - ORKBooleanAnswerFormat
|
||||
@@ -1800,7 +1791,7 @@ NSArray<Class> *ORKAllowableValueClasses(void) {
|
||||
|
||||
|
||||
#pragma mark - ORKTimeOfDayAnswerFormat
|
||||
#if TARGET_OS_IOS
|
||||
|
||||
@implementation ORKTimeOfDayAnswerFormat
|
||||
|
||||
- (instancetype)init {
|
||||
@@ -2827,7 +2818,7 @@ NSArray<Class> *ORKAllowableValueClasses(void) {
|
||||
|
||||
|
||||
#pragma mark - ORKTextScaleAnswerFormat
|
||||
#if TARGET_OS_IOS
|
||||
|
||||
@interface ORKTextScaleAnswerFormat () {
|
||||
|
||||
ORKChoiceAnswerFormatHelper *_helper;
|
||||
@@ -3038,19 +3029,15 @@ NSArray<Class> *ORKAllowableValueClasses(void) {
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#pragma mark - ORKTextAnswerFormat
|
||||
|
||||
@interface ORKTextAnswerFormat()
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@implementation ORKTextAnswerFormat
|
||||
|
||||
|
||||
- (Class)questionResultClass {
|
||||
return [ORKTextQuestionResult class];
|
||||
}
|
||||
@@ -3069,7 +3056,6 @@ NSArray<Class> *ORKAllowableValueClasses(void) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
- (instancetype)initWithMaximumLength:(NSInteger)maximumLength {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
@@ -3321,7 +3307,6 @@ static NSString *const kSecureTextEntryEscapeString = @"*";
|
||||
return answerString;
|
||||
}
|
||||
|
||||
|
||||
- (ORKQuestionResult *)resultWithIdentifier:(NSString *)identifier answer:(id)answer {
|
||||
ORKQuestionResult *questionResult = nil;
|
||||
questionResult = (ORKQuestionResult *)[super resultWithIdentifier:identifier answer:answer];
|
||||
@@ -4019,7 +4004,7 @@ static const NSInteger ORKAgeAnswerDefaultMaxAge = 125;
|
||||
@end
|
||||
|
||||
|
||||
#if ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION
|
||||
#if ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION && TARGET_OS_IOS
|
||||
#pragma mark - ORKLocationAnswerFormat
|
||||
@implementation ORKLocationAnswerFormat
|
||||
|
||||
@@ -4153,4 +4138,3 @@ static const NSInteger ORKAgeAnswerDefaultMaxAge = 125;
|
||||
}
|
||||
|
||||
@end
|
||||
#endif
|
||||
|
||||
@@ -33,10 +33,9 @@
|
||||
#import <HealthKit/HealthKit.h>
|
||||
#endif
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import <ResearchKit/ORKAnswerFormat_Private.h>
|
||||
#import <ResearchKit/ORKChoiceAnswerFormatHelper.h>
|
||||
#endif
|
||||
|
||||
@class ORKChoiceAnswerFormatHelper;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
@@ -58,7 +57,7 @@ NSString *ORKQuestionTypeString(ORKQuestionType questionType);
|
||||
@end
|
||||
|
||||
ORK_DESIGNATE_CODING_AND_SERIALIZATION_INITIALIZERS(ORKAnswerFormat)
|
||||
#if TARGET_OS_IOS
|
||||
#if TARGET_OS_IOS || TARGET_OS_VISION
|
||||
ORK_DESIGNATE_CODING_AND_SERIALIZATION_INITIALIZERS(ORKImageChoiceAnswerFormat)
|
||||
ORK_DESIGNATE_CODING_AND_SERIALIZATION_INITIALIZERS(ORKValuePickerAnswerFormat)
|
||||
ORK_DESIGNATE_CODING_AND_SERIALIZATION_INITIALIZERS(ORKMultipleValuePickerAnswerFormat)
|
||||
@@ -135,6 +134,11 @@ ORK_DESIGNATE_CODING_AND_SERIALIZATION_INITIALIZERS(ORKTextChoice)
|
||||
|
||||
@end
|
||||
|
||||
@interface ORKDateAnswerFormat () {
|
||||
NSDate *_currentDateOverride;
|
||||
}
|
||||
@end
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
@protocol ORKScaleAnswerFormatProvider <NSObject>
|
||||
|
||||
@@ -198,6 +202,7 @@ NSArray<Class> *ORKAllowableValueClasses(void);
|
||||
@end
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
|
||||
@interface ORKValuePickerAnswerFormat ()
|
||||
|
||||
- (instancetype)initWithTextChoices:(NSArray<ORKTextChoice *> *)textChoices nullChoice:(ORKTextChoice *)nullChoice NS_DESIGNATED_INITIALIZER;
|
||||
@@ -206,7 +211,6 @@ NSArray<Class> *ORKAllowableValueClasses(void);
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface ORKImageChoice () <ORKAnswerOption>
|
||||
|
||||
@end
|
||||
@@ -218,11 +222,7 @@ NSArray<Class> *ORKAllowableValueClasses(void);
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface ORKDateAnswerFormat () {
|
||||
NSDate *_currentDateOverride;
|
||||
}
|
||||
|
||||
@interface ORKDateAnswerFormat ()
|
||||
- (NSDate *)pickerDefaultDate;
|
||||
- (nullable NSDate *)pickerMinimumDate;
|
||||
- (nullable NSDate *)pickerMaximumDate;
|
||||
|
||||
@@ -28,12 +28,7 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import <ResearchKit/ORKAnswerFormat.h>
|
||||
#endif
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@@ -60,7 +55,6 @@ ORK_EXTERN id ORKNullAnswerValue(void) ORK_AVAILABLE_DECL;
|
||||
|
||||
An `ORKConfirmTextAnswerFormat` object produces an `ORKBooleanQuestionResult` object.
|
||||
*/
|
||||
#if TARGET_OS_IOS
|
||||
ORK_CLASS_AVAILABLE
|
||||
@interface ORKConfirmTextAnswerFormat : ORKTextAnswerFormat
|
||||
|
||||
@@ -95,7 +89,6 @@ ORK_CLASS_AVAILABLE
|
||||
@property (nonatomic, copy, readonly) NSString *errorMessage;
|
||||
|
||||
@end
|
||||
#endif
|
||||
|
||||
@protocol ORKAnswerFormatPlatterPresentable <NSObject>
|
||||
|
||||
|
||||
@@ -30,16 +30,20 @@
|
||||
|
||||
|
||||
#import "ORKBodyItem.h"
|
||||
#import "ORKBodyItem_Internal.h"
|
||||
#import "ORKLearnMoreInstructionStep.h"
|
||||
#import "ORKHelpers_Internal.h"
|
||||
|
||||
#if !TARGET_OS_WATCH
|
||||
#import "ORKBodyItem_Internal.h"
|
||||
#endif
|
||||
|
||||
@implementation ORKBodyItem
|
||||
{
|
||||
// For our internal custom button type
|
||||
BOOL _isCustomButtonType;
|
||||
}
|
||||
|
||||
#if !TARGET_OS_WATCH
|
||||
- (instancetype)initWithCustomButtonConfigurationHandler:(void(^)(UIButton *button))configurationHandler
|
||||
{
|
||||
self = [super init];
|
||||
@@ -50,6 +54,7 @@
|
||||
}
|
||||
return self;
|
||||
}
|
||||
#endif
|
||||
|
||||
- (BOOL)isCustomButtonItemType
|
||||
{
|
||||
|
||||
@@ -34,9 +34,11 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ORKBodyItem ()
|
||||
|
||||
#if !TARGET_OS_WATCH
|
||||
@property (nonatomic, copy, nullable) void (^customButtonConfigurationHandler)(UIButton *button);
|
||||
|
||||
- (instancetype)initWithCustomButtonConfigurationHandler:(void(^)(UIButton *button))configurationHandler;
|
||||
#endif
|
||||
|
||||
- (BOOL)isCustomButtonItemType;
|
||||
|
||||
|
||||
@@ -28,10 +28,7 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import <ResearchKit/ORKResult.h>
|
||||
#endif
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
@@ -29,11 +29,7 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import <ResearchKit/ORKCollectionResult.h>
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
@@ -30,10 +30,9 @@
|
||||
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <ResearchKit/ORKInstructionStep.h>
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import <ResearchKit/ORKInstructionStep.h>
|
||||
#import <ResearchKit/ORKTask.h>
|
||||
#endif
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !TARGET_OS_VISION
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <ResearchKit/ResearchKit.h>
|
||||
@@ -200,3 +201,5 @@ ORK_CLASS_AVAILABLE
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
#endif
|
||||
|
||||
@@ -29,10 +29,7 @@
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#if TARGET_OS_IOS
|
||||
#import <ResearchKit/ORKDefines.h>
|
||||
#endif
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
@@ -28,10 +28,7 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import <ResearchKit/ORKDevice.h>
|
||||
#endif
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
@@ -29,11 +29,7 @@
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import <ResearchKit/ORKDefines.h>
|
||||
#endif
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
@@ -30,12 +30,7 @@
|
||||
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import <ResearchKit/ORKDefines.h>
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
@@ -30,12 +30,8 @@
|
||||
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import <ResearchKit/ORKStep.h>
|
||||
#import <ResearchKit/ORKDefines.h>
|
||||
#endif
|
||||
#import <ResearchKit/ORKStep.h>
|
||||
|
||||
/**
|
||||
Values that determine the style
|
||||
|
||||
@@ -39,7 +39,10 @@
|
||||
#import "ORKStep_Private.h"
|
||||
|
||||
#import "ORKHelpers_Internal.h"
|
||||
|
||||
#if !TARGET_OS_WATCH
|
||||
#import "ORKFormItemVisibilityRule.h"
|
||||
#endif
|
||||
|
||||
@implementation ORKFormStep
|
||||
|
||||
@@ -182,7 +185,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION
|
||||
#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION && TARGET_OS_IOS
|
||||
- (NSSet<HKObjectType *> *)requestedHealthKitTypesForReading {
|
||||
NSMutableSet<HKObjectType *> *healthTypes = [NSMutableSet set];
|
||||
|
||||
@@ -262,6 +265,7 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
- (ORKFormItem *)confirmationAnswerFormItemWithIdentifier:(NSString *)identifier
|
||||
text:(nullable NSString *)text
|
||||
errorMessage:(NSString *)errorMessage {
|
||||
@@ -281,6 +285,7 @@
|
||||
optional:self.optional];
|
||||
return item;
|
||||
}
|
||||
#endif
|
||||
|
||||
+ (BOOL)supportsSecureCoding {
|
||||
return YES;
|
||||
@@ -294,7 +299,9 @@
|
||||
item->_learnMoreItem = [_learnMoreItem copy];
|
||||
item->_showsProgress = _showsProgress;
|
||||
item->_tagText = [_tagText copy];
|
||||
#if !TARGET_OS_WATCH
|
||||
item->_visibilityRule = [_visibilityRule copy];
|
||||
#endif
|
||||
return item;
|
||||
}
|
||||
|
||||
@@ -311,7 +318,9 @@
|
||||
ORK_DECODE_OBJ_CLASS(aDecoder, answerFormat, ORKAnswerFormat);
|
||||
ORK_DECODE_OBJ_CLASS(aDecoder, step, ORKFormStep);
|
||||
ORK_DECODE_OBJ_CLASS(aDecoder, tagText, NSString);
|
||||
#if !TARGET_OS_WATCH
|
||||
ORK_DECODE_OBJ_CLASS(aDecoder, visibilityRule, ORKFormItemVisibilityRule);
|
||||
#endif
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -350,8 +359,12 @@
|
||||
}
|
||||
|
||||
- (NSUInteger)hash {
|
||||
#if !TARGET_OS_WATCH
|
||||
// Ignore the step reference - it's not part of the content of this item
|
||||
return _identifier.hash ^ _text.hash ^ _placeholder.hash ^ _answerFormat.hash ^ (_optional ? 0xf : 0x0) ^ _detailText.hash ^ _learnMoreItem.hash ^ (_showsProgress ? 0xf : 0x0) ^ _tagText.hash ^ _visibilityRule.hash;
|
||||
#else
|
||||
return _identifier.hash ^ _text.hash ^ _placeholder.hash ^ _answerFormat.hash ^ (_optional ? 0xf : 0x0) ^ _detailText.hash ^ _learnMoreItem.hash ^ (_showsProgress ? 0xf : 0x0) ^ _tagText.hash;
|
||||
#endif
|
||||
}
|
||||
|
||||
- (ORKAnswerFormat *)impliedAnswerFormat {
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
#import <ResearchKit/ORKAnswerFormat.h>
|
||||
|
||||
#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION
|
||||
#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION && TARGET_OS_IOS
|
||||
#import <HealthKit/HealthKit.h>
|
||||
#endif
|
||||
|
||||
@@ -74,7 +74,7 @@ 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
|
||||
#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION && TARGET_OS_IOS
|
||||
ORK_CLASS_AVAILABLE
|
||||
@interface ORKHealthKitCharacteristicTypeAnswerFormat : ORKAnswerFormat
|
||||
|
||||
@@ -226,6 +226,7 @@ included in the question result generated by form items or question steps
|
||||
- (NSString *)localizedUnitString;
|
||||
|
||||
@end
|
||||
#endif
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -29,19 +29,13 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import <ResearchKit/ORKTypes.h>
|
||||
#import <ResearchKit/ORKHelpers_Private.h>
|
||||
#import <ResearchKit/ORKErrors.h>
|
||||
#endif
|
||||
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <os/log.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import <ResearchKit/ORKErrors.h>
|
||||
#import <ResearchKit/ORKHelpers_Private.h>
|
||||
#import <ResearchKit/ORKTypes.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
@@ -30,11 +30,7 @@
|
||||
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#if TARGET_OS_IOS
|
||||
#import <ResearchKit/ORKDefines.h>
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
@@ -28,14 +28,8 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import <ResearchKit/ORKStep.h>
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
@@ -30,12 +30,7 @@
|
||||
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import <ResearchKit/ORKTask.h>
|
||||
#endif
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
@@ -30,16 +30,19 @@
|
||||
*/
|
||||
|
||||
#import "ORKOrderedTask.h"
|
||||
|
||||
#import "ORKAnswerFormat.h"
|
||||
#import "ORKInstructionStep.h"
|
||||
#import "ORKCompletionStep.h"
|
||||
#import "ORKStep_Private.h"
|
||||
#import "ORKHelpers_Internal.h"
|
||||
#import "ORKSkin.h"
|
||||
#if TARGET_OS_IOS
|
||||
#import "ORKQuestionStep.h"
|
||||
#import "ORKFormStep.h"
|
||||
#import "ORKFormItem_Internal.h"
|
||||
#import "ORKHelpers_Internal.h"
|
||||
#import "ORKInstructionStep.h"
|
||||
#import "ORKQuestionStep.h"
|
||||
#import "ORKSkin.h"
|
||||
#import "ORKStep_Private.h"
|
||||
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import "ORKActiveStep_Internal.h"
|
||||
#import "ORKEarlyTerminationConfiguration.h"
|
||||
#endif
|
||||
@@ -263,7 +266,6 @@
|
||||
int currentStepStartingProgressNumber = 0;
|
||||
|
||||
for (ORKStep *step in self.steps) {
|
||||
#if TARGET_OS_IOS
|
||||
if ([step isKindOfClass:[ORKFormStep class]]) {
|
||||
ORKFormStep *formStep = (ORKFormStep *)step;
|
||||
if (formStep.identifier == currentStep.identifier) {
|
||||
@@ -277,14 +279,6 @@
|
||||
}
|
||||
totalQuestions += 1;
|
||||
}
|
||||
#else
|
||||
if ([step isKindOfClass:[ORKQuestionStep class]]) {
|
||||
if (step.identifier == currentStep.identifier) {
|
||||
currentStepStartingProgressNumber = (totalQuestions + 1);
|
||||
}
|
||||
totalQuestions += 1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
totalProgress.currentStepStartingProgressPosition = currentStepStartingProgressNumber;
|
||||
@@ -293,7 +287,6 @@
|
||||
return totalProgress;
|
||||
}
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
- (NSMutableArray *)calculateSectionsForFormItems:(NSArray *)formItems {
|
||||
NSMutableArray<NSMutableArray *> *_sections = [NSMutableArray new];
|
||||
NSMutableArray *section = nil;
|
||||
@@ -374,6 +367,7 @@
|
||||
return NO;
|
||||
}
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
- (BOOL)providesBackgroundAudioPrompts {
|
||||
BOOL providesAudioPrompts = NO;
|
||||
for (ORKStep *step in self.steps) {
|
||||
|
||||
@@ -28,12 +28,8 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import <ResearchKit/ORKStep.h>
|
||||
#import <ResearchKit/ORKOrderedTask.h>
|
||||
#endif
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import <ResearchKit/ORKDefines.h>
|
||||
#import <UserNotifications/UserNotifications.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@@ -49,7 +50,6 @@ typedef NS_ENUM(NSInteger, ORKRequestPermissionsState) {
|
||||
ORKRequestPermissionsStateError,
|
||||
};
|
||||
|
||||
typedef NS_OPTIONS(NSUInteger, UNAuthorizationOptions);
|
||||
typedef NSString * SRSensor NS_TYPED_ENUM API_AVAILABLE(ios(14.0));
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,17 +28,13 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import <Contacts/Contacts.h>
|
||||
#import <ResearchKit/ORKResult.h>
|
||||
#endif
|
||||
|
||||
|
||||
#if ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION
|
||||
#if ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION && TARGET_OS_IOS
|
||||
#import <CoreLocation/CLLocation.h>
|
||||
#endif
|
||||
|
||||
#import <Contacts/Contacts.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class CLCircularRegion;
|
||||
@@ -153,7 +149,7 @@ ORK_CLASS_AVAILABLE
|
||||
@end
|
||||
|
||||
|
||||
#if ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION
|
||||
#if ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION && TARGET_OS_IOS
|
||||
/**
|
||||
The `ORKLocation` class represents the location addess obtained from a location question.
|
||||
*/
|
||||
@@ -184,7 +180,7 @@ ORK_CLASS_AVAILABLE
|
||||
@property (nonatomic, copy, readonly, nullable) CNPostalAddress *postalAddress;
|
||||
|
||||
@end
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
A result object from a location answer format.
|
||||
@@ -196,7 +192,8 @@ ORK_CLASS_AVAILABLE
|
||||
completes, it may be appropriate to serialize it for transmission to a server,
|
||||
or to immediately perform analysis on it.
|
||||
*/
|
||||
#if ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION
|
||||
|
||||
#if ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION && TARGET_OS_IOS
|
||||
ORK_CLASS_AVAILABLE
|
||||
@interface ORKLocationQuestionResult : ORKQuestionResult
|
||||
|
||||
@@ -206,7 +203,7 @@ ORK_CLASS_AVAILABLE
|
||||
@property (nonatomic, copy, nullable) ORKLocation *locationAnswer;
|
||||
|
||||
@end
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
A result object from a multiple-component picker-style choice-based answer format.
|
||||
|
||||
@@ -28,15 +28,18 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import <ResearchKit/ORKAnswerFormat_Internal.h>
|
||||
#import <ResearchKit/ORKAnswerFormat_Private.h>
|
||||
#import <ResearchKit/ORKHelpers_Internal.h>
|
||||
#import <ResearchKit/ORKQuestionResult.h>
|
||||
#import <ResearchKit/ORKQuestionStep.h>
|
||||
#import <ResearchKit/ORKQuestionStep_Private.h>
|
||||
#import <ResearchKit/ORKResult_Private.h>
|
||||
#import <ResearchKit/ORKStep_Private.h>
|
||||
|
||||
#import "ORKQuestionResult_Private.h"
|
||||
#import "ORKResult_Private.h"
|
||||
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import "ORKQuestionStep.h"
|
||||
#import "ORKHelpers_Internal.h"
|
||||
#import "ORKAnswerFormat_Internal.h"
|
||||
#if ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION && TARGET_OS_IOS
|
||||
#import <CoreLocation/CoreLocation.h>
|
||||
#import <MapKit/MapKit.h>
|
||||
#endif
|
||||
|
||||
@implementation ORKQuestionResult {
|
||||
@@ -326,7 +329,7 @@
|
||||
|
||||
@end
|
||||
|
||||
#if ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION
|
||||
#if ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION && TARGET_OS_IOS
|
||||
#pragma mark - ORKLocationQuestionResult
|
||||
|
||||
@implementation ORKLocation
|
||||
@@ -468,7 +471,6 @@ static NSString *const RegionIdentifierKey = @"region.identifier";
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
||||
#pragma mark - ORKSESQuestionResult
|
||||
|
||||
@@ -28,15 +28,10 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import <ResearchKit/ORKQuestionResult.h>
|
||||
#endif
|
||||
|
||||
#import <MapKit/MapKit.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ORKQuestionResult ()
|
||||
@@ -49,9 +44,10 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@end
|
||||
|
||||
#if ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION
|
||||
#if ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION && TARGET_OS_IOS
|
||||
@interface ORKLocation ()
|
||||
|
||||
|
||||
- (instancetype)initWithCoordinate:(CLLocationCoordinate2D)coordinate
|
||||
region:(nullable CLCircularRegion *)region
|
||||
userInput:(nullable NSString *)userInput
|
||||
@@ -60,5 +56,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
- (instancetype)initWithPlacemark:(CLPlacemark *)placemark userInput:(NSString *)userInput;
|
||||
|
||||
@end
|
||||
#endif
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -29,11 +29,7 @@
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import <ResearchKit/ORKTypes.h>
|
||||
#endif
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
@@ -28,10 +28,7 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import <ResearchKit/ORKResult.h>
|
||||
#endif
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
@@ -28,12 +28,8 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#if TARGET_OS_IOS
|
||||
#import <ResearchKit/ORKDefines.h>
|
||||
#endif
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
@@ -30,11 +30,7 @@
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import <ResearchKit/ORKSkin.h>
|
||||
#endif
|
||||
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
|
||||
|
||||
@@ -28,13 +28,9 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import <ResearchKit/ORKTypes.h>
|
||||
#endif
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <ResearchKit/ORKTypes.h>
|
||||
|
||||
@class HKObjectType;
|
||||
@class ORKResult;
|
||||
@@ -252,7 +248,7 @@ ORK_CLASS_AVAILABLE API_AVAILABLE(ios(11.0), watchos(6.0))
|
||||
|
||||
#pragma mark - iOS
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#if TARGET_OS_IOS || TARGET_OS_VISION
|
||||
|
||||
@class ORKBodyItem;
|
||||
|
||||
@@ -313,4 +309,7 @@ API_AVAILABLE(ios(11))
|
||||
#endif
|
||||
|
||||
|
||||
#pragma mark - watchOS / VisionOS
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -28,10 +28,7 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import <ResearchKit/ORKStep.h>
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
@@ -35,11 +35,7 @@
|
||||
#import <HealthKit/HealthKit.h>
|
||||
#endif
|
||||
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import <ResearchKit/ORKTypes.h>
|
||||
#endif
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
@@ -30,11 +30,7 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import <ResearchKit/ORKDefines.h>
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
@@ -87,12 +87,14 @@ ORK_CLASS_AVAILABLE
|
||||
*/
|
||||
@property (nonatomic, getter=isAudioMute) BOOL audioMute;
|
||||
|
||||
#if !TARGET_OS_VISION
|
||||
/**
|
||||
Constants indicating the desired torch mode to use
|
||||
|
||||
The default value is `AVCaptureTorchModeAuto` (see `AVCaptureTorchMode`).
|
||||
*/
|
||||
@property (nonatomic) AVCaptureTorchMode torchMode;
|
||||
#endif
|
||||
|
||||
/**
|
||||
Constants indicating the physical position of an AVCaptureDevice's hardware on the system.
|
||||
|
||||
@@ -63,8 +63,10 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@optional
|
||||
- (UIView<ORKCustomSignatureAccessoryViewProtocol> * _Nullable)customHeaderViewForSignatureContent;
|
||||
|
||||
#if !TARGET_OS_VISION
|
||||
@optional
|
||||
- (UIScrollViewKeyboardDismissMode)keyboardDismissModeForCustomView;
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
|
||||
+27
-26
@@ -29,26 +29,42 @@
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ORKDefines.h>
|
||||
#import <ResearchKit/ORKTypes.h>
|
||||
|
||||
#import <ResearchKit/ORKStep.h>
|
||||
#import <ResearchKit/ORKActiveStep.h>
|
||||
#import <ResearchKit/ORKAnswerFormat.h>
|
||||
#import <ResearchKit/ORKCollectionResult.h>
|
||||
#import <ResearchKit/ORKCompletionStep.h>
|
||||
#import <ResearchKit/ORKFormItemVisibilityRule.h>
|
||||
#import <ResearchKit/ORKFormStep.h>
|
||||
#import <ResearchKit/ORKInstructionStep.h>
|
||||
#import <ResearchKit/ORKLearnMoreInstructionStep.h>
|
||||
#import <ResearchKit/ORKLearnMoreItem.h>
|
||||
#import <ResearchKit/ORKOrderedTask.h>
|
||||
#import <ResearchKit/ORKQuestionResult.h>
|
||||
#import <ResearchKit/ORKQuestionStep.h>
|
||||
#import <ResearchKit/ORKResult.h>
|
||||
#import <ResearchKit/ORKSkin.h>
|
||||
#import <ResearchKit/ORKStep.h>
|
||||
#import <ResearchKit/ORKTask.h>
|
||||
#import <ResearchKit/ORKTypes.h>
|
||||
#import <ResearchKit/ORKResultPredicate.h>
|
||||
#import <ResearchKit/ORKBodyItem.h>
|
||||
#import <ResearchKit/ORKEarlyTerminationConfiguration.h>
|
||||
#import <ResearchKit/ORKNavigableOrderedTask.h>
|
||||
#import <ResearchKit/ORKStepNavigationRule.h>
|
||||
|
||||
// Import these files for iOS only.
|
||||
#if !TARGET_OS_WATCH && !TARGET_OS_VISION
|
||||
#import <ResearchKit/ORKDefines.h>
|
||||
#import <ResearchKit/ORKActiveStep.h>
|
||||
#import <ResearchKit/ORKConsentReviewStep.h>
|
||||
#import <ResearchKit/ORKConsentSharingStep.h>
|
||||
#import <ResearchKit/ORKFormStep.h>
|
||||
#import <ResearchKit/ORKFormItemVisibilityRule.h>
|
||||
#import <ResearchKit/ORKHTMLPDFWriter.h>
|
||||
#import <ResearchKit/ORKImageCaptureStep.h>
|
||||
#import <ResearchKit/ORKInstructionStep.h>
|
||||
#import <ResearchKit/ORKLoginStep.h>
|
||||
#import <ResearchKit/ORKNavigablePageStep.h>
|
||||
#import <ResearchKit/ORKPageStep.h>
|
||||
#import <ResearchKit/ORKPasscodeStep.h>
|
||||
#import <ResearchKit/ORKPDFViewerStep.h>
|
||||
#import <ResearchKit/ORKPredicateFormItemVisibilityRule.h>
|
||||
#import <ResearchKit/ORKQuestionStep.h>
|
||||
#import <ResearchKit/ORKRegistrationStep.h>
|
||||
#import <ResearchKit/ORKReviewStep.h>
|
||||
#import <ResearchKit/ORKSignatureFormatter.h>
|
||||
@@ -61,24 +77,15 @@
|
||||
#import <ResearchKit/ORKFrontFacingCameraStep.h>
|
||||
#import <ResearchKit/ORKWebViewStep.h>
|
||||
#import <ResearchKit/ORKCustomStep.h>
|
||||
#import <ResearchKit/ORKLearnMoreInstructionStep.h>
|
||||
#import <ResearchKit/ORKSecondaryTaskStep.h>
|
||||
#import <ResearchKit/ORKRequestPermissionsStep.h>
|
||||
|
||||
#import <ResearchKit/ORKTask.h>
|
||||
#import <ResearchKit/ORKOrderedTask.h>
|
||||
#import <ResearchKit/ORKNavigableOrderedTask.h>
|
||||
#import <ResearchKit/ORKStepNavigationRule.h>
|
||||
|
||||
#import <ResearchKit/ORKAnswerFormat.h>
|
||||
#import <ResearchKit/ORKHealthAnswerFormat.h>
|
||||
|
||||
#import <ResearchKit/ORKResult.h>
|
||||
#import <ResearchKit/ORKCollectionResult.h>
|
||||
#import <ResearchKit/ORKConsentSignatureResult.h>
|
||||
#import <ResearchKit/ORKFrontFacingCameraStepResult.h>
|
||||
#import <ResearchKit/ORKPasscodeResult.h>
|
||||
#import <ResearchKit/ORKQuestionResult.h>
|
||||
#import <ResearchKit/ORKSignatureResult.h>
|
||||
#import <ResearchKit/ORKVideoInstructionStepResult.h>
|
||||
#import <ResearchKit/ORKWebViewStepResult.h>
|
||||
@@ -92,10 +99,6 @@
|
||||
#import <ResearchKit/ORKConsentSignature.h>
|
||||
|
||||
#import <ResearchKit/ORKKeychainWrapper.h>
|
||||
|
||||
#import <ResearchKit/ORKBodyItem.h>
|
||||
#import <ResearchKit/ORKLearnMoreItem.h>
|
||||
|
||||
#import <ResearchKit/ORKDataCollectionManager.h>
|
||||
#import <ResearchKit/ORKCollector.h>
|
||||
|
||||
@@ -107,11 +110,7 @@
|
||||
#import <ResearchKit/ORKLocationPermissionType.h>
|
||||
|
||||
#import <ResearchKit/ORKDeprecated.h>
|
||||
|
||||
#import <ResearchKit/ORKEarlyTerminationConfiguration.h>
|
||||
#import <ResearchKit/ORKBundleAsset.h>
|
||||
#import <ResearchKit/ORKSkin.h>
|
||||
|
||||
#import <ResearchKit/HKSample+ORKJSONDictionary.h>
|
||||
#import <ResearchKit/CMMotionActivity+ORKJSONDictionary.h>
|
||||
|
||||
@@ -122,3 +121,5 @@
|
||||
#import <ResearchKit/ORKHealthCondition.h>
|
||||
#import <ResearchKit/ORKRelatedPerson.h>
|
||||
#import <ResearchKit/ORKRelativeGroup.h>
|
||||
|
||||
#endif
|
||||
|
||||
@@ -28,30 +28,35 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import <ResearchKit/CLLocationManager+ResearchKit.h>
|
||||
#import <ResearchKit/ORKActiveStep_Internal.h>
|
||||
#import <ResearchKit/ORKAnswerFormat_Internal.h>
|
||||
#import <ResearchKit/ORKAnswerFormat_Private.h>
|
||||
#import <ResearchKit/ORKBodyItem_Internal.h>
|
||||
#import <ResearchKit/ORKChoiceAnswerFormatHelper.h>
|
||||
#import <ResearchKit/ORKCollectionResult_Private.h>
|
||||
#import <ResearchKit/ORKErrors.h>
|
||||
#import <ResearchKit/ORKHelpers_Internal.h>
|
||||
#import <ResearchKit/ORKHelpers_Private.h>
|
||||
#import <ResearchKit/ORKQuestionResult_Private.h>
|
||||
#import <ResearchKit/ORKQuestionStep_Private.h>
|
||||
#import <ResearchKit/ORKResult_Private.h>
|
||||
#import <ResearchKit/ORKStep_Private.h>
|
||||
#import <ResearchKit/ORKDevice.h>
|
||||
#import <ResearchKit/ORKStepNavigationRule_Private.h>
|
||||
|
||||
#if !TARGET_OS_WATCH && !TARGET_OS_VISION
|
||||
#import <ResearchKit/CLLocationManager+ResearchKit.h>
|
||||
#import <ResearchKit/ORKActiveStep_Internal.h>
|
||||
#import <ResearchKit/ORKConsentDocument_Private.h>
|
||||
#import <ResearchKit/ORKConsentSection_Private.h>
|
||||
#import <ResearchKit/ORKDataLogger.h>
|
||||
#import <ResearchKit/ORKDevice_Private.h>
|
||||
#import <ResearchKit/ORKErrors.h>
|
||||
#import <ResearchKit/ORKHelpers_Internal.h>
|
||||
#import <ResearchKit/ORKHelpers_Private.h>
|
||||
#import <ResearchKit/ORKOrderedTask_Private.h>
|
||||
#import <ResearchKit/ORKPageStep_Private.h>
|
||||
#import <ResearchKit/ORKPredicateFormItemVisibilityRule_Private.h>
|
||||
#import <ResearchKit/ORKQuestionResult_Private.h>
|
||||
#import <ResearchKit/ORKQuestionStep_Private.h>
|
||||
#import <ResearchKit/ORKRecorder_Private.h>
|
||||
#import <ResearchKit/ORKResult_Private.h>
|
||||
#import <ResearchKit/ORKSignatureResult_Private.h>
|
||||
#import <ResearchKit/ORKSkin_Private.h>
|
||||
#import <ResearchKit/ORKStepNavigationRule_Private.h>
|
||||
#import <ResearchKit/ORKStep_Private.h>
|
||||
#import <ResearchKit/ORKTypes_Private.h>
|
||||
#import <ResearchKit/ORKWebViewStepResult_Private.h>
|
||||
|
||||
#endif
|
||||
|
||||
@@ -30,11 +30,7 @@
|
||||
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import <ResearchKit/ORKStep.h>
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
@@ -28,15 +28,15 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import <ResearchKit/ORKAnswerFormat_Internal.h>
|
||||
#import <ResearchKit/ORKAnswerFormat_Private.h>
|
||||
#import <ResearchKit/ORKHelpers_Internal.h>
|
||||
#import <ResearchKit/ORKQuestionStep_Private.h>
|
||||
#import <ResearchKit/ORKQuestionStep.h>
|
||||
#import <ResearchKit/ORKStep_Private.h>
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import "ORKAnswerFormat_Internal.h"
|
||||
#import "ORKAnswerFormat_Private.h"
|
||||
#import "ORKHelpers_Internal.h"
|
||||
#import "ORKLearnMoreItem.h"
|
||||
#import "ORKQuestionStep_Private.h"
|
||||
#import "ORKStep_Private.h"
|
||||
#import "ORKQuestionStep.h"
|
||||
#endif
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
|
||||
@@ -28,12 +28,8 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import <ResearchKit/ORKQuestionStep.h>
|
||||
#import <ResearchKit/ORKAnswerFormat_Private.h>
|
||||
#endif
|
||||
#import <ResearchKit/ORKQuestionStep.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
@@ -0,0 +1,851 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 70;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
0BF86B222B4F360000E3790E /* ResearchKitSwiftUI.docc in Sources */ = {isa = PBXBuildFile; fileRef = 0BF86B212B4F360000E3790E /* ResearchKitSwiftUI.docc */; };
|
||||
380E14B02C3350A900085D2B /* ResearchForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 380E14AF2C3350A900085D2B /* ResearchForm.swift */; };
|
||||
3815C0142C2081CE00A29CD3 /* DateTimeQuestion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3815C0132C2081CE00A29CD3 /* DateTimeQuestion.swift */; };
|
||||
381CAC0A2C35E06500FCE89A /* ResearchFormStepContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 381CAC092C35E05A00FCE89A /* ResearchFormStepContentView.swift */; };
|
||||
381CAC0C2C35F4CE00FCE89A /* ResearchFormCompletion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 381CAC0B2C35F4C300FCE89A /* ResearchFormCompletion.swift */; };
|
||||
381CAC102C3C759200FCE89A /* HeightQuestion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 381CAC0F2C3C759200FCE89A /* HeightQuestion.swift */; };
|
||||
3820BE212C6574C00012CB2C /* ResearchFormResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3820BE1F2C6574350012CB2C /* ResearchFormResult.swift */; };
|
||||
3820BE222C6574C50012CB2C /* StepResultKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3820BE202C6574640012CB2C /* StepResultKey.swift */; };
|
||||
382CF7852C3F2632000F3433 /* WeightQuestion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 382CF7842C3F262F000F3433 /* WeightQuestion.swift */; };
|
||||
3858D2662C90EF1700F334EB /* ResultValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3858D2652C90EF1500F334EB /* ResultValue.swift */; };
|
||||
389694182C6E7622007BA17D /* MeasurementUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 389694172C6E7621007BA17D /* MeasurementUtility.swift */; };
|
||||
38CD26102C1909CC000FDBB0 /* Question.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38CD260F2C1909CC000FDBB0 /* Question.swift */; };
|
||||
38CD263B2C1BA6EA000FDBB0 /* TextQuestion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38CD263A2C1BA6EA000FDBB0 /* TextQuestion.swift */; };
|
||||
38DB82E32C753A8B0074B95D /* ResearchKitSwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0BF86B1D2B4F360000E3790E /* ResearchKitSwiftUI.framework */; platformFilters = (ios, xros, ); };
|
||||
38F4F6C62C4AD35C004E7A8D /* ImageChoiceQuestion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38F4F6C52C4AD35C004E7A8D /* ImageChoiceQuestion.swift */; };
|
||||
5E05B53D2C2E135D00A0A83B /* DoneKeyboardToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E05B53C2C2E135D00A0A83B /* DoneKeyboardToolbar.swift */; };
|
||||
5E44D4482C24A6FF0045007E /* NumericQuestion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E44D4472C24A6FF0045007E /* NumericQuestion.swift */; };
|
||||
5E507F812C1141F2002958E2 /* SliderValueForegroundStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E507F802C1141F2002958E2 /* SliderValueForegroundStyle.swift */; };
|
||||
5EA939252CD18B01001060CD /* ResearchFormStep.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EA939242CD18AFB001060CD /* ResearchFormStep.swift */; };
|
||||
5EAFC71C2C7549BD008560D7 /* NavigationalLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EAFC71B2C7549B9008560D7 /* NavigationalLayout.swift */; };
|
||||
5EBAA28D2C40634800F193C6 /* BodyItemIconForegroundStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EBAA28C2C40634800F193C6 /* BodyItemIconForegroundStyle.swift */; };
|
||||
5EBAA28F2C4063C100F193C6 /* StepIconForegroundStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EBAA28E2C4063C100F193C6 /* StepIconForegroundStyle.swift */; };
|
||||
5ED16E272C5AF7E50072016D /* InstructionBodyItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5ED16E262C5AF7DF0072016D /* InstructionBodyItem.swift */; };
|
||||
5ED33A372CAE1EB900926DA3 /* TextChoice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5ED33A362CAE1EB600926DA3 /* TextChoice.swift */; };
|
||||
5EDFA9D72BFFFA2B0044C156 /* StepHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EDFA9D62BFFFA2B0044C156 /* StepHeader.swift */; };
|
||||
5EE5FD312C6A8486004BBABF /* StateManagementType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EE5FD302C6A8482004BBABF /* StateManagementType.swift */; };
|
||||
5EFC55352C9B9C4D002C9587 /* QuestionCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EFC55342C9B9C4D002C9587 /* QuestionCard.swift */; };
|
||||
5EFC55742C9DEFC0002C9587 /* KeyboardUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EFC55732C9DEF9E002C9587 /* KeyboardUtilities.swift */; };
|
||||
69EE51532C77F1B8008B2AD6 /* QuestionRequired.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69EE51522C77F1B8008B2AD6 /* QuestionRequired.swift */; };
|
||||
E46F44CF2C66BF25009E5762 /* MultipleChoiceQuestion.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4DBF1B52B97C2AC0085D6A1 /* MultipleChoiceQuestion.swift */; };
|
||||
E473AC712CB0AFC700530CB1 /* ResearchKitSwiftUI.strings in Resources */ = {isa = PBXBuildFile; fileRef = E473AC6F2CB0AFBE00530CB1 /* ResearchKitSwiftUI.strings */; };
|
||||
E4DBF1B92B97C3060085D6A1 /* TextChoiceOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4DBF1B82B97C3030085D6A1 /* TextChoiceOption.swift */; };
|
||||
E4DBF1BF2BA24AA40085D6A1 /* StickyScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4DBF1BE2BA24AA10085D6A1 /* StickyScrollView.swift */; };
|
||||
E4DBF1C12BA24B190085D6A1 /* StickyFooterLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4DBF1C02BA24B150085D6A1 /* StickyFooterLayout.swift */; };
|
||||
E4DBF1C32BA24B8B0085D6A1 /* StickyScrollView+StickyLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4DBF1C22BA24B860085D6A1 /* StickyScrollView+StickyLayout.swift */; };
|
||||
E4E0AFCA2C5B12B40082ADF8 /* Color+ResearchFormColors.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E0AFC92C5B12B40082ADF8 /* Color+ResearchFormColors.swift */; };
|
||||
E4FBFEDD2C6C0D0500F50946 /* SliderQuestion.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA32DE9E2BCF34440041A937 /* SliderQuestion.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
38DB82E42C753A8B0074B95D /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 0BF86B142B4F360000E3790E /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 0BF86B1C2B4F360000E3790E;
|
||||
remoteInfo = "";
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
0B72132F2B4F36B900463E69 /* ResearchKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ResearchKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0B7213392B4F379500463E69 /* ResearchKitUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ResearchKitUI.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0B72133C2B4F37B300463E69 /* ResearchKitActiveTask.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ResearchKitActiveTask.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0B7213542B4F3A8400463E69 /* ResearchKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ResearchKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0B7213572B4F3ABD00463E69 /* ResearchKitActiveTask.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ResearchKitActiveTask.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0B7213612B4F3C7F00463E69 /* ResearchKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ResearchKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0B7213672B4F3CB200463E69 /* ResearchKitUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ResearchKitUI.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0BF86B1D2B4F360000E3790E /* ResearchKitSwiftUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ResearchKitSwiftUI.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0BF86B212B4F360000E3790E /* ResearchKitSwiftUI.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = ResearchKitSwiftUI.docc; sourceTree = "<group>"; };
|
||||
380E14AF2C3350A900085D2B /* ResearchForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResearchForm.swift; sourceTree = "<group>"; };
|
||||
3815C0132C2081CE00A29CD3 /* DateTimeQuestion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateTimeQuestion.swift; sourceTree = "<group>"; };
|
||||
381CAC092C35E05A00FCE89A /* ResearchFormStepContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResearchFormStepContentView.swift; sourceTree = "<group>"; };
|
||||
381CAC0B2C35F4C300FCE89A /* ResearchFormCompletion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResearchFormCompletion.swift; sourceTree = "<group>"; };
|
||||
381CAC0F2C3C759200FCE89A /* HeightQuestion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeightQuestion.swift; sourceTree = "<group>"; };
|
||||
3820BE1F2C6574350012CB2C /* ResearchFormResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResearchFormResult.swift; sourceTree = "<group>"; };
|
||||
3820BE202C6574640012CB2C /* StepResultKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepResultKey.swift; sourceTree = "<group>"; };
|
||||
382CF7842C3F262F000F3433 /* WeightQuestion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeightQuestion.swift; sourceTree = "<group>"; };
|
||||
3858D2652C90EF1500F334EB /* ResultValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultValue.swift; sourceTree = "<group>"; };
|
||||
389694172C6E7621007BA17D /* MeasurementUtility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeasurementUtility.swift; sourceTree = "<group>"; };
|
||||
38CD260F2C1909CC000FDBB0 /* Question.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Question.swift; sourceTree = "<group>"; };
|
||||
38CD263A2C1BA6EA000FDBB0 /* TextQuestion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextQuestion.swift; sourceTree = "<group>"; };
|
||||
38DB82DF2C753A8B0074B95D /* ResearchKitSwiftUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ResearchKitSwiftUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
38F4F6C52C4AD35C004E7A8D /* ImageChoiceQuestion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageChoiceQuestion.swift; sourceTree = "<group>"; };
|
||||
5E05B53C2C2E135D00A0A83B /* DoneKeyboardToolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DoneKeyboardToolbar.swift; sourceTree = "<group>"; };
|
||||
5E44D4472C24A6FF0045007E /* NumericQuestion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumericQuestion.swift; sourceTree = "<group>"; };
|
||||
5E507F802C1141F2002958E2 /* SliderValueForegroundStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SliderValueForegroundStyle.swift; sourceTree = "<group>"; };
|
||||
5E67B9712CA765540062A0F6 /* ResearchKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ResearchKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
5E7596BC2BE049B90088F7B7 /* ResearchKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ResearchKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
5EA939242CD18AFB001060CD /* ResearchFormStep.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResearchFormStep.swift; sourceTree = "<group>"; };
|
||||
5EAFC71B2C7549B9008560D7 /* NavigationalLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationalLayout.swift; sourceTree = "<group>"; };
|
||||
5EBAA28C2C40634800F193C6 /* BodyItemIconForegroundStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BodyItemIconForegroundStyle.swift; sourceTree = "<group>"; };
|
||||
5EBAA28E2C4063C100F193C6 /* StepIconForegroundStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepIconForegroundStyle.swift; sourceTree = "<group>"; };
|
||||
5ED16E262C5AF7DF0072016D /* InstructionBodyItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstructionBodyItem.swift; sourceTree = "<group>"; };
|
||||
5ED33A362CAE1EB600926DA3 /* TextChoice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextChoice.swift; sourceTree = "<group>"; };
|
||||
5EDFA9D62BFFFA2B0044C156 /* StepHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepHeader.swift; sourceTree = "<group>"; };
|
||||
5EE5FD302C6A8482004BBABF /* StateManagementType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StateManagementType.swift; sourceTree = "<group>"; };
|
||||
5EFC55342C9B9C4D002C9587 /* QuestionCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuestionCard.swift; sourceTree = "<group>"; };
|
||||
5EFC55732C9DEF9E002C9587 /* KeyboardUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardUtilities.swift; sourceTree = "<group>"; };
|
||||
69EE51522C77F1B8008B2AD6 /* QuestionRequired.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuestionRequired.swift; sourceTree = "<group>"; };
|
||||
BA32DE9E2BCF34440041A937 /* SliderQuestion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SliderQuestion.swift; sourceTree = "<group>"; };
|
||||
E473AC6E2CB0AFBE00530CB1 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = ResearchKitSwiftUI.strings; sourceTree = "<group>"; };
|
||||
E49B08D92C73EDD000669012 /* ResearchKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ResearchKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
E4B087862C668EA500EFCE35 /* ResearchKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ResearchKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
E4B0878A2C668EB100EFCE35 /* ResearchKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ResearchKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
E4B0878B2C668EBB00EFCE35 /* ResearchKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ResearchKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
E4DBF1B52B97C2AC0085D6A1 /* MultipleChoiceQuestion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultipleChoiceQuestion.swift; sourceTree = "<group>"; };
|
||||
E4DBF1B82B97C3030085D6A1 /* TextChoiceOption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextChoiceOption.swift; sourceTree = "<group>"; };
|
||||
E4DBF1BE2BA24AA10085D6A1 /* StickyScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StickyScrollView.swift; sourceTree = "<group>"; };
|
||||
E4DBF1C02BA24B150085D6A1 /* StickyFooterLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StickyFooterLayout.swift; sourceTree = "<group>"; };
|
||||
E4DBF1C22BA24B860085D6A1 /* StickyScrollView+StickyLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StickyScrollView+StickyLayout.swift"; sourceTree = "<group>"; };
|
||||
E4E0AFC92C5B12B40082ADF8 /* Color+ResearchFormColors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+ResearchFormColors.swift"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||
38DB82E02C753A8B0074B95D /* ResearchKitSwiftUITests */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = ResearchKitSwiftUITests; sourceTree = "<group>"; };
|
||||
/* End PBXFileSystemSynchronizedRootGroup section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
0BF86B1A2B4F360000E3790E /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
38DB82DC2C753A8B0074B95D /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
38DB82E32C753A8B0074B95D /* ResearchKitSwiftUI.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
0B53DA5F2BD054EE00227126 /* Localization */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E473AC702CB0AFBE00530CB1 /* en.lproj */,
|
||||
);
|
||||
path = Localization;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0B72132E2B4F36B900463E69 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5E67B9712CA765540062A0F6 /* ResearchKit.framework */,
|
||||
E49B08D92C73EDD000669012 /* ResearchKit.framework */,
|
||||
E4B0878B2C668EBB00EFCE35 /* ResearchKit.framework */,
|
||||
E4B0878A2C668EB100EFCE35 /* ResearchKit.framework */,
|
||||
E4B087862C668EA500EFCE35 /* ResearchKit.framework */,
|
||||
5E7596BC2BE049B90088F7B7 /* ResearchKit.framework */,
|
||||
0B7213612B4F3C7F00463E69 /* ResearchKit.framework */,
|
||||
0B7213672B4F3CB200463E69 /* ResearchKitUI.framework */,
|
||||
0B7213542B4F3A8400463E69 /* ResearchKit.framework */,
|
||||
0B7213572B4F3ABD00463E69 /* ResearchKitActiveTask.framework */,
|
||||
0B72132F2B4F36B900463E69 /* ResearchKit.framework */,
|
||||
0B7213392B4F379500463E69 /* ResearchKitUI.framework */,
|
||||
0B72133C2B4F37B300463E69 /* ResearchKitActiveTask.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0BF86B132B4F360000E3790E = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0BF86B1F2B4F360000E3790E /* ResearchKitSwiftUI */,
|
||||
38DB82E02C753A8B0074B95D /* ResearchKitSwiftUITests */,
|
||||
0BF86B1E2B4F360000E3790E /* Products */,
|
||||
0B72132E2B4F36B900463E69 /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0BF86B1E2B4F360000E3790E /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0BF86B1D2B4F360000E3790E /* ResearchKitSwiftUI.framework */,
|
||||
38DB82DF2C753A8B0074B95D /* ResearchKitSwiftUITests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0BF86B1F2B4F360000E3790E /* ResearchKitSwiftUI */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5EA939202CD18A11001060CD /* Form */,
|
||||
5EA9391F2CD18A05001060CD /* Step */,
|
||||
E4DBF1B22B97C27E0085D6A1 /* Questions */,
|
||||
5EA939212CD18A40001060CD /* Instructions */,
|
||||
5EA9391C2CD18997001060CD /* Scrolling */,
|
||||
3820BE1E2C65742D0012CB2C /* Results */,
|
||||
5EA939232CD18AA8001060CD /* Measurement */,
|
||||
5EFC55722C9DEF8F002C9587 /* Keyboard */,
|
||||
5EA939272CD18C58001060CD /* Color */,
|
||||
0B53DA5F2BD054EE00227126 /* Localization */,
|
||||
0BF86B212B4F360000E3790E /* ResearchKitSwiftUI.docc */,
|
||||
);
|
||||
path = ResearchKitSwiftUI;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
3820BE1E2C65742D0012CB2C /* Results */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3858D2652C90EF1500F334EB /* ResultValue.swift */,
|
||||
3820BE202C6574640012CB2C /* StepResultKey.swift */,
|
||||
3820BE1F2C6574350012CB2C /* ResearchFormResult.swift */,
|
||||
);
|
||||
path = Results;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5EA9391C2CD18997001060CD /* Scrolling */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E4DBF1C22BA24B860085D6A1 /* StickyScrollView+StickyLayout.swift */,
|
||||
E4DBF1C02BA24B150085D6A1 /* StickyFooterLayout.swift */,
|
||||
E4DBF1BE2BA24AA10085D6A1 /* StickyScrollView.swift */,
|
||||
);
|
||||
path = Scrolling;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5EA9391D2CD189CF001060CD /* Types */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5EA9392B2CD18DFD001060CD /* Multiple Choice */,
|
||||
381CAC0F2C3C759200FCE89A /* HeightQuestion.swift */,
|
||||
382CF7842C3F262F000F3433 /* WeightQuestion.swift */,
|
||||
5EA939222CD18A89001060CD /* Slider */,
|
||||
38CD263A2C1BA6EA000FDBB0 /* TextQuestion.swift */,
|
||||
3815C0132C2081CE00A29CD3 /* DateTimeQuestion.swift */,
|
||||
5E44D4472C24A6FF0045007E /* NumericQuestion.swift */,
|
||||
38F4F6C52C4AD35C004E7A8D /* ImageChoiceQuestion.swift */,
|
||||
);
|
||||
path = Types;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5EA9391E2CD189DF001060CD /* Optionality */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
69EE51522C77F1B8008B2AD6 /* QuestionRequired.swift */,
|
||||
);
|
||||
path = Optionality;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5EA9391F2CD18A05001060CD /* Step */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
381CAC092C35E05A00FCE89A /* ResearchFormStepContentView.swift */,
|
||||
5EA939242CD18AFB001060CD /* ResearchFormStep.swift */,
|
||||
5EDFA9D62BFFFA2B0044C156 /* StepHeader.swift */,
|
||||
5EBAA28E2C4063C100F193C6 /* StepIconForegroundStyle.swift */,
|
||||
);
|
||||
path = Step;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5EA939202CD18A11001060CD /* Form */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
380E14AF2C3350A900085D2B /* ResearchForm.swift */,
|
||||
381CAC0B2C35F4C300FCE89A /* ResearchFormCompletion.swift */,
|
||||
5EAFC71B2C7549B9008560D7 /* NavigationalLayout.swift */,
|
||||
);
|
||||
path = Form;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5EA939212CD18A40001060CD /* Instructions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5ED16E262C5AF7DF0072016D /* InstructionBodyItem.swift */,
|
||||
5EBAA28C2C40634800F193C6 /* BodyItemIconForegroundStyle.swift */,
|
||||
);
|
||||
path = Instructions;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5EA939222CD18A89001060CD /* Slider */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BA32DE9E2BCF34440041A937 /* SliderQuestion.swift */,
|
||||
5E507F802C1141F2002958E2 /* SliderValueForegroundStyle.swift */,
|
||||
);
|
||||
path = Slider;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5EA939232CD18AA8001060CD /* Measurement */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
389694172C6E7621007BA17D /* MeasurementUtility.swift */,
|
||||
);
|
||||
path = Measurement;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5EA939272CD18C58001060CD /* Color */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E4E0AFC92C5B12B40082ADF8 /* Color+ResearchFormColors.swift */,
|
||||
);
|
||||
path = Color;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5EA939282CD18D68001060CD /* Response Options */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5ED33A362CAE1EB600926DA3 /* TextChoice.swift */,
|
||||
);
|
||||
path = "Response Options";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5EA939292CD18D8F001060CD /* Basic UI Structure */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
38CD260F2C1909CC000FDBB0 /* Question.swift */,
|
||||
5EFC55342C9B9C4D002C9587 /* QuestionCard.swift */,
|
||||
);
|
||||
path = "Basic UI Structure";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5EA9392A2CD18DB7001060CD /* Binding Management */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5EE5FD302C6A8482004BBABF /* StateManagementType.swift */,
|
||||
);
|
||||
path = "Binding Management";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5EA9392B2CD18DFD001060CD /* Multiple Choice */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E4DBF1B52B97C2AC0085D6A1 /* MultipleChoiceQuestion.swift */,
|
||||
E4DBF1B82B97C3030085D6A1 /* TextChoiceOption.swift */,
|
||||
);
|
||||
path = "Multiple Choice";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5EFC55722C9DEF8F002C9587 /* Keyboard */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5EFC55732C9DEF9E002C9587 /* KeyboardUtilities.swift */,
|
||||
5E05B53C2C2E135D00A0A83B /* DoneKeyboardToolbar.swift */,
|
||||
);
|
||||
path = Keyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
E473AC702CB0AFBE00530CB1 /* en.lproj */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E473AC6F2CB0AFBE00530CB1 /* ResearchKitSwiftUI.strings */,
|
||||
);
|
||||
path = en.lproj;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
E4DBF1B22B97C27E0085D6A1 /* Questions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5EA939292CD18D8F001060CD /* Basic UI Structure */,
|
||||
5EA9391D2CD189CF001060CD /* Types */,
|
||||
5EA939282CD18D68001060CD /* Response Options */,
|
||||
5EA9392A2CD18DB7001060CD /* Binding Management */,
|
||||
5EA9391E2CD189DF001060CD /* Optionality */,
|
||||
);
|
||||
path = Questions;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
3810A4032C7D3BA000535B38 /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
0BF86B1C2B4F360000E3790E /* ResearchKitSwiftUI */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 0BF86B262B4F360000E3790E /* Build configuration list for PBXNativeTarget "ResearchKitSwiftUI" */;
|
||||
buildPhases = (
|
||||
3810A4032C7D3BA000535B38 /* Headers */,
|
||||
0BF86B192B4F360000E3790E /* Sources */,
|
||||
0BF86B1A2B4F360000E3790E /* Frameworks */,
|
||||
0BF86B1B2B4F360000E3790E /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = ResearchKitSwiftUI;
|
||||
productName = ResearchKitSwiftUI;
|
||||
productReference = 0BF86B1D2B4F360000E3790E /* ResearchKitSwiftUI.framework */;
|
||||
productType = "com.apple.product-type.framework";
|
||||
};
|
||||
38DB82DE2C753A8B0074B95D /* ResearchKitSwiftUITests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 38DB82E82C753A8B0074B95D /* Build configuration list for PBXNativeTarget "ResearchKitSwiftUITests" */;
|
||||
buildPhases = (
|
||||
38DB82DB2C753A8B0074B95D /* Sources */,
|
||||
38DB82DC2C753A8B0074B95D /* Frameworks */,
|
||||
38DB82DD2C753A8B0074B95D /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
38DB82E52C753A8B0074B95D /* PBXTargetDependency */,
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
38DB82E02C753A8B0074B95D /* ResearchKitSwiftUITests */,
|
||||
);
|
||||
name = ResearchKitSwiftUITests;
|
||||
packageProductDependencies = (
|
||||
);
|
||||
productName = ResearchKitSwiftUITests;
|
||||
productReference = 38DB82DF2C753A8B0074B95D /* ResearchKitSwiftUITests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
0BF86B142B4F360000E3790E /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = 1;
|
||||
LastSwiftUpdateCheck = 1600;
|
||||
LastUpgradeCheck = 1510;
|
||||
TargetAttributes = {
|
||||
0BF86B1C2B4F360000E3790E = {
|
||||
CreatedOnToolsVersion = 15.1;
|
||||
};
|
||||
38DB82DE2C753A8B0074B95D = {
|
||||
CreatedOnToolsVersion = 16.0;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 0BF86B172B4F360000E3790E /* Build configuration list for PBXProject "ResearchKitSwiftUI" */;
|
||||
compatibilityVersion = "Xcode 15.0";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 0BF86B132B4F360000E3790E;
|
||||
productRefGroup = 0BF86B1E2B4F360000E3790E /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
0BF86B1C2B4F360000E3790E /* ResearchKitSwiftUI */,
|
||||
38DB82DE2C753A8B0074B95D /* ResearchKitSwiftUITests */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
0BF86B1B2B4F360000E3790E /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
E473AC712CB0AFC700530CB1 /* ResearchKitSwiftUI.strings in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
38DB82DD2C753A8B0074B95D /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
0BF86B192B4F360000E3790E /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
E46F44CF2C66BF25009E5762 /* MultipleChoiceQuestion.swift in Sources */,
|
||||
69EE51532C77F1B8008B2AD6 /* QuestionRequired.swift in Sources */,
|
||||
381CAC0C2C35F4CE00FCE89A /* ResearchFormCompletion.swift in Sources */,
|
||||
E4DBF1C32BA24B8B0085D6A1 /* StickyScrollView+StickyLayout.swift in Sources */,
|
||||
E4DBF1C12BA24B190085D6A1 /* StickyFooterLayout.swift in Sources */,
|
||||
5E44D4482C24A6FF0045007E /* NumericQuestion.swift in Sources */,
|
||||
3820BE222C6574C50012CB2C /* StepResultKey.swift in Sources */,
|
||||
E4DBF1BF2BA24AA40085D6A1 /* StickyScrollView.swift in Sources */,
|
||||
E4FBFEDD2C6C0D0500F50946 /* SliderQuestion.swift in Sources */,
|
||||
5EFC55352C9B9C4D002C9587 /* QuestionCard.swift in Sources */,
|
||||
5EBAA28F2C4063C100F193C6 /* StepIconForegroundStyle.swift in Sources */,
|
||||
5EAFC71C2C7549BD008560D7 /* NavigationalLayout.swift in Sources */,
|
||||
5EBAA28D2C40634800F193C6 /* BodyItemIconForegroundStyle.swift in Sources */,
|
||||
5EDFA9D72BFFFA2B0044C156 /* StepHeader.swift in Sources */,
|
||||
5E507F812C1141F2002958E2 /* SliderValueForegroundStyle.swift in Sources */,
|
||||
5EA939252CD18B01001060CD /* ResearchFormStep.swift in Sources */,
|
||||
5EFC55742C9DEFC0002C9587 /* KeyboardUtilities.swift in Sources */,
|
||||
E4E0AFCA2C5B12B40082ADF8 /* Color+ResearchFormColors.swift in Sources */,
|
||||
38CD26102C1909CC000FDBB0 /* Question.swift in Sources */,
|
||||
380E14B02C3350A900085D2B /* ResearchForm.swift in Sources */,
|
||||
E4DBF1B92B97C3060085D6A1 /* TextChoiceOption.swift in Sources */,
|
||||
5E05B53D2C2E135D00A0A83B /* DoneKeyboardToolbar.swift in Sources */,
|
||||
3815C0142C2081CE00A29CD3 /* DateTimeQuestion.swift in Sources */,
|
||||
382CF7852C3F2632000F3433 /* WeightQuestion.swift in Sources */,
|
||||
38F4F6C62C4AD35C004E7A8D /* ImageChoiceQuestion.swift in Sources */,
|
||||
38CD263B2C1BA6EA000FDBB0 /* TextQuestion.swift in Sources */,
|
||||
3820BE212C6574C00012CB2C /* ResearchFormResult.swift in Sources */,
|
||||
381CAC102C3C759200FCE89A /* HeightQuestion.swift in Sources */,
|
||||
5ED33A372CAE1EB900926DA3 /* TextChoice.swift in Sources */,
|
||||
381CAC0A2C35E06500FCE89A /* ResearchFormStepContentView.swift in Sources */,
|
||||
0BF86B222B4F360000E3790E /* ResearchKitSwiftUI.docc in Sources */,
|
||||
5EE5FD312C6A8486004BBABF /* StateManagementType.swift in Sources */,
|
||||
3858D2662C90EF1700F334EB /* ResultValue.swift in Sources */,
|
||||
389694182C6E7622007BA17D /* MeasurementUtility.swift in Sources */,
|
||||
5ED16E272C5AF7E50072016D /* InstructionBodyItem.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
38DB82DB2C753A8B0074B95D /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
38DB82E52C753A8B0074B95D /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
platformFilters = (
|
||||
ios,
|
||||
xros,
|
||||
);
|
||||
target = 0BF86B1C2B4F360000E3790E /* ResearchKitSwiftUI */;
|
||||
targetProxy = 38DB82E42C753A8B0074B95D /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
E473AC6F2CB0AFBE00530CB1 /* ResearchKitSwiftUI.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
E473AC6E2CB0AFBE00530CB1 /* en */,
|
||||
);
|
||||
name = ResearchKitSwiftUI.strings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
0BF86B242B4F360000E3790E /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.2;
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = NO;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = xros;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
0BF86B252B4F360000E3790E /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.2;
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = NO;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = xros;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
0BF86B272B4F360000E3790E /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES;
|
||||
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
DRIVERKIT_DEPLOYMENT_TARGET = 20.0;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
ENABLE_MODULE_VERIFIER = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = "";
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
MARKETING_VERSION = 1.0;
|
||||
MODULEMAP_FILE = "";
|
||||
MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
|
||||
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20";
|
||||
MODULE_VERIFIER_VERBOSE = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.researchkit.ResearchKitSwiftUI;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SDKROOT = iphoneos;
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator watchos watchsimulator xros xrsimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2,4,7";
|
||||
WATCHOS_DEPLOYMENT_TARGET = 11.0;
|
||||
XROS_DEPLOYMENT_TARGET = 2.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
0BF86B282B4F360000E3790E /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES;
|
||||
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
DRIVERKIT_DEPLOYMENT_TARGET = 20.0;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
ENABLE_MODULE_VERIFIER = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = "";
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
MARKETING_VERSION = 1.0;
|
||||
MODULEMAP_FILE = "";
|
||||
MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
|
||||
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20";
|
||||
MODULE_VERIFIER_VERBOSE = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.researchkit.ResearchKitSwiftUI;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SDKROOT = iphoneos;
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator watchos watchsimulator xros xrsimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2,4,7";
|
||||
WATCHOS_DEPLOYMENT_TARGET = 11.0;
|
||||
XROS_DEPLOYMENT_TARGET = 2.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
38DB82E62C753A8B0074B95D /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 15.0;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = come.appleinternal.ResearchKitSwiftUITests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SDKROOT = auto;
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator";
|
||||
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2,7";
|
||||
XROS_DEPLOYMENT_TARGET = 2.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
38DB82E72C753A8B0074B95D /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 15.0;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = come.appleinternal.ResearchKitSwiftUITests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SDKROOT = auto;
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator";
|
||||
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2,7";
|
||||
XROS_DEPLOYMENT_TARGET = 2.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
0BF86B172B4F360000E3790E /* Build configuration list for PBXProject "ResearchKitSwiftUI" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
0BF86B242B4F360000E3790E /* Debug */,
|
||||
0BF86B252B4F360000E3790E /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
0BF86B262B4F360000E3790E /* Build configuration list for PBXNativeTarget "ResearchKitSwiftUI" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
0BF86B272B4F360000E3790E /* Debug */,
|
||||
0BF86B282B4F360000E3790E /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
38DB82E82C753A8B0074B95D /* Build configuration list for PBXNativeTarget "ResearchKitSwiftUITests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
38DB82E62C753A8B0074B95D /* Debug */,
|
||||
38DB82E72C753A8B0074B95D /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 0BF86B142B4F360000E3790E /* Project object */;
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1600"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES"
|
||||
buildArchitectures = "Automatic">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0BF86B1C2B4F360000E3790E"
|
||||
BuildableName = "ResearchKitSwiftUI.framework"
|
||||
BlueprintName = "ResearchKitSwiftUI"
|
||||
ReferencedContainer = "container:ResearchKitSwiftUI.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0BF86B1C2B4F360000E3790E"
|
||||
BuildableName = "ResearchKitSwiftUI.framework"
|
||||
BlueprintName = "ResearchKitSwiftUI"
|
||||
ReferencedContainer = "container:ResearchKitSwiftUI.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1600"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES"
|
||||
buildArchitectures = "Automatic">
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO"
|
||||
parallelizable = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "38DB82DE2C753A8B0074B95D"
|
||||
BuildableName = "ResearchKitSwiftUITests.xctest"
|
||||
BlueprintName = "ResearchKitSwiftUITests"
|
||||
ReferencedContainer = "container:ResearchKitSwiftUI.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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 Foundation
|
||||
import SwiftUI
|
||||
|
||||
extension Color {
|
||||
public enum ColorChoice {
|
||||
case background
|
||||
case secondaryBackground
|
||||
case label
|
||||
case systemGray4
|
||||
case systemGray5
|
||||
}
|
||||
|
||||
public static func choice(for choice: ColorChoice) -> Color {
|
||||
switch choice {
|
||||
case .background:
|
||||
#if os(watchOS)
|
||||
return Color.primary
|
||||
#else
|
||||
return Color(uiColor: UIColor.systemBackground)
|
||||
#endif
|
||||
|
||||
case .secondaryBackground:
|
||||
#if os(watchOS)
|
||||
return Color.secondary
|
||||
#else
|
||||
return Color(uiColor: UIColor.secondarySystemBackground)
|
||||
#endif
|
||||
|
||||
case .label:
|
||||
#if os(watchOS)
|
||||
return Color.primary
|
||||
#else
|
||||
return Color(uiColor: UIColor.label)
|
||||
#endif
|
||||
|
||||
case .systemGray4:
|
||||
#if os(watchOS)
|
||||
return Color.secondary.opacity(0.4)
|
||||
#else
|
||||
return Color(uiColor: UIColor.systemGray4)
|
||||
#endif
|
||||
case .systemGray5:
|
||||
#if os(watchOS)
|
||||
return Color.secondary.opacity(0.5)
|
||||
#else
|
||||
return Color(uiColor: UIColor.systemGray5)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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 SwiftUI
|
||||
|
||||
struct NavigationalLayout: View {
|
||||
|
||||
@Environment(\.dismiss) var dismiss
|
||||
|
||||
@State
|
||||
private var stepIdentifiers: [Subview.ID] = []
|
||||
|
||||
@State
|
||||
private var researchFormCompletion: ResearchFormCompletion?
|
||||
|
||||
private let steps: SubviewsCollection
|
||||
private let onResearchFormCompletion: ((ResearchFormCompletion) -> Void)?
|
||||
|
||||
init(
|
||||
_ steps: SubviewsCollection,
|
||||
onResearchFormCompletion: ((ResearchFormCompletion) -> Void)?
|
||||
) {
|
||||
self.steps = steps
|
||||
self.onResearchFormCompletion = onResearchFormCompletion
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
NavigationStack(path: $stepIdentifiers) {
|
||||
if let firstStep = steps.first {
|
||||
ResearchFormStepContentView(
|
||||
isLastStep: isLastStep(for: firstStep)
|
||||
) { completion in
|
||||
switch completion {
|
||||
case .failed, .discarded, .terminated:
|
||||
dismiss()
|
||||
case .completed(let result):
|
||||
handle(formCompletion: completion, with: result)
|
||||
case .saved:
|
||||
if let currentStepIndex = index(for: firstStep) {
|
||||
moveToNextStep(
|
||||
relativeToCurrentIndex: currentStepIndex)
|
||||
}
|
||||
}
|
||||
} content: {
|
||||
firstStep
|
||||
}
|
||||
.navigationTitle("1 of \(steps.count)")
|
||||
.navigationDestination(for: Subview.ID.self) { subviewID in
|
||||
ResearchFormStepContentView(
|
||||
isLastStep: isLastStep(for: subviewID)
|
||||
) { completion in
|
||||
switch completion {
|
||||
case .failed, .discarded, .terminated:
|
||||
dismiss()
|
||||
case .completed(let result):
|
||||
handle(formCompletion: completion, with: result)
|
||||
case .saved:
|
||||
if let currentStepIndex = index(for: subviewID) {
|
||||
moveToNextStep(
|
||||
relativeToCurrentIndex: currentStepIndex)
|
||||
}
|
||||
}
|
||||
} content: {
|
||||
if let currentStepIndex = index(for: subviewID) {
|
||||
steps[currentStepIndex]
|
||||
}
|
||||
}
|
||||
.navigationTitle(navigationTitle(for: subviewID))
|
||||
}
|
||||
}
|
||||
}
|
||||
#if os(watchOS)
|
||||
.preference(
|
||||
key: ResearchFormCompletionKey.self,
|
||||
value: researchFormCompletion)
|
||||
#endif
|
||||
}
|
||||
|
||||
private func handle(
|
||||
formCompletion completion: ResearchFormCompletion,
|
||||
with result: ResearchFormResult
|
||||
) {
|
||||
if let onResearchFormCompletion {
|
||||
#if os(watchOS)
|
||||
researchFormCompletion = .completed(result)
|
||||
#endif
|
||||
onResearchFormCompletion(completion)
|
||||
} else {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
private func moveToNextStep(relativeToCurrentIndex currentIndex: Int) {
|
||||
let nextStepIndex = currentIndex + 1
|
||||
if nextStepIndex < steps.count {
|
||||
stepIdentifiers.append(steps[nextStepIndex].id)
|
||||
}
|
||||
}
|
||||
|
||||
private func isLastStep(for subview: Subview) -> Bool {
|
||||
isLastStep(for: subview.id)
|
||||
}
|
||||
|
||||
private func isLastStep(for id: Subview.ID) -> Bool {
|
||||
steps.firstIndex(where: { $0.id == id }) == steps.count - 1
|
||||
}
|
||||
|
||||
private func index(for subview: Subview) -> Int? {
|
||||
index(for: subview.id)
|
||||
}
|
||||
|
||||
private func index(for id: Subview.ID) -> Int? {
|
||||
steps.firstIndex { step in
|
||||
step.id == id
|
||||
}
|
||||
}
|
||||
|
||||
private func navigationTitle(for id: Subview.ID) -> String {
|
||||
let navigationTitle: String
|
||||
if let index = index(for: id) {
|
||||
navigationTitle = "\(index + 1) of \(steps.count)"
|
||||
} else {
|
||||
navigationTitle = ""
|
||||
}
|
||||
return navigationTitle
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if os(watchOS)
|
||||
struct ResearchFormCompletionKey: PreferenceKey {
|
||||
|
||||
static var defaultValue: ResearchFormCompletion? = nil
|
||||
|
||||
static func reduce(
|
||||
value: inout ResearchFormCompletion?,
|
||||
nextValue: () -> ResearchFormCompletion?
|
||||
) {
|
||||
value = nextValue()
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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 SwiftUI
|
||||
|
||||
/// Manages the navigation between steps in a survey.
|
||||
///
|
||||
/// To add steps to a `ResearchForm`, create instances of ``ResearchFormStep`` and pass them into a `ResearchForm`. To display questions in each step, ResearchKit provides various question formats that can be used within a ``ResearchFormStep`` such as ``MultipleChoiceQuestion``, ``DateTimeQuestion``, and more. These questions can be marked as optional or required, which a `ResearchForm` tracks as part of navigation management.
|
||||
///
|
||||
/// Additionally, a `ResearchForm` manages survey results for questions that manage their own bindings internally. Results are passed through a ``ResearchFormResult`` once a survey is completed. For instance, a text question for which no binding is provided has its result stored in ``ResearchFormResult``. However, a text question that has been provided a binding does not have its result stored in ``ResearchFormResult``, and in this case, you are expected to manage the result for the text question.
|
||||
public struct ResearchForm<Content: View>: View {
|
||||
@State
|
||||
private var managedFormResult: ResearchFormResult
|
||||
|
||||
#if os(watchOS)
|
||||
@State
|
||||
private var researchFormCompletion: ResearchFormCompletion?
|
||||
#endif
|
||||
|
||||
private let taskKey: StepResultKey<String?>
|
||||
private let steps: Content
|
||||
|
||||
private var onResearchFormCompletion: ((ResearchFormCompletion) -> Void)?
|
||||
|
||||
/// Initializes an instance of ``ResearchForm`` with the provided configuration.
|
||||
/// - Parameters:
|
||||
/// - taskIdentifier: An identifier that uniquely identifies this research form.
|
||||
/// - restorationResult: A result used to pre-populate questions that had previously been answered.
|
||||
/// - steps: The steps in a survey, each of which can contain a set of questions.
|
||||
/// - onResearchFormCompletion: A completion that is triggered when the survey is dismissed.
|
||||
public init(
|
||||
id: String,
|
||||
restorationResult: ResearchFormResult? = nil,
|
||||
@ViewBuilder steps: () -> Content,
|
||||
onResearchFormCompletion: ((ResearchFormCompletion) -> Void)? = nil
|
||||
) {
|
||||
self.taskKey = .text(id: id)
|
||||
self.steps = steps()
|
||||
self.onResearchFormCompletion = onResearchFormCompletion
|
||||
self.managedFormResult = restorationResult ?? ResearchFormResult()
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
Group(subviews: steps) { steps in
|
||||
NavigationalLayout(
|
||||
steps, onResearchFormCompletion: onResearchFormCompletion)
|
||||
}
|
||||
.environmentObject(managedFormResult)
|
||||
.environment(\.questionContext, .formEmbedded)
|
||||
#if os(watchOS)
|
||||
// On the watch, an x button is automatically added to the top left of the screen when presenting content, so we have
|
||||
// to remove the cancel button, which had invoked `onResearchFormCompletion` with a completion of `discarded`.
|
||||
//
|
||||
// Here, we track the completions that come in, and in `onDisappear`, we invoke the completion with the `discarded`
|
||||
// state if no completion was ever set. This helps with passing through the discarded state even when there is
|
||||
// no cancel button on the watch.
|
||||
.onPreferenceChange(
|
||||
ResearchFormCompletionKey.self,
|
||||
perform: { researchFormCompletion in
|
||||
self.researchFormCompletion = researchFormCompletion
|
||||
}
|
||||
)
|
||||
.onDisappear {
|
||||
if researchFormCompletion == nil {
|
||||
onResearchFormCompletion?(.discarded)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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.
|
||||
*/
|
||||
|
||||
/// Represents the context for a survey's completion.
|
||||
public enum ResearchFormCompletion {
|
||||
/// The survey was dismissed with the intent to save question responses.
|
||||
case saved(ResearchFormResult)
|
||||
/// The survey was dismissed with the intent to discard question responses.
|
||||
case discarded
|
||||
/// The survey was completed, and question responses are provided.
|
||||
case completed(ResearchFormResult)
|
||||
/// The survey was dismissed in a failed manner.
|
||||
case failed
|
||||
/// The survey was terminated.
|
||||
case terminated
|
||||
}
|
||||
|
||||
extension ResearchFormCompletion: Equatable {
|
||||
|
||||
public static func == (
|
||||
lhs: ResearchFormCompletion, rhs: ResearchFormCompletion
|
||||
) -> Bool {
|
||||
let isEqual: Bool
|
||||
|
||||
switch (lhs, rhs) {
|
||||
case let (.saved(lhsValue), .saved(rhsValue)):
|
||||
isEqual = lhsValue == rhsValue
|
||||
case (.discarded, .discarded):
|
||||
isEqual = true
|
||||
case let (.completed(lhsValue), .completed(rhsValue)):
|
||||
isEqual = lhsValue == rhsValue
|
||||
case (.failed, .failed):
|
||||
isEqual = true
|
||||
case (.terminated, .terminated):
|
||||
isEqual = true
|
||||
default:
|
||||
isEqual = false
|
||||
}
|
||||
|
||||
return isEqual
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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 SwiftUI
|
||||
|
||||
extension ShapeStyle where Self == BodyItemIconForegroundStyle {
|
||||
|
||||
/// This foreground style is used for labels that display values associated with sliders.
|
||||
static var bodyItemIconForegroundStyle: BodyItemIconForegroundStyle {
|
||||
BodyItemIconForegroundStyle()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct BodyItemIconForegroundStyle: ShapeStyle {
|
||||
|
||||
func resolve(in environment: EnvironmentValues) -> some ShapeStyle {
|
||||
#if os(visionOS)
|
||||
.white
|
||||
#else
|
||||
Color.accentColor
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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 SwiftUI
|
||||
|
||||
/// Displays an image and text side by side.
|
||||
public struct InstructionBodyItem: View {
|
||||
|
||||
private let image: Image?
|
||||
private let text: Text?
|
||||
|
||||
/// Initializes an instance of ``InstructionBodyItem`` with the provided configuration.
|
||||
/// - Parameters:
|
||||
/// - image: The image to display for this instruction.
|
||||
/// - text: The text to display for this instruction.
|
||||
public init(image: Image? = nil, text: Text? = nil) {
|
||||
self.image = image
|
||||
self.text = text
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
#if os(watchOS)
|
||||
VStack(alignment: .leading) {
|
||||
image?
|
||||
.frame(width: 20, height: 20)
|
||||
.foregroundStyle(.bodyItemIconForegroundStyle)
|
||||
|
||||
text?
|
||||
.font(.subheadline)
|
||||
}
|
||||
.preference(key: QuestionCardPreferenceKey.self, value: false)
|
||||
#else
|
||||
HStack {
|
||||
image?
|
||||
.frame(width: 40, height: 40)
|
||||
.foregroundStyle(.bodyItemIconForegroundStyle)
|
||||
|
||||
text?
|
||||
.font(.subheadline)
|
||||
}
|
||||
.preference(key: QuestionCardPreferenceKey.self, value: false)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
struct QuestionCardPreferenceKey: PreferenceKey {
|
||||
|
||||
static var defaultValue = true
|
||||
|
||||
static func reduce(value: inout Bool, nextValue: () -> Bool) {
|
||||
value = nextValue()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#Preview {
|
||||
ScrollView {
|
||||
ResearchFormStep(
|
||||
image: Image(systemName: "doc.text.magnifyingglass"),
|
||||
title: "Before You Join join"
|
||||
) {
|
||||
InstructionBodyItem(
|
||||
image: Image(systemName: "heart.fill"),
|
||||
text: Text(
|
||||
"The study will ask you to share some of your Health data.")
|
||||
)
|
||||
|
||||
InstructionBodyItem(
|
||||
image: Image(systemName: "checkmark.circle.fill"),
|
||||
text: Text(
|
||||
"You will be asked to complete various tasks over the duration of the study."
|
||||
)
|
||||
)
|
||||
|
||||
InstructionBodyItem(
|
||||
image: Image(systemName: "signature"),
|
||||
text: Text(
|
||||
"Before joining, we will ask you to sign an informed consent document."
|
||||
)
|
||||
)
|
||||
|
||||
InstructionBodyItem(
|
||||
image: Image(systemName: "lock.fill"),
|
||||
text: Text("Your data is kept private and secure.")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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 SwiftUI
|
||||
|
||||
extension View {
|
||||
|
||||
/// Adds a toolbar above the keyboard that contains a done button, which invokes the action passed to this modifier.
|
||||
/// - Parameter condition: The condition under which the keyboard toolbar is shown.
|
||||
/// - Parameter action: The action triggered when the done button in the toolbar is tapped.
|
||||
/// - Returns: The modified view that includes the toolbar when the keyboard is invoked.
|
||||
func doneKeyboardToolbar(
|
||||
condition: @escaping () -> Bool, action: @escaping () -> Void
|
||||
) -> some View {
|
||||
modifier(
|
||||
DoneKeyboardToolbar(condition: condition, action: action)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct DoneKeyboardToolbar: ViewModifier {
|
||||
|
||||
private let condition: () -> Bool
|
||||
private let action: () -> Void
|
||||
|
||||
init(condition: @escaping () -> Bool, action: @escaping () -> Void) {
|
||||
self.condition = condition
|
||||
self.action = action
|
||||
}
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
#if os(iOS)
|
||||
content
|
||||
.toolbar {
|
||||
ToolbarItemGroup(placement: .keyboard) {
|
||||
if condition() {
|
||||
Spacer()
|
||||
|
||||
Button("Done") {
|
||||
action()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#elseif os(visionOS)
|
||||
content
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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 UIKit
|
||||
|
||||
#if !os(watchOS)
|
||||
extension UIApplication {
|
||||
|
||||
func endEditing() {
|
||||
sendAction(
|
||||
#selector(UIResponder.resignFirstResponder), to: nil, from: nil,
|
||||
for: nil)
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
Copyright (c) 2020, 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.
|
||||
*/
|
||||
|
||||
"BUTTON_CANCEL" = "Cancel";
|
||||
"BUTTON_NEXT" = "Next";
|
||||
"BUTTON_DONE" = "Done";
|
||||
"QUESTION_PROGRESS" = "%D OF %D";
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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 SwiftUI
|
||||
import math_h
|
||||
|
||||
func convertCentimetersToFeetAndInches(_ centimeters: Double) -> (
|
||||
feet: Int, inches: Int
|
||||
) {
|
||||
var feet = 0
|
||||
var inches = 0
|
||||
let centimetersToInches = centimeters * 0.393701
|
||||
inches = Int(centimetersToInches)
|
||||
feet = inches / 12
|
||||
inches = inches % 12
|
||||
return (feet, inches)
|
||||
}
|
||||
|
||||
func convertKilogramsToPoundsAndOunces(_ kilograms: Double) -> (
|
||||
pounds: Double, ounces: Double
|
||||
) {
|
||||
let poundPerKilogram = 2.20462262
|
||||
let fractionalPounds = kilograms * poundPerKilogram
|
||||
var pounds = floor(fractionalPounds)
|
||||
var ounces = round((fractionalPounds - pounds) * 16)
|
||||
if ounces == 16 {
|
||||
pounds += 1
|
||||
ounces = 0
|
||||
}
|
||||
return (pounds, ounces)
|
||||
}
|
||||
|
||||
func convertPoundsAndOuncesToKilograms(pounds: Double, ounces: Double) -> Double
|
||||
{
|
||||
let kilogramsPerPound = 0.45359237
|
||||
let kg = (pounds + (ounces / 16)) * kilogramsPerPound
|
||||
return round(kg * 100) / 100
|
||||
}
|
||||
|
||||
func convertKilogramsToWholeAndFraction(_ kilograms: Double) -> (
|
||||
whole: Double, fraction: Double
|
||||
) {
|
||||
let whole = floor(kilograms)
|
||||
let fraction = round((kilograms - floor(kilograms)) * 100)
|
||||
return (whole, fraction)
|
||||
}
|
||||
|
||||
extension Binding {
|
||||
func unwrapped<T>(defaultValue: T) -> Binding<T> where Value == T? {
|
||||
Binding<T>(
|
||||
get: { self.wrappedValue ?? defaultValue },
|
||||
set: { self.wrappedValue = $0 })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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 SwiftUI
|
||||
|
||||
struct Question<Header: View, Content: View>: View {
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
|
||||
private let header: Header
|
||||
private let content: Content
|
||||
|
||||
init(
|
||||
@ViewBuilder header: () -> Header,
|
||||
@ViewBuilder content: () -> Content
|
||||
) {
|
||||
self.header = header()
|
||||
self.content = content()
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: .zero) {
|
||||
header
|
||||
|
||||
Divider()
|
||||
.padding(.horizontal)
|
||||
|
||||
content
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Question where Header == QuestionHeader {
|
||||
init(
|
||||
title: String,
|
||||
detail: String? = nil,
|
||||
content: () -> Content
|
||||
) {
|
||||
self.header = QuestionHeader(title: title, detail: detail)
|
||||
self.content = content()
|
||||
}
|
||||
}
|
||||
|
||||
/// A question header containing a title and detail.
|
||||
public struct QuestionHeader: View {
|
||||
|
||||
private let title: String
|
||||
private let detail: String?
|
||||
|
||||
init(title: String, detail: String? = nil) {
|
||||
self.title = title
|
||||
self.detail = detail
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
titleText(title)
|
||||
|
||||
if let detail {
|
||||
Text(detail)
|
||||
.font(detailFont)
|
||||
}
|
||||
}
|
||||
#if os(watchOS)
|
||||
.padding(.horizontal)
|
||||
.padding(.bottom, 4)
|
||||
.padding(.top, 4)
|
||||
#else
|
||||
.padding()
|
||||
#endif
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func titleText(_ text: String) -> some View {
|
||||
Text(title)
|
||||
.foregroundStyle(Color.choice(for: .label))
|
||||
.fontWeight(.bold)
|
||||
#if os(watchOS)
|
||||
.font(.footnote)
|
||||
#else
|
||||
.font(.body)
|
||||
#endif
|
||||
}
|
||||
|
||||
private var detailFont: Font {
|
||||
#if os(watchOS)
|
||||
.system(size: 12)
|
||||
#else
|
||||
.subheadline
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#Preview("Detail and Title") {
|
||||
VStack {
|
||||
Spacer()
|
||||
Question(title: "What is your name?") {
|
||||
Text("Specific component content will show up here")
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
.padding()
|
||||
.background {
|
||||
Color.choice(for: .secondaryBackground)
|
||||
}
|
||||
.ignoresSafeArea()
|
||||
}
|
||||
|
||||
#Preview("Just title") {
|
||||
VStack {
|
||||
Spacer()
|
||||
Question(title: "What is your name?") {
|
||||
Text("Specific component content will show up here")
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
.padding()
|
||||
.background {
|
||||
Color.choice(for: .secondaryBackground)
|
||||
}
|
||||
.ignoresSafeArea()
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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 SwiftUI
|
||||
|
||||
struct QuestionCard<Content: View>: View {
|
||||
|
||||
@Environment(\.isQuestionCardEnabled)
|
||||
private var isQuestionCardEnabled
|
||||
|
||||
private let content: Content
|
||||
|
||||
init(content: () -> Content) {
|
||||
self.content = content()
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
if isQuestionCardEnabled {
|
||||
content
|
||||
.modifier(
|
||||
QuestionCardModifier()
|
||||
)
|
||||
.questionCardEnabled(false)
|
||||
} else {
|
||||
content
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension EnvironmentValues {
|
||||
|
||||
@Entry var isQuestionCardEnabled = true
|
||||
|
||||
}
|
||||
|
||||
struct QuestionCardModifier: ViewModifier {
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.background(.cardColor)
|
||||
.clipShape(
|
||||
RoundedRectangle(cornerRadius: 12)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension ShapeStyle where Self == CardColor {
|
||||
|
||||
fileprivate static var cardColor: CardColor {
|
||||
CardColor()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private struct CardColor: ShapeStyle {
|
||||
|
||||
func resolve(in environment: EnvironmentValues) -> some ShapeStyle {
|
||||
#if os(visionOS)
|
||||
.regularMaterial
|
||||
#else
|
||||
environment.colorScheme == .dark
|
||||
? Color.choice(for: .systemGray4) : .white
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension View {
|
||||
|
||||
func questionCardEnabled(_ isEnabled: Bool) -> some View {
|
||||
environment(\.isQuestionCardEnabled, isEnabled)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#Preview {
|
||||
ScrollView {
|
||||
VStack(spacing: 20) {
|
||||
QuestionCard {
|
||||
VStack(alignment: .leading) {
|
||||
Text("Title")
|
||||
Text("Detail")
|
||||
}
|
||||
}
|
||||
|
||||
QuestionCard {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
QuestionCard {
|
||||
VStack(alignment: .leading) {
|
||||
Text("Title")
|
||||
Text("Detail")
|
||||
}
|
||||
}
|
||||
Rectangle()
|
||||
.fill(Color(white: 0.975))
|
||||
.frame(height: 44)
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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 SwiftUI
|
||||
|
||||
enum StateManagementType<T> {
|
||||
case automatic(key: StepResultKey<T>)
|
||||
case manual(Binding<T>)
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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 SwiftUI
|
||||
|
||||
extension EnvironmentValues {
|
||||
@Entry var questionRequired: Bool = false
|
||||
}
|
||||
|
||||
extension View {
|
||||
|
||||
/// Designates a question as required or optional.
|
||||
public func questionRequired(_ value: Bool) -> some View {
|
||||
self
|
||||
.environment(\.questionRequired, value)
|
||||
}
|
||||
}
|
||||
|
||||
struct QuestionRequiredPreferenceKey: PreferenceKey {
|
||||
static var defaultValue: Bool = false
|
||||
|
||||
static func reduce(value: inout Bool, nextValue: () -> Bool) {
|
||||
value = nextValue()
|
||||
}
|
||||
}
|
||||
|
||||
struct QuestionAnsweredPreferenceKey: PreferenceKey {
|
||||
static var defaultValue = false
|
||||
|
||||
static func reduce(value: inout Bool, nextValue: () -> Bool) {
|
||||
value = nextValue()
|
||||
}
|
||||
}
|
||||
|
||||
struct StepCompletedPreferenceKey: PreferenceKey {
|
||||
static var defaultValue: Bool = false
|
||||
|
||||
static func reduce(value: inout Bool, nextValue: () -> Bool) {
|
||||
value = nextValue()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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 Foundation
|
||||
|
||||
/// Represents a text choice.
|
||||
public struct TextChoice: Identifiable {
|
||||
|
||||
/// The id for this multiple choice option.
|
||||
public let id: String
|
||||
let choiceText: String
|
||||
let value: ResultValue
|
||||
|
||||
/// Initializes an instance of ``TextChoice`` with the provided configuration.
|
||||
/// - Parameters:
|
||||
/// - id: The unique identifier for this multiple choice option.
|
||||
/// - choiceText: The text for this multiple choice option.
|
||||
/// - value: The integer value associated with this multiple choice option.
|
||||
public init(
|
||||
id: String,
|
||||
choiceText: String,
|
||||
value: Int
|
||||
) {
|
||||
self.init(id: id, choiceText: choiceText, value: .int(value))
|
||||
}
|
||||
|
||||
/// Initializes an instance of ``TextChoice`` with the provided configuration.
|
||||
/// - Parameters:
|
||||
/// - id: The unique identifier for this multiple choice option.
|
||||
/// - choiceText: The text for this multiple choice option.
|
||||
/// - value: The string value associated with this multiple choice option.
|
||||
public init(
|
||||
id: String,
|
||||
choiceText: String,
|
||||
value: String
|
||||
) {
|
||||
self.init(id: id, choiceText: choiceText, value: .string(value))
|
||||
}
|
||||
|
||||
/// Initializes an instance of ``TextChoice`` with the provided configuration.
|
||||
/// - Parameters:
|
||||
/// - id: The unique identifier for this multiple choice option.
|
||||
/// - choiceText: The text for this multiple choice option.
|
||||
/// - value: The date value associated with this multiple choice option.
|
||||
public init(
|
||||
id: String,
|
||||
choiceText: String,
|
||||
value: Date
|
||||
) {
|
||||
self.init(id: id, choiceText: choiceText, value: .date(value))
|
||||
}
|
||||
|
||||
private init(
|
||||
id: String,
|
||||
choiceText: String,
|
||||
value: ResultValue
|
||||
) {
|
||||
self.id = id
|
||||
self.choiceText = choiceText
|
||||
self.value = value
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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 SwiftUI
|
||||
|
||||
/// A question that allows for date and time input.
|
||||
public struct DateTimeQuestion<Header: View>: View {
|
||||
|
||||
@EnvironmentObject
|
||||
private var managedFormResult: ResearchFormResult
|
||||
|
||||
@Environment(\.questionContext)
|
||||
private var questionContext: QuestionContext
|
||||
|
||||
@Environment(\.questionRequired)
|
||||
private var isRequired: Bool
|
||||
|
||||
@State
|
||||
private var managedDate: Date?
|
||||
|
||||
@State
|
||||
private var showDatePickerModal = false
|
||||
|
||||
@State
|
||||
private var showTimePickerModal = false
|
||||
private let id: String
|
||||
private let header: Header
|
||||
private let pickerPrompt: String
|
||||
private let displayedComponents: DatePicker.Components
|
||||
private let range: ClosedRange<Date>
|
||||
private let selection: StateManagementType<Date?>
|
||||
|
||||
private var resolvedResult: Binding<Date?> {
|
||||
switch selection {
|
||||
case let .automatic(key: key):
|
||||
return Binding(
|
||||
get: {
|
||||
let date: Date?
|
||||
if let managedDate {
|
||||
date = managedDate
|
||||
} else {
|
||||
switch questionContext {
|
||||
case .formEmbedded:
|
||||
date = managedFormResult.resultForStep(key: key) ?? Date()
|
||||
case .standalone:
|
||||
date = Date()
|
||||
}
|
||||
}
|
||||
return date
|
||||
},
|
||||
set: {
|
||||
managedDate = $0
|
||||
|
||||
if case .formEmbedded = questionContext {
|
||||
managedFormResult.setResultForStep(.date($0), key: key)
|
||||
}
|
||||
}
|
||||
)
|
||||
case let .manual(value):
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes an instance of ``DateTimeQuestion`` with the provided configuration.
|
||||
/// - Parameters:
|
||||
/// - id: The unique identifier for this question.
|
||||
/// - header: The header for this question.
|
||||
/// - date: The selected date.
|
||||
/// - pickerPrompt: The prompt that informs the user.
|
||||
/// - displayedComponents: The date-time components that are displayed for this question.
|
||||
/// - range: The range of selectable dates.
|
||||
public init(
|
||||
id: String,
|
||||
@ViewBuilder header: () -> Header,
|
||||
date: Date = Date(),
|
||||
pickerPrompt: String,
|
||||
displayedComponents: DatePicker.Components,
|
||||
range: ClosedRange<Date>
|
||||
) {
|
||||
self.id = id
|
||||
self.header = header()
|
||||
self.pickerPrompt = pickerPrompt
|
||||
self.displayedComponents = displayedComponents
|
||||
self.range = range
|
||||
self.selection = .automatic(key: .date(id: id))
|
||||
}
|
||||
|
||||
/// Initializes an instance of ``DateTimeQuestion`` with the provided configuration.
|
||||
/// - Parameters:
|
||||
/// - id: The unique identifier for this question.
|
||||
/// - header: The header for this question.
|
||||
/// - date: The selected date.
|
||||
/// - pickerPrompt: The prompt that informs the user.
|
||||
/// - displayedComponents: The date-time components that are displayed for this question.
|
||||
/// - range: The range of selectable dates.
|
||||
public init(
|
||||
id: String,
|
||||
@ViewBuilder header: () -> Header,
|
||||
date: Binding<Date?>,
|
||||
pickerPrompt: String,
|
||||
displayedComponents: DatePicker.Components,
|
||||
range: ClosedRange<Date>
|
||||
) {
|
||||
self.id = id
|
||||
self.header = header()
|
||||
self.pickerPrompt = pickerPrompt
|
||||
self.displayedComponents = displayedComponents
|
||||
self.range = range
|
||||
self.selection = .manual(date)
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
QuestionCard {
|
||||
Question {
|
||||
header
|
||||
} content: {
|
||||
#if os(watchOS)
|
||||
VStack {
|
||||
if displayedComponents.contains(.date) {
|
||||
Button {
|
||||
showDatePickerModal.toggle()
|
||||
} label: {
|
||||
Text(
|
||||
resolvedResult.wrappedValue ?? Date(),
|
||||
format: .dateTime.day().month().year()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if displayedComponents.contains(.hourMinuteAndSecond) {
|
||||
Button {
|
||||
showTimePickerModal.toggle()
|
||||
} label: {
|
||||
Text(
|
||||
resolvedResult.wrappedValue ?? Date(),
|
||||
format: .dateTime.hour().minute().second()
|
||||
)
|
||||
}
|
||||
} else if displayedComponents.contains(.hourAndMinute) {
|
||||
Button {
|
||||
showTimePickerModal.toggle()
|
||||
} label: {
|
||||
Text(
|
||||
resolvedResult.wrappedValue ?? Date(),
|
||||
format: .dateTime.hour().minute()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
.buttonBorderShape(.roundedRectangle)
|
||||
.buttonStyle(.bordered)
|
||||
.padding()
|
||||
.navigationDestination(isPresented: $showDatePickerModal) {
|
||||
watchDatePicker(displayedComponents: .date)
|
||||
}
|
||||
.navigationDestination(isPresented: $showTimePickerModal) {
|
||||
watchDatePicker(
|
||||
displayedComponents: displayedComponents.contains(
|
||||
.hourMinuteAndSecond)
|
||||
? .hourMinuteAndSecond : .hourAndMinute)
|
||||
}
|
||||
#else
|
||||
DatePicker(
|
||||
pickerPrompt,
|
||||
selection: resolvedResult.unwrapped(
|
||||
defaultValue: Date()),
|
||||
in: range,
|
||||
displayedComponents: displayedComponents
|
||||
)
|
||||
.datePickerStyle(.compact)
|
||||
.foregroundStyle(.primary)
|
||||
.padding()
|
||||
#endif
|
||||
}
|
||||
.preference(
|
||||
key: QuestionRequiredPreferenceKey.self, value: isRequired
|
||||
)
|
||||
.preference(
|
||||
key: QuestionAnsweredPreferenceKey.self, value: isAnswered)
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func watchDatePicker(displayedComponents: DatePicker.Components)
|
||||
-> some View
|
||||
{
|
||||
VStack(alignment: .leading) {
|
||||
header
|
||||
DatePicker(
|
||||
pickerPrompt,
|
||||
selection: resolvedResult.unwrapped(defaultValue: Date()),
|
||||
in: range,
|
||||
displayedComponents: displayedComponents
|
||||
)
|
||||
#if !os(watchOS)
|
||||
.padding(.horizontal)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private var isAnswered: Bool {
|
||||
return resolvedResult.wrappedValue != nil
|
||||
}
|
||||
}
|
||||
|
||||
extension DateTimeQuestion where Header == QuestionHeader {
|
||||
|
||||
/// Initializes an instance of ``DateTimeQuestion`` with the provided configuration.
|
||||
/// - Parameters:
|
||||
/// - id: The unique identifier for this question.
|
||||
/// - title: The title for this question.
|
||||
/// - detail: The details for this question.
|
||||
/// - date: The selected date.
|
||||
/// - pickerPrompt: The prompt that informs the user.
|
||||
/// - displayedComponents: The date-time components that are displayed for this question.
|
||||
/// - range: The range of selectable dates.
|
||||
public init(
|
||||
id: String,
|
||||
title: String,
|
||||
detail: String? = nil,
|
||||
date: Date = Date(),
|
||||
pickerPrompt: String,
|
||||
displayedComponents: DatePicker.Components,
|
||||
range: ClosedRange<Date>
|
||||
) {
|
||||
self.id = id
|
||||
self.header = QuestionHeader(title: title, detail: detail)
|
||||
self.pickerPrompt = pickerPrompt
|
||||
self.displayedComponents = displayedComponents
|
||||
self.range = range
|
||||
self.selection = .automatic(key: .date(id: id))
|
||||
}
|
||||
|
||||
/// Initializes an instance of ``DateTimeQuestion`` with the provided configuration.
|
||||
/// - Parameters:
|
||||
/// - id: The unique identifier for this question.
|
||||
/// - title: The title for this question.
|
||||
/// - detail: The details for this question.
|
||||
/// - date: The selected date.
|
||||
/// - pickerPrompt: The prompt that informs the user.
|
||||
/// - displayedComponents: The date-time components that are displayed for this question.
|
||||
/// - range: The range of selectable dates.
|
||||
public init(
|
||||
id: String,
|
||||
title: String,
|
||||
detail: String? = nil,
|
||||
date: Binding<Date?>,
|
||||
pickerPrompt: String,
|
||||
displayedComponents: DatePicker.Components,
|
||||
range: ClosedRange<Date>
|
||||
) {
|
||||
self.id = id
|
||||
self.header = QuestionHeader(title: title, detail: detail)
|
||||
self.pickerPrompt = pickerPrompt
|
||||
self.displayedComponents = displayedComponents
|
||||
self.range = range
|
||||
self.selection = .manual(date)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#Preview("Date Only") {
|
||||
@Previewable @State var date: Date? = Date()
|
||||
NavigationStack {
|
||||
ScrollView {
|
||||
DateTimeQuestion(
|
||||
id: UUID().uuidString,
|
||||
title: "What is your birthday?",
|
||||
detail: "Question 1 of 4",
|
||||
date: $date,
|
||||
pickerPrompt: "Select Date",
|
||||
displayedComponents: [.date],
|
||||
range: Date.distantPast...Date.distantFuture
|
||||
)
|
||||
.padding(.horizontal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview("Time Only") {
|
||||
@Previewable @State var date: Date? = Date()
|
||||
NavigationStack {
|
||||
ScrollView {
|
||||
DateTimeQuestion(
|
||||
id: UUID().uuidString,
|
||||
title: "What time is it?",
|
||||
detail: "Question 2 of 4",
|
||||
date: $date,
|
||||
pickerPrompt: "Select Time",
|
||||
displayedComponents: [.hourAndMinute],
|
||||
range: Date.distantPast...Date.distantFuture
|
||||
)
|
||||
.padding(.horizontal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview("Time and Date") {
|
||||
@Previewable @State var date: Date? = Date()
|
||||
NavigationStack {
|
||||
ScrollView {
|
||||
DateTimeQuestion(
|
||||
id: UUID().uuidString,
|
||||
title: "What is the time and date?",
|
||||
detail: "Question 2 of 4",
|
||||
date: $date,
|
||||
pickerPrompt: "Select Time and Date",
|
||||
displayedComponents: [.date, .hourAndMinute],
|
||||
range: Date.distantPast...Date.distantFuture
|
||||
)
|
||||
.padding(.horizontal)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,387 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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 SwiftUI
|
||||
|
||||
/// Represents the different measurement systems that can be used.
|
||||
public enum MeasurementSystem {
|
||||
|
||||
/// The US Customary measurement system.
|
||||
case USC
|
||||
|
||||
/// The measurement system defined by the system.
|
||||
case local
|
||||
|
||||
/// The metric measurement system.
|
||||
case metric
|
||||
|
||||
}
|
||||
|
||||
/// A question that allows for height input.
|
||||
public struct HeightQuestion: View {
|
||||
|
||||
@EnvironmentObject
|
||||
private var managedFormResult: ResearchFormResult
|
||||
|
||||
@Environment(\.questionContext)
|
||||
private var questionContext: QuestionContext
|
||||
|
||||
@Environment(\.questionRequired)
|
||||
private var isRequired: Bool
|
||||
|
||||
@State
|
||||
private var managedHeight: Double?
|
||||
|
||||
@State private var isInputActive = false
|
||||
@State private var hasChanges: Bool
|
||||
|
||||
private let id: String
|
||||
private let title: String
|
||||
private let detail: String?
|
||||
private let measurementSystem: MeasurementSystem
|
||||
private let selection: StateManagementType<Double?>
|
||||
|
||||
private var initialPrimaryValue: Double = 162 // Denotes height in cm, which is ~5'4", a good average height.
|
||||
|
||||
private var resolvedResult: Binding<Double?> {
|
||||
switch selection {
|
||||
case let .automatic(key: key):
|
||||
return Binding(
|
||||
get: {
|
||||
let height: Double?
|
||||
if let managedHeight {
|
||||
height = managedHeight
|
||||
} else {
|
||||
switch questionContext {
|
||||
case .formEmbedded:
|
||||
height = managedFormResult.resultForStep(key: key) ?? initialPrimaryValue
|
||||
case .standalone:
|
||||
height = initialPrimaryValue
|
||||
}
|
||||
}
|
||||
return height
|
||||
},
|
||||
set: {
|
||||
managedHeight = $0
|
||||
|
||||
if case .formEmbedded = questionContext {
|
||||
managedFormResult.setResultForStep(.height($0), key: key)
|
||||
}
|
||||
}
|
||||
)
|
||||
case let .manual(value):
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes an instance of ``HeightQuestion`` with the provided configuration.
|
||||
/// - Parameters:
|
||||
/// - id: The unique identifier for this question.
|
||||
/// - title: The title for this question.
|
||||
/// - detail: The details for this question.
|
||||
/// - measurementSystem: The measurement system for this question.
|
||||
public init(
|
||||
id: String,
|
||||
title: String,
|
||||
detail: String? = nil,
|
||||
measurementSystem: MeasurementSystem
|
||||
) {
|
||||
self.id = id
|
||||
self.hasChanges = false
|
||||
self.title = title
|
||||
self.detail = detail
|
||||
|
||||
let system: MeasurementSystem = {
|
||||
switch measurementSystem {
|
||||
case .USC:
|
||||
return .USC
|
||||
case .local:
|
||||
if Locale.current.measurementSystem == .us {
|
||||
return .USC
|
||||
} else {
|
||||
return .metric
|
||||
}
|
||||
case .metric:
|
||||
return .metric
|
||||
}
|
||||
}()
|
||||
self.measurementSystem = system
|
||||
self.selection = .automatic(key: .height(id: id))
|
||||
}
|
||||
|
||||
/// Initializes an instance of ``HeightQuestion`` with the provided configuration.
|
||||
/// - Parameters:
|
||||
/// - id: The unique identifier question.
|
||||
/// - title: The title for this question.
|
||||
/// - detail: The details for this question.
|
||||
/// - measurementSystem: The measurement system for this question.
|
||||
/// - height: The selected height.
|
||||
public init(
|
||||
id: String,
|
||||
title: String,
|
||||
detail: String? = nil,
|
||||
measurementSystem: MeasurementSystem,
|
||||
height: Binding<Double?>
|
||||
) {
|
||||
self.id = id
|
||||
self.hasChanges = false
|
||||
self.title = title
|
||||
self.detail = detail
|
||||
|
||||
let system: MeasurementSystem = {
|
||||
switch measurementSystem {
|
||||
case .USC:
|
||||
return .USC
|
||||
case .local:
|
||||
if Locale.current.measurementSystem == .us {
|
||||
return .USC
|
||||
} else {
|
||||
return .metric
|
||||
}
|
||||
case .metric:
|
||||
return .metric
|
||||
}
|
||||
}()
|
||||
self.measurementSystem = system
|
||||
self.selection = .manual(height)
|
||||
}
|
||||
|
||||
private var selectionString: String {
|
||||
guard let result = resolvedResult.wrappedValue else {
|
||||
return "\(Int(initialPrimaryValue)) cm"
|
||||
}
|
||||
if measurementSystem == .USC {
|
||||
let (feet, inches) = convertCentimetersToFeetAndInches(result)
|
||||
return "\(feet)' \(inches)\""
|
||||
} else {
|
||||
return "\(Int(result)) cm"
|
||||
}
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
QuestionCard {
|
||||
Question(title: title, detail: detail) {
|
||||
HStack {
|
||||
Text("Select Height")
|
||||
.foregroundStyle(Color.primary)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
Button {
|
||||
isInputActive = true
|
||||
#if !os(watchOS)
|
||||
UIApplication.shared.endEditing()
|
||||
#endif
|
||||
} label: {
|
||||
Text(selectionString)
|
||||
.foregroundStyle(Color.primary)
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.buttonBorderShape(.roundedRectangle)
|
||||
#if !os(watchOS)
|
||||
.popover(
|
||||
isPresented: $isInputActive,
|
||||
attachmentAnchor: .point(.bottom),
|
||||
arrowEdge: .top
|
||||
) {
|
||||
HeightPickerView(
|
||||
measurementSystem: measurementSystem,
|
||||
selection: resolvedResult,
|
||||
hasChanges: $hasChanges
|
||||
)
|
||||
.frame(width: 300)
|
||||
.presentationCompactAdaptation((.popover))
|
||||
}
|
||||
#else
|
||||
.navigationDestination(
|
||||
isPresented: $isInputActive
|
||||
) {
|
||||
HeightPickerView(
|
||||
measurementSystem: measurementSystem,
|
||||
selection: resolvedResult,
|
||||
hasChanges: $hasChanges
|
||||
)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
.preference(
|
||||
key: QuestionRequiredPreferenceKey.self, value: isRequired
|
||||
)
|
||||
.preference(
|
||||
key: QuestionAnsweredPreferenceKey.self, value: isAnswered)
|
||||
}
|
||||
}
|
||||
|
||||
private var isAnswered: Bool {
|
||||
resolvedResult.wrappedValue != nil
|
||||
}
|
||||
}
|
||||
|
||||
struct HeightPickerView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@State private var firstSelection: Int
|
||||
@State private var secondSelection: Int
|
||||
private let footToCentimetersMultiplier: Double = 30.48
|
||||
private let inchToCentimetersMultiplier: Double = 2.54
|
||||
|
||||
private let measurementSystem: MeasurementSystem
|
||||
|
||||
@Binding private var selection: Double?
|
||||
@Binding private var hasChanges: Bool
|
||||
|
||||
init(
|
||||
measurementSystem: MeasurementSystem,
|
||||
selection: Binding<Double?>,
|
||||
hasChanges: Binding<Bool>
|
||||
) {
|
||||
self.measurementSystem = measurementSystem
|
||||
self._selection = selection
|
||||
self._hasChanges = hasChanges
|
||||
|
||||
guard let centimeters = selection.wrappedValue else {
|
||||
firstSelection = 0
|
||||
secondSelection = 0
|
||||
return
|
||||
}
|
||||
if Self.usesMetricSystem(measurementSystem: measurementSystem) == false
|
||||
{
|
||||
let (feet, inches) = convertCentimetersToFeetAndInches(centimeters)
|
||||
firstSelection = feet
|
||||
secondSelection = inches
|
||||
} else {
|
||||
firstSelection = Int(centimeters)
|
||||
secondSelection = 0
|
||||
}
|
||||
}
|
||||
|
||||
private var upperValue: Int {
|
||||
if measurementSystem == .USC {
|
||||
return 10
|
||||
} else {
|
||||
return 300
|
||||
}
|
||||
}
|
||||
|
||||
private var secondaryUpperValue: Int {
|
||||
// Numbers up to 1 foot or 12 inches
|
||||
return 12
|
||||
}
|
||||
|
||||
private var primaryUnit: String {
|
||||
if measurementSystem == .USC {
|
||||
return "ft"
|
||||
} else {
|
||||
return "cm"
|
||||
}
|
||||
}
|
||||
|
||||
private var secondaryUnit: String {
|
||||
return "in"
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
HStack(spacing: .zero) {
|
||||
Picker(selection: $firstSelection) {
|
||||
ForEach(0..<upperValue, id: \.self) { i in
|
||||
Text("\(i) \(primaryUnit)")
|
||||
.tag(i)
|
||||
}
|
||||
} label: {
|
||||
Text(primaryUnit)
|
||||
}
|
||||
.pickerStyle(.wheel)
|
||||
.onChange(of: firstSelection) { _, _ in
|
||||
hasChanges = true
|
||||
selection = standardizedHeight(
|
||||
(firstSelection, secondSelection))
|
||||
}
|
||||
|
||||
if measurementSystem == .USC {
|
||||
Picker(selection: $secondSelection) {
|
||||
ForEach(0..<secondaryUpperValue, id: \.self) { i in
|
||||
Text("\(i) \(secondaryUnit)")
|
||||
.tag(i)
|
||||
}
|
||||
} label: {
|
||||
Text(secondaryUnit)
|
||||
}
|
||||
.pickerStyle(.wheel)
|
||||
.onChange(of: secondSelection) { _, _ in
|
||||
hasChanges = true
|
||||
selection = standardizedHeight(
|
||||
(firstSelection, secondSelection))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func standardizedHeight(_ height: (Int, Int)) -> Double {
|
||||
if Self.usesMetricSystem(measurementSystem: measurementSystem) == false
|
||||
{
|
||||
let centimeters =
|
||||
(Double(height.0) * footToCentimetersMultiplier)
|
||||
+ (Double(height.1) * inchToCentimetersMultiplier)
|
||||
return centimeters
|
||||
} else {
|
||||
return Double(height.0)
|
||||
}
|
||||
}
|
||||
|
||||
private static func usesMetricSystem(measurementSystem: MeasurementSystem)
|
||||
-> Bool
|
||||
{
|
||||
switch measurementSystem {
|
||||
case .USC:
|
||||
return false
|
||||
case .local:
|
||||
if Locale.current.measurementSystem == .us {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
case .metric:
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 18.0, *)
|
||||
#Preview {
|
||||
@Previewable @State var selection: Double? = 162
|
||||
NavigationStack {
|
||||
HeightQuestion(
|
||||
id: UUID().uuidString,
|
||||
title: "Height question here",
|
||||
detail: nil,
|
||||
measurementSystem: .USC,
|
||||
height: $selection
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,567 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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 SwiftUI
|
||||
|
||||
/// An image choice.
|
||||
public struct ImageChoice: Identifiable, Equatable {
|
||||
|
||||
/// The unique identifier for this image choice.
|
||||
public let id: String
|
||||
|
||||
/// The image for the unselected state.
|
||||
public let normalImage: UIImage
|
||||
|
||||
/// The image for the selected state.
|
||||
public let selectedImage: UIImage?
|
||||
|
||||
/// The text that describes the image.
|
||||
public let text: String
|
||||
|
||||
let value: ResultValue
|
||||
|
||||
/// Initializes an instance of ``ImageChoice`` with the provided configuration.
|
||||
/// - Parameters:
|
||||
/// - normalImage: The image for the unselected state.
|
||||
/// - selectedImage: The image for the selected state.
|
||||
/// - text: The text that describes the image.
|
||||
/// - value: The selection value for this image.
|
||||
public init(
|
||||
normalImage: UIImage,
|
||||
selectedImage: UIImage?,
|
||||
text: String,
|
||||
value: Int
|
||||
) {
|
||||
self.init(
|
||||
normalImage: normalImage,
|
||||
selectedImage: selectedImage,
|
||||
text: text,
|
||||
value: .int(value)
|
||||
)
|
||||
}
|
||||
|
||||
/// Initializes an instance of ``ImageChoice`` with the provided configuration.
|
||||
/// - Parameters:
|
||||
/// - normalImage: The image for the unselected state.
|
||||
/// - selectedImage: The image for the selected state.
|
||||
/// - text: The text that describes the image.
|
||||
/// - value: The selection value for this image.
|
||||
public init(
|
||||
normalImage: UIImage,
|
||||
selectedImage: UIImage?,
|
||||
text: String,
|
||||
value: String
|
||||
) {
|
||||
self.init(
|
||||
normalImage: normalImage,
|
||||
selectedImage: selectedImage,
|
||||
text: text,
|
||||
value: .string(value)
|
||||
)
|
||||
}
|
||||
|
||||
/// Initializes an instance of ``ImageChoice`` with the provided configuration.
|
||||
/// - Parameters:
|
||||
/// - normalImage: The image for the unselected state.
|
||||
/// - selectedImage: The image for the selected state.
|
||||
/// - text: The text that describes the image.
|
||||
/// - value: The selection value for this image.
|
||||
public init(
|
||||
normalImage: UIImage,
|
||||
selectedImage: UIImage?,
|
||||
text: String,
|
||||
value: Date
|
||||
) {
|
||||
self.init(
|
||||
normalImage: normalImage,
|
||||
selectedImage: selectedImage,
|
||||
text: text,
|
||||
value: .date(value)
|
||||
)
|
||||
}
|
||||
|
||||
private init(
|
||||
normalImage: UIImage,
|
||||
selectedImage: UIImage?,
|
||||
text: String,
|
||||
value: ResultValue
|
||||
) {
|
||||
self.id = String(describing: value)
|
||||
self.normalImage = normalImage
|
||||
self.selectedImage = selectedImage
|
||||
self.text = text
|
||||
self.value = value
|
||||
}
|
||||
|
||||
public static func == (lhs: ImageChoice, rhs: ImageChoice) -> Bool {
|
||||
return lhs.id == rhs.id && lhs.text == rhs.text
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the number of of choices that can be selected.
|
||||
public enum ChoiceSelectionLimit {
|
||||
|
||||
/// Allows for the selection of only one choice.
|
||||
case single
|
||||
|
||||
/// Allows for the selection of more than one choice.
|
||||
case multiple
|
||||
|
||||
}
|
||||
|
||||
/// A question that allows for image input.
|
||||
public struct ImageChoiceQuestion: View {
|
||||
|
||||
@EnvironmentObject
|
||||
private var managedFormResult: ResearchFormResult
|
||||
|
||||
@Environment(\.questionContext)
|
||||
private var questionContext: QuestionContext
|
||||
|
||||
@Environment(\.questionRequired)
|
||||
private var isRequired: Bool
|
||||
|
||||
@State
|
||||
private var managedResults: [ResultValue]?
|
||||
|
||||
private let id: String
|
||||
private let title: String
|
||||
private let detail: String?
|
||||
private let choices: [ImageChoice]
|
||||
private let choiceSelectionLimit: ChoiceSelectionLimit
|
||||
private let vertical: Bool
|
||||
private let selection: StateManagementType<[ResultValue]?>
|
||||
|
||||
private var resolvedResult: Binding<[ResultValue]?> {
|
||||
switch selection {
|
||||
case .automatic(let key):
|
||||
return Binding(
|
||||
get: {
|
||||
let result: [ResultValue]?
|
||||
if let managedResults {
|
||||
result = managedResults
|
||||
} else {
|
||||
switch questionContext {
|
||||
case .formEmbedded:
|
||||
result = managedFormResult.resultForStep(key: key) ?? []
|
||||
case .standalone:
|
||||
result = []
|
||||
}
|
||||
}
|
||||
return result
|
||||
},
|
||||
set: {
|
||||
managedResults = $0
|
||||
|
||||
if case .formEmbedded = questionContext {
|
||||
managedFormResult.setResultForStep(.image($0), key: key)
|
||||
}
|
||||
}
|
||||
)
|
||||
case .manual(let value):
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes an instance of ``ImageChoiceQuestion`` with the provided configuration.
|
||||
/// - Parameters:
|
||||
/// - id: The unique identifier for this question.
|
||||
/// - title: The title for this question.
|
||||
/// - detail: The details for this question.
|
||||
/// - choices: The image choices for this question.
|
||||
/// - choiceSelectionLimit: The choice selection limit for this image choice question.
|
||||
/// - vertical: Whether or not the images should be displayed horizontally or vertically.
|
||||
/// - imageValue: The selected value associated with one of the image choices.
|
||||
public init(
|
||||
id: String,
|
||||
title: String,
|
||||
detail: String?,
|
||||
choices: [ImageChoice],
|
||||
choiceSelectionLimit: ChoiceSelectionLimit,
|
||||
vertical: Bool,
|
||||
imageValue: Binding<[Int]?>
|
||||
) {
|
||||
self.init(
|
||||
id: id,
|
||||
title: title,
|
||||
detail: detail,
|
||||
choices: choices,
|
||||
choiceSelectionLimit: choiceSelectionLimit,
|
||||
vertical: vertical,
|
||||
selection: .init(
|
||||
get: {
|
||||
guard let integers = imageValue.wrappedValue else {
|
||||
return nil
|
||||
}
|
||||
return integers.map { .int($0) }
|
||||
},
|
||||
set: { newValues in
|
||||
guard let newValues else {
|
||||
imageValue.wrappedValue = nil
|
||||
return
|
||||
}
|
||||
|
||||
imageValue.wrappedValue = newValues.compactMap {
|
||||
resultValue in
|
||||
guard case let .int(value) = resultValue else {
|
||||
return nil
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/// Initializes an instance of ``ImageChoiceQuestion`` with the provided configuration.
|
||||
/// - Parameters:
|
||||
/// - id: The unique identifier for this question.
|
||||
/// - title: The title for this question.
|
||||
/// - detail: The details for this question.
|
||||
/// - choices: The image choices for this question.
|
||||
/// - choiceSelectionLimit: The choice selection limit for this image choice question.
|
||||
/// - vertical: Whether or not the images should be displayed horizontally or vertically.
|
||||
/// - imageValue: The selected value associated with one of the image choices.
|
||||
public init(
|
||||
id: String,
|
||||
title: String,
|
||||
detail: String?,
|
||||
choices: [ImageChoice],
|
||||
choiceSelectionLimit: ChoiceSelectionLimit,
|
||||
vertical: Bool,
|
||||
imageValue: Binding<[String]?>
|
||||
) {
|
||||
self.init(
|
||||
id: id,
|
||||
title: title,
|
||||
detail: detail,
|
||||
choices: choices,
|
||||
choiceSelectionLimit: choiceSelectionLimit,
|
||||
vertical: vertical,
|
||||
selection: .init(
|
||||
get: {
|
||||
guard let strings = imageValue.wrappedValue else {
|
||||
return nil
|
||||
}
|
||||
return strings.map { .string($0) }
|
||||
},
|
||||
set: { newValues in
|
||||
guard let newValues else {
|
||||
imageValue.wrappedValue = nil
|
||||
return
|
||||
}
|
||||
|
||||
imageValue.wrappedValue = newValues.compactMap {
|
||||
resultValue in
|
||||
guard case let .string(value) = resultValue else {
|
||||
return nil
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/// Initializes an instance of ``ImageChoiceQuestion`` with the provided configuration.
|
||||
/// - Parameters:
|
||||
/// - id: The unique identifier for this question.
|
||||
/// - title: The title for this question.
|
||||
/// - detail: The details for this question.
|
||||
/// - choices: The image choices for this question.
|
||||
/// - choiceSelectionLimit: The choice selection limit for this image choice question.
|
||||
/// - vertical: Whether or not the images should be displayed horizontally or vertically.
|
||||
/// - imageValue: The selected value associated with one of the image choices.
|
||||
public init(
|
||||
id: String,
|
||||
title: String,
|
||||
detail: String?,
|
||||
choices: [ImageChoice],
|
||||
choiceSelectionLimit: ChoiceSelectionLimit,
|
||||
vertical: Bool,
|
||||
imageValue: Binding<[Date]?>
|
||||
) {
|
||||
self.init(
|
||||
id: id,
|
||||
title: title,
|
||||
detail: detail,
|
||||
choices: choices,
|
||||
choiceSelectionLimit: choiceSelectionLimit,
|
||||
vertical: vertical,
|
||||
selection: .init(
|
||||
get: {
|
||||
guard let dates = imageValue.wrappedValue else {
|
||||
return nil
|
||||
}
|
||||
return dates.map { .date($0) }
|
||||
},
|
||||
set: { newValues in
|
||||
guard let newValues else {
|
||||
imageValue.wrappedValue = nil
|
||||
return
|
||||
}
|
||||
|
||||
imageValue.wrappedValue = newValues.compactMap {
|
||||
resultValue in
|
||||
guard case let .date(value) = resultValue else {
|
||||
return nil
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private init(
|
||||
id: String,
|
||||
title: String,
|
||||
detail: String?,
|
||||
choices: [ImageChoice],
|
||||
choiceSelectionLimit: ChoiceSelectionLimit,
|
||||
vertical: Bool,
|
||||
selection: Binding<[ResultValue]?>
|
||||
) {
|
||||
self.id = id
|
||||
self.title = title
|
||||
self.detail = detail
|
||||
self.choices = choices
|
||||
self.choiceSelectionLimit = choiceSelectionLimit
|
||||
self.vertical = vertical
|
||||
self.selection = .manual(selection)
|
||||
}
|
||||
|
||||
/// Initializes an instance of ``ImageChoiceQuestion`` with the provided configuration.
|
||||
/// - Parameters:
|
||||
/// - id: The unique identifier for this question.
|
||||
/// - title: The title for this question.
|
||||
/// - detail: The details for this question.
|
||||
/// - choices: The image choices for this question.
|
||||
/// - choiceSelectionLimit: The choice selection limit for this image choice question.
|
||||
/// - vertical: Whether or not the images should be displayed horizontally or vertically.
|
||||
public init(
|
||||
id: String,
|
||||
title: String,
|
||||
detail: String?,
|
||||
choices: [ImageChoice],
|
||||
choiceSelectionLimit: ChoiceSelectionLimit,
|
||||
vertical: Bool
|
||||
) {
|
||||
self.id = id
|
||||
self.title = title
|
||||
self.detail = detail
|
||||
self.choices = choices
|
||||
self.choiceSelectionLimit = choiceSelectionLimit
|
||||
self.vertical = vertical
|
||||
self.selection = .automatic(key: .imageChoice(id: id))
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
QuestionCard {
|
||||
Question(title: title, detail: detail) {
|
||||
VStack {
|
||||
if choiceSelectionLimit == .multiple {
|
||||
multipleSelectionHeader()
|
||||
}
|
||||
|
||||
if vertical {
|
||||
VStack {
|
||||
imageChoices()
|
||||
}
|
||||
.padding()
|
||||
} else {
|
||||
HStack {
|
||||
imageChoices()
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
selectionText()
|
||||
.frame(maxWidth: .infinity, alignment: .center)
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
.preference(
|
||||
key: QuestionRequiredPreferenceKey.self, value: isRequired
|
||||
)
|
||||
.preference(
|
||||
key: QuestionAnsweredPreferenceKey.self, value: isAnswered)
|
||||
}
|
||||
}
|
||||
|
||||
private var isAnswered: Bool {
|
||||
if let resultArray = resolvedResult.wrappedValue, !resultArray.isEmpty {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func selectionText() -> some View {
|
||||
|
||||
if let result = resolvedResult.wrappedValue,
|
||||
result.isEmpty == false
|
||||
{
|
||||
let strings: [String] = {
|
||||
var strings: [String] = []
|
||||
for i in result {
|
||||
if let choice = choices.first(where: { $0.value == i }) {
|
||||
strings.append(choice.text)
|
||||
}
|
||||
}
|
||||
return strings
|
||||
}()
|
||||
|
||||
Text(strings.joined(separator: ", "))
|
||||
.foregroundStyle(.primary)
|
||||
} else {
|
||||
Text("Tap to select")
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func imageChoices() -> some View {
|
||||
ForEach(choices, id: \.id) { choice in
|
||||
Button {
|
||||
if let index = resolvedResult.wrappedValue?.firstIndex(where: {
|
||||
$0 == choice.value
|
||||
}) {
|
||||
resolvedResult.wrappedValue?.remove(at: index)
|
||||
} else {
|
||||
resolvedResult.wrappedValue?.append(choice.value)
|
||||
}
|
||||
} label: {
|
||||
if let result = resolvedResult.wrappedValue,
|
||||
result.contains(where: { $0 == choice.value })
|
||||
{
|
||||
Image(uiImage: choice.selectedImage ?? choice.normalImage)
|
||||
.resizable()
|
||||
.imageSizeConstraints()
|
||||
.scaledToFit()
|
||||
.padding()
|
||||
.frame(maxWidth: .infinity)
|
||||
.background(
|
||||
choice.selectedImage == nil
|
||||
? Color.choice(for: .systemGray5) : Color.clear
|
||||
)
|
||||
.cornerRadius(24)
|
||||
.imageChoiceHoverEffect()
|
||||
} else {
|
||||
Image(uiImage: choice.normalImage)
|
||||
.resizable()
|
||||
.imageSizeConstraints()
|
||||
.scaledToFit()
|
||||
.padding()
|
||||
.frame(maxWidth: .infinity)
|
||||
.cornerRadius(24)
|
||||
.imageChoiceHoverEffect()
|
||||
.contentShape(Capsule())
|
||||
}
|
||||
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func multipleSelectionHeader() -> some View {
|
||||
#if !os(watchOS)
|
||||
Text("SELECT ALL THAT APPLY")
|
||||
.font(.caption)
|
||||
.fontWeight(.semibold)
|
||||
.foregroundStyle(.secondary)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding([.horizontal, .top])
|
||||
Divider()
|
||||
#else
|
||||
Text("SELECT ALL THAT APPLY")
|
||||
.lineLimit(1)
|
||||
.minimumScaleFactor(0.5)
|
||||
.font(.caption2)
|
||||
.fontWeight(.semibold)
|
||||
.foregroundStyle(.secondary)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(.horizontal)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
fileprivate func imageChoiceHoverEffect() -> some View {
|
||||
self
|
||||
#if !os(watchOS)
|
||||
.contentShape(.hoverEffect, RoundedRectangle(cornerRadius: 24))
|
||||
.hoverEffect()
|
||||
#endif
|
||||
}
|
||||
|
||||
fileprivate func imageSizeConstraints() -> some View {
|
||||
self
|
||||
#if !os(watchOS)
|
||||
.frame(
|
||||
minWidth: 10, maxWidth: 50,
|
||||
minHeight: 10, maxHeight: 50
|
||||
)
|
||||
#else
|
||||
.frame(
|
||||
minWidth: 10, maxWidth: 30,
|
||||
minHeight: 10, maxHeight: 30
|
||||
)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
@Previewable @State var selection: [Int]? = []
|
||||
ScrollView {
|
||||
ImageChoiceQuestion(
|
||||
id: UUID().uuidString,
|
||||
title: "Which do you prefer?",
|
||||
detail: nil,
|
||||
choices: [
|
||||
ImageChoice(
|
||||
normalImage: UIImage(systemName: "carrot")!,
|
||||
selectedImage: nil,
|
||||
text: "carrot",
|
||||
value: 0
|
||||
),
|
||||
ImageChoice(
|
||||
normalImage: UIImage(systemName: "birthday.cake")!,
|
||||
selectedImage: nil,
|
||||
text: "cake",
|
||||
value: 1
|
||||
),
|
||||
],
|
||||
choiceSelectionLimit: .multiple,
|
||||
vertical: false,
|
||||
imageValue: $selection
|
||||
)
|
||||
}
|
||||
}
|
||||
+352
@@ -0,0 +1,352 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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 SwiftUI
|
||||
|
||||
/// A question that allows for multiple choice input.
|
||||
public struct MultipleChoiceQuestion: View {
|
||||
|
||||
@EnvironmentObject
|
||||
private var managedFormResult: ResearchFormResult
|
||||
|
||||
@Environment(\.questionContext)
|
||||
private var questionContext: QuestionContext
|
||||
|
||||
@Environment(\.questionRequired)
|
||||
private var isRequired: Bool
|
||||
|
||||
@State
|
||||
private var managedResults: [ResultValue]?
|
||||
|
||||
private var resolvedResult: Binding<[ResultValue]?> {
|
||||
switch selection {
|
||||
case let .automatic(key: key):
|
||||
return Binding(
|
||||
get: {
|
||||
let result: [ResultValue]?
|
||||
if let managedResults {
|
||||
result = managedResults
|
||||
} else {
|
||||
switch questionContext {
|
||||
case .formEmbedded:
|
||||
result = managedFormResult.resultForStep(key: key) ?? nil
|
||||
case .standalone:
|
||||
result = nil
|
||||
}
|
||||
}
|
||||
return result
|
||||
},
|
||||
set: {
|
||||
managedResults = $0
|
||||
|
||||
if case .formEmbedded = questionContext {
|
||||
managedFormResult.setResultForStep(.multipleChoice($0), key: key)
|
||||
}
|
||||
}
|
||||
)
|
||||
case let .manual(value):
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
private let id: String
|
||||
private let title: String
|
||||
private let detail: String?
|
||||
private let choices: [TextChoice]
|
||||
private let choiceSelectionLimit: ChoiceSelectionLimit
|
||||
private let selection: StateManagementType<[ResultValue]?>
|
||||
|
||||
/// Initializes an instance of ``MultipleChoiceQuestion`` with the provided configuration for an integer result.
|
||||
/// - Parameters:
|
||||
/// - id: The unique identifier associated with this question.
|
||||
/// - title: The title for this question.
|
||||
/// - detail: The details for this question.
|
||||
/// - choices: The choices that can be selected for this question.
|
||||
/// - choiceSelectionLimit: The selection type for this question.
|
||||
/// - selection: The selected choices, where the choice values are represented as integers.
|
||||
public init(
|
||||
id: String,
|
||||
title: String,
|
||||
detail: String? = nil,
|
||||
choices: [TextChoice],
|
||||
choiceSelectionLimit: ChoiceSelectionLimit,
|
||||
selection: Binding<[Int]?>
|
||||
) {
|
||||
self.init(
|
||||
id: id,
|
||||
title: title,
|
||||
detail: detail,
|
||||
choices: choices,
|
||||
choiceSelectionLimit: choiceSelectionLimit,
|
||||
selection: .init(
|
||||
get: {
|
||||
guard let integers = selection.wrappedValue else {
|
||||
return nil
|
||||
}
|
||||
return integers.map { .int($0) }
|
||||
},
|
||||
set: { newValues in
|
||||
guard let newValues else {
|
||||
selection.wrappedValue = nil
|
||||
return
|
||||
}
|
||||
|
||||
selection.wrappedValue = newValues.compactMap {
|
||||
resultValue in
|
||||
guard case let .int(value) = resultValue else {
|
||||
return nil
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/// Initializes an instance of ``MultipleChoiceQuestion`` with the provided configuration for a string result.
|
||||
/// - Parameters:
|
||||
/// - id: The unique identifier associated with this question.
|
||||
/// - title: The title for this question.
|
||||
/// - detail: The details for this question.
|
||||
/// - choices: The choices that can be selected for this question.
|
||||
/// - choiceSelectionLimit: The selection type for this question.
|
||||
/// - selection: The selected choices, where the choice values are represented as strings.
|
||||
public init(
|
||||
id: String,
|
||||
title: String,
|
||||
detail: String? = nil,
|
||||
choices: [TextChoice],
|
||||
choiceSelectionLimit: ChoiceSelectionLimit,
|
||||
selection: Binding<[String]?>
|
||||
) {
|
||||
self.init(
|
||||
id: id,
|
||||
title: title,
|
||||
detail: detail,
|
||||
choices: choices,
|
||||
choiceSelectionLimit: choiceSelectionLimit,
|
||||
selection: .init(
|
||||
get: {
|
||||
guard let strings = selection.wrappedValue else {
|
||||
return nil
|
||||
}
|
||||
return strings.map { .string($0) }
|
||||
},
|
||||
set: { newValues in
|
||||
guard let newValues else {
|
||||
selection.wrappedValue = nil
|
||||
return
|
||||
}
|
||||
|
||||
selection.wrappedValue = newValues.compactMap {
|
||||
resultValue in
|
||||
guard case let .string(value) = resultValue else {
|
||||
return nil
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/// Initializes an instance of ``MultipleChoiceQuestion`` with the provided configuration for a date result.
|
||||
/// - Parameters:
|
||||
/// - id: The unique identifier associated with this question.
|
||||
/// - title: The title for this question.
|
||||
/// - detail: The details for this question.
|
||||
/// - choices: The choices that can be selected for this question.
|
||||
/// - choiceSelectionLimit: The selection type for this question.
|
||||
/// - selection: The selected choices, where the choice values are represented as dates.
|
||||
public init(
|
||||
id: String,
|
||||
title: String,
|
||||
detail: String? = nil,
|
||||
choices: [TextChoice],
|
||||
choiceSelectionLimit: ChoiceSelectionLimit,
|
||||
selection: Binding<[Date]?>
|
||||
) {
|
||||
self.init(
|
||||
id: id,
|
||||
title: title,
|
||||
detail: detail,
|
||||
choices: choices,
|
||||
choiceSelectionLimit: choiceSelectionLimit,
|
||||
selection: .init(
|
||||
get: {
|
||||
guard let dates = selection.wrappedValue else {
|
||||
return nil
|
||||
}
|
||||
return dates.map { .date($0) }
|
||||
},
|
||||
set: { newValues in
|
||||
guard let newValues else {
|
||||
selection.wrappedValue = nil
|
||||
return
|
||||
}
|
||||
|
||||
selection.wrappedValue = newValues.compactMap {
|
||||
resultValue in
|
||||
guard case let .date(value) = resultValue else {
|
||||
return nil
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private init(
|
||||
id: String,
|
||||
title: String,
|
||||
detail: String? = nil,
|
||||
choices: [TextChoice],
|
||||
choiceSelectionLimit: ChoiceSelectionLimit,
|
||||
selection: Binding<[ResultValue]?>
|
||||
) {
|
||||
self.id = id
|
||||
self.title = title
|
||||
self.detail = detail
|
||||
self.choices = choices
|
||||
self.choiceSelectionLimit = choiceSelectionLimit
|
||||
self.selection = .manual(selection)
|
||||
}
|
||||
|
||||
/// Initializes an instance of ``MultipleChoiceQuestion`` with the provided configuration and manages a binding internally.
|
||||
/// - Parameters:
|
||||
/// - id: The unique identifier associated with this question.
|
||||
/// - title: The title for this question.
|
||||
/// - detail: The details for this question.
|
||||
/// - choices: The choices that can be selected for this question.
|
||||
/// - choiceSelectionLimit: The selection type for this question.
|
||||
public init(
|
||||
id: String,
|
||||
title: String,
|
||||
detail: String? = nil,
|
||||
choices: [TextChoice],
|
||||
choiceSelectionLimit: ChoiceSelectionLimit
|
||||
) {
|
||||
self.id = id
|
||||
self.title = title
|
||||
self.detail = detail
|
||||
self.choices = choices
|
||||
self.choiceSelectionLimit = choiceSelectionLimit
|
||||
self.selection = .automatic(key: .multipleChoice(id: id))
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
QuestionCard {
|
||||
Question(title: title, detail: detail) {
|
||||
ForEach(Array(choices.enumerated()), id: \.offset) {
|
||||
index, option in
|
||||
VStack(spacing: .zero) {
|
||||
if index != 0 {
|
||||
Divider()
|
||||
}
|
||||
|
||||
TextChoiceOption(
|
||||
title: Text(option.choiceText),
|
||||
isSelected: isSelected(option)
|
||||
) {
|
||||
choiceSelected(option)
|
||||
}
|
||||
.padding(.horizontal, 8)
|
||||
#if !os(watchOS)
|
||||
.contentShape(
|
||||
.hoverEffect, RoundedRectangle(cornerRadius: 12)
|
||||
)
|
||||
.hoverEffect()
|
||||
#endif
|
||||
.padding(.horizontal, -8)
|
||||
}
|
||||
}
|
||||
}
|
||||
.preference(
|
||||
key: QuestionRequiredPreferenceKey.self, value: isRequired
|
||||
)
|
||||
.preference(
|
||||
key: QuestionAnsweredPreferenceKey.self, value: isAnswered)
|
||||
}
|
||||
}
|
||||
|
||||
private var isAnswered: Bool {
|
||||
if let resultArray = resolvedResult.wrappedValue, !resultArray.isEmpty {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private func isSelected(_ option: TextChoice) -> Bool {
|
||||
resolvedResult.wrappedValue?.contains(where: { choice in
|
||||
choice == option.value
|
||||
}) ?? false
|
||||
}
|
||||
|
||||
private func choiceSelected(_ option: TextChoice) {
|
||||
if let resultArray = resolvedResult.wrappedValue,
|
||||
let index = resultArray.firstIndex(where: { $0 == option.value })
|
||||
{
|
||||
resolvedResult.wrappedValue?.remove(at: index)
|
||||
} else {
|
||||
switch choiceSelectionLimit {
|
||||
case .single:
|
||||
resolvedResult.wrappedValue = [option.value]
|
||||
case .multiple:
|
||||
resolvedResult.wrappedValue =
|
||||
(resolvedResult.wrappedValue ?? []) + [option.value]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MultipleChoiceQuestionView_Previews: PreviewProvider {
|
||||
|
||||
static var previews: some View {
|
||||
ZStack {
|
||||
Color.choice(for: .secondaryBackground)
|
||||
.ignoresSafeArea()
|
||||
|
||||
MultipleChoiceQuestion(
|
||||
id: UUID().uuidString,
|
||||
title: "Which do you prefer?",
|
||||
choices: [
|
||||
TextChoice(id: "a", choiceText: "Option A", value: 0),
|
||||
TextChoice(id: "b", choiceText: "Option B", value: 1),
|
||||
TextChoice(id: "c", choiceText: "Option C", value: 2),
|
||||
],
|
||||
choiceSelectionLimit: .multiple,
|
||||
selection: .constant([0])
|
||||
)
|
||||
.padding(.horizontal)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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 SwiftUI
|
||||
|
||||
struct TextChoiceOption: View {
|
||||
|
||||
var isSelected: Bool
|
||||
|
||||
var title: Text
|
||||
|
||||
var selection: () -> Void
|
||||
|
||||
public init(title: Text, isSelected: Bool, selection: @escaping () -> Void)
|
||||
{
|
||||
self.title = title
|
||||
self.selection = selection
|
||||
self.isSelected = isSelected
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
public var body: some View {
|
||||
|
||||
HStack {
|
||||
title
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(Color.choice(for: .label))
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
Image(systemName: isSelected ? "checkmark.circle.fill" : "circle")
|
||||
.imageScale(.large)
|
||||
.foregroundColor(isSelected ? .accentColor : deselectedCheckmarkColor)
|
||||
.font(.body)
|
||||
}
|
||||
.padding(.vertical, 8)
|
||||
.padding(.horizontal, 16)
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture {
|
||||
selection()
|
||||
}
|
||||
}
|
||||
|
||||
private var deselectedCheckmarkColor: Color {
|
||||
#if os(iOS)
|
||||
Color(.systemGray3)
|
||||
#else
|
||||
Color(.lightGray)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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 Foundation
|
||||
import SwiftUI
|
||||
|
||||
/// A question that allows for numeric input.
|
||||
@available(watchOS, unavailable)
|
||||
public struct NumericQuestion<Header: View>: View {
|
||||
|
||||
@EnvironmentObject
|
||||
private var managedFormResult: ResearchFormResult
|
||||
|
||||
private enum FocusTarget {
|
||||
|
||||
case numericQuestion
|
||||
|
||||
}
|
||||
|
||||
private let id: String
|
||||
private let header: Header
|
||||
private let prompt: String?
|
||||
@FocusState private var focusTarget: FocusTarget?
|
||||
private let selection: StateManagementType<Double?>
|
||||
|
||||
@Environment(\.questionContext)
|
||||
private var questionContext: QuestionContext
|
||||
|
||||
@Environment(\.questionRequired)
|
||||
private var isRequired: Bool
|
||||
|
||||
@State
|
||||
private var managedNumber: Double?
|
||||
|
||||
private var resolvedResult: Binding<Double?> {
|
||||
switch selection {
|
||||
case let .automatic(key: key):
|
||||
return Binding(
|
||||
get: {
|
||||
let number: Double?
|
||||
if let managedNumber {
|
||||
number = managedNumber
|
||||
} else {
|
||||
switch questionContext {
|
||||
case .formEmbedded:
|
||||
number = managedFormResult.resultForStep(key: key) ?? nil
|
||||
case .standalone:
|
||||
number = nil
|
||||
}
|
||||
}
|
||||
return number
|
||||
},
|
||||
set: {
|
||||
managedNumber = $0
|
||||
|
||||
if case .formEmbedded = questionContext {
|
||||
managedFormResult.setResultForStep(.numeric($0), key: key)
|
||||
}
|
||||
}
|
||||
)
|
||||
case let .manual(value):
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
QuestionCard {
|
||||
Question(
|
||||
header: {
|
||||
header
|
||||
},
|
||||
content: {
|
||||
TextField(
|
||||
"", value: resolvedResult, format: .number,
|
||||
prompt: placeholder
|
||||
)
|
||||
#if !os(watchOS) && !os(macOS)
|
||||
.keyboardType(.decimalPad)
|
||||
.focused($focusTarget, equals: .numericQuestion)
|
||||
#endif
|
||||
.doneKeyboardToolbar(
|
||||
condition: {
|
||||
focusTarget == .numericQuestion
|
||||
},
|
||||
action: {
|
||||
focusTarget = nil
|
||||
}
|
||||
)
|
||||
.padding()
|
||||
}
|
||||
)
|
||||
.preference(
|
||||
key: QuestionRequiredPreferenceKey.self, value: isRequired
|
||||
)
|
||||
.preference(
|
||||
key: QuestionAnsweredPreferenceKey.self, value: isAnswered)
|
||||
}
|
||||
}
|
||||
|
||||
private var placeholder: Text? {
|
||||
if let prompt {
|
||||
return Text(prompt)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
private var isAnswered: Bool {
|
||||
resolvedResult.wrappedValue != nil
|
||||
}
|
||||
}
|
||||
|
||||
@available(watchOS, unavailable)
|
||||
extension NumericQuestion where Header == QuestionHeader {
|
||||
|
||||
/// Initializes an instance of ``NumericQuestion`` with the provided configuration.
|
||||
/// - Parameters:
|
||||
/// - id: The unique identifier for this numeric question.
|
||||
/// - number: The entered number.
|
||||
/// - title: The title for this question.
|
||||
/// - detail: The details for this question.
|
||||
/// - prompt: The prompt that informs the user.
|
||||
public init(
|
||||
id: String,
|
||||
number: Binding<Double?>,
|
||||
title: String,
|
||||
detail: String? = nil,
|
||||
prompt: String?
|
||||
) {
|
||||
self.id = id
|
||||
header = QuestionHeader(title: title, detail: detail)
|
||||
self.prompt = prompt
|
||||
self.selection = .manual(number)
|
||||
}
|
||||
|
||||
/// Initializes an instance of ``NumericQuestion`` with the provided configuration.
|
||||
/// - Parameters:
|
||||
/// - id: The unique identifier for this question.
|
||||
/// - number: The entered number.
|
||||
/// - title: The title for this question.
|
||||
/// - detail: The details for this question.
|
||||
/// - prompt: The prompt that informs the user.
|
||||
public init(
|
||||
id: String,
|
||||
number: Decimal? = nil,
|
||||
title: String,
|
||||
detail: String? = nil,
|
||||
prompt: String?
|
||||
) {
|
||||
self.id = id
|
||||
header = QuestionHeader(title: title, detail: detail)
|
||||
self.prompt = prompt
|
||||
self.selection = .automatic(key: .numeric(id: id))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@available(watchOS, unavailable)
|
||||
struct NumericQuestionView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
ZStack {
|
||||
Color.choice(for: .secondaryBackground)
|
||||
.ignoresSafeArea()
|
||||
|
||||
ScrollView {
|
||||
NumericQuestion(
|
||||
id: UUID().uuidString,
|
||||
number: .constant(22.0),
|
||||
title: "How old are you?",
|
||||
detail: nil,
|
||||
prompt: "Tap to enter age"
|
||||
)
|
||||
.padding(.horizontal)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,797 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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 Foundation
|
||||
import SwiftUI
|
||||
|
||||
/// A configuration representing the kind of `SliderQuestion` to be used.
|
||||
enum SliderQuestionConfiguration {
|
||||
|
||||
@available(watchOS, unavailable)
|
||||
case textChoice([TextChoice])
|
||||
|
||||
case integerRange(ClosedRange<Int>)
|
||||
case doubleRange(ClosedRange<Double>)
|
||||
}
|
||||
|
||||
/// A question that allows for integer, double, or text input by means of a slider.
|
||||
public struct SliderQuestion: View {
|
||||
|
||||
private let id: String
|
||||
private let title: String
|
||||
private let detail: String?
|
||||
private let sliderQuestionConfiguration: SliderQuestionConfiguration
|
||||
private let step: Double
|
||||
|
||||
private enum ScaleSelectionBindingValue: Equatable {
|
||||
|
||||
static func == (
|
||||
lhs: SliderQuestion.ScaleSelectionBindingValue,
|
||||
rhs: SliderQuestion.ScaleSelectionBindingValue
|
||||
) -> Bool {
|
||||
switch lhs {
|
||||
case .textChoice(let binding):
|
||||
guard case .textChoice(let rhsBinding) = rhs else {
|
||||
return false
|
||||
}
|
||||
return rhsBinding.wrappedValue.id == binding.wrappedValue.id
|
||||
case .int(let binding):
|
||||
guard case .int(let rhsBinding) = rhs else {
|
||||
return false
|
||||
}
|
||||
return rhsBinding.wrappedValue == binding.wrappedValue
|
||||
case .double(let binding):
|
||||
guard case .double(let rhsBinding) = rhs else {
|
||||
return false
|
||||
}
|
||||
return rhsBinding.wrappedValue == binding.wrappedValue
|
||||
}
|
||||
}
|
||||
|
||||
case int(Binding<Int?>)
|
||||
case double(Binding<Double?>)
|
||||
|
||||
@available(watchOS, unavailable)
|
||||
case textChoice(Binding<TextChoice>)
|
||||
|
||||
}
|
||||
|
||||
private enum ScaleSelectionPrimitiveValue: Equatable {
|
||||
|
||||
static func == (
|
||||
lhs: SliderQuestion.ScaleSelectionPrimitiveValue,
|
||||
rhs: SliderQuestion.ScaleSelectionPrimitiveValue
|
||||
) -> Bool {
|
||||
switch lhs {
|
||||
case .textChoice(let lhsValue):
|
||||
guard case .textChoice(let rhsValue) = rhs else {
|
||||
return false
|
||||
}
|
||||
return lhsValue.id == rhsValue.id
|
||||
case .int(let lhsValue):
|
||||
guard case .int(let rhsValue) = rhs else {
|
||||
return false
|
||||
}
|
||||
return lhsValue == rhsValue
|
||||
case .double(let lhsValue):
|
||||
guard case .double(let rhsValue) = rhs else {
|
||||
return false
|
||||
}
|
||||
return lhsValue == rhsValue
|
||||
}
|
||||
}
|
||||
|
||||
case int(Int)
|
||||
case double(Double)
|
||||
|
||||
@available(watchOS, unavailable)
|
||||
case textChoice(TextChoice)
|
||||
|
||||
}
|
||||
|
||||
@EnvironmentObject
|
||||
private var managedFormResult: ResearchFormResult
|
||||
|
||||
@Environment(\.questionContext)
|
||||
private var questionContext: QuestionContext
|
||||
|
||||
@Environment(\.questionRequired)
|
||||
private var isRequired: Bool
|
||||
|
||||
@State
|
||||
private var managedSelectedValue: Double?
|
||||
|
||||
@State
|
||||
private var isWaitingForUserFeedback: Bool = true
|
||||
|
||||
private let stateManagementType: StateManagementType<Double>
|
||||
|
||||
private var resolvedBinding: Binding<ScaleSelectionBindingValue> {
|
||||
let resolvedBinding: Binding<ScaleSelectionBindingValue>
|
||||
switch stateManagementType {
|
||||
case .automatic:
|
||||
resolvedBinding = resolvedManagedResult
|
||||
case .manual:
|
||||
resolvedBinding = .init(
|
||||
get: {
|
||||
clientManagedSelection
|
||||
},
|
||||
// This binding isn't invoked with respect to `set` because another binding is returned in `get`.
|
||||
set: { _ in }
|
||||
)
|
||||
}
|
||||
return resolvedBinding
|
||||
}
|
||||
|
||||
// Actual underlying value of the slider
|
||||
@State
|
||||
private var sliderUIValue: Double
|
||||
|
||||
private var resolvedManagedResult: Binding<ScaleSelectionBindingValue> {
|
||||
.init(
|
||||
get: {
|
||||
// `resolvedManagedResult` is only applicable in the `automatic` case,
|
||||
// as implemented in `resolvedBinding`.
|
||||
guard case .automatic(let key) = stateManagementType else {
|
||||
// Return dummy binding since this should never be invoked.
|
||||
return .int(
|
||||
.init(
|
||||
get: {
|
||||
0
|
||||
},
|
||||
set: { _ in }
|
||||
)
|
||||
)
|
||||
}
|
||||
let scaleSelectionBinding: ScaleSelectionBindingValue
|
||||
switch sliderQuestionConfiguration {
|
||||
#if !os(watchOS)
|
||||
case .textChoice(let multipleChoiceOptions):
|
||||
scaleSelectionBinding = .textChoice(
|
||||
.init(
|
||||
get: {
|
||||
let textChoice: TextChoice
|
||||
if let managedSelectedValue {
|
||||
textChoice = multipleChoiceOptions[Int(managedSelectedValue)]
|
||||
} else {
|
||||
switch questionContext {
|
||||
case .formEmbedded:
|
||||
if let selectedIndex = managedFormResult.resultForStep(key: key) {
|
||||
textChoice = multipleChoiceOptions[Int(selectedIndex)]
|
||||
} else {
|
||||
textChoice = TextChoice(id: "", choiceText: "", value: 0)
|
||||
}
|
||||
case .standalone:
|
||||
textChoice = TextChoice(id: "", choiceText: "", value: 0)
|
||||
}
|
||||
}
|
||||
return textChoice
|
||||
},
|
||||
set: { multipleChoiceOption in
|
||||
guard
|
||||
let index =
|
||||
multipleChoiceOptions.firstIndex(
|
||||
where: {
|
||||
$0.id
|
||||
== multipleChoiceOption
|
||||
.id
|
||||
})
|
||||
else {
|
||||
return
|
||||
}
|
||||
|
||||
managedSelectedValue = Double(index)
|
||||
|
||||
if case .formEmbedded = questionContext {
|
||||
managedFormResult.setResultForStep(
|
||||
.scale(Double(index)), key: key)
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
#endif
|
||||
case .integerRange:
|
||||
scaleSelectionBinding = .int(
|
||||
.init(
|
||||
get: {
|
||||
let integerValue: Int?
|
||||
if let managedSelectedValue {
|
||||
integerValue = Int(managedSelectedValue)
|
||||
} else {
|
||||
switch questionContext {
|
||||
case .formEmbedded:
|
||||
if let value = managedFormResult.resultForStep(key: key) {
|
||||
integerValue = Int(value)
|
||||
} else {
|
||||
integerValue = 0
|
||||
}
|
||||
case .standalone:
|
||||
integerValue = 0
|
||||
}
|
||||
}
|
||||
return integerValue
|
||||
},
|
||||
set: {
|
||||
guard let result = $0 else { return }
|
||||
|
||||
managedSelectedValue = Double(result)
|
||||
|
||||
if case .formEmbedded = questionContext {
|
||||
managedFormResult.setResultForStep(
|
||||
.scale(Double(result)), key: key)
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
case .doubleRange:
|
||||
scaleSelectionBinding = .double(
|
||||
.init(
|
||||
get: {
|
||||
let doubleValue: Double
|
||||
if let managedSelectedValue {
|
||||
doubleValue = managedSelectedValue
|
||||
} else {
|
||||
switch questionContext {
|
||||
case .formEmbedded:
|
||||
if let value = managedFormResult.resultForStep(key: key) {
|
||||
doubleValue = value
|
||||
} else {
|
||||
doubleValue = 0
|
||||
}
|
||||
case .standalone:
|
||||
doubleValue = 0
|
||||
}
|
||||
}
|
||||
return doubleValue
|
||||
},
|
||||
set: {
|
||||
guard let result = $0 else { return }
|
||||
|
||||
managedSelectedValue = result
|
||||
|
||||
if case .formEmbedded = questionContext {
|
||||
managedFormResult.setResultForStep(
|
||||
.scale(result), key: key)
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return scaleSelectionBinding
|
||||
},
|
||||
set: { newValue in
|
||||
// `resolvedManagedResult` is only applicable in the `automatic` case,
|
||||
// as implemented in `resolvedBinding`.
|
||||
guard case .automatic(let key) = stateManagementType else {
|
||||
return
|
||||
}
|
||||
|
||||
switch (newValue, sliderQuestionConfiguration) {
|
||||
#if !os(watchOS)
|
||||
case let (
|
||||
.textChoice(binding), .textChoice(multipleChoiceOptions)
|
||||
):
|
||||
let index =
|
||||
multipleChoiceOptions.firstIndex(
|
||||
where: { binding.wrappedValue.id == $0.id }
|
||||
) ?? 0
|
||||
|
||||
managedSelectedValue = Double(index)
|
||||
|
||||
if case .formEmbedded = questionContext {
|
||||
managedFormResult.setResultForStep(
|
||||
.scale(Double(index)), key: key)
|
||||
}
|
||||
#endif
|
||||
case let (.int(binding), .integerRange):
|
||||
guard let result = binding.wrappedValue else { return }
|
||||
|
||||
managedSelectedValue = Double(result)
|
||||
|
||||
if case .formEmbedded = questionContext {
|
||||
managedFormResult.setResultForStep(
|
||||
.scale(Double(result)), key: key)
|
||||
}
|
||||
|
||||
case let (.double(binding), .doubleRange):
|
||||
guard let result = binding.wrappedValue else { return }
|
||||
|
||||
managedSelectedValue = result
|
||||
|
||||
if case .formEmbedded = questionContext {
|
||||
managedFormResult.setResultForStep(.scale(result), key: key)
|
||||
}
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private var clientManagedSelection: ScaleSelectionBindingValue
|
||||
|
||||
/// Initializes an instance of ``SliderQuestion`` with the provided configuration and manages the binding for double values internally.
|
||||
/// - Parameters:
|
||||
/// - id: The unique identifier for this question.
|
||||
/// - title: The title for this question.
|
||||
/// - detail: The details for this question.
|
||||
/// - range: The range of selectable values for this question.
|
||||
/// - step: The amount of change between each increment or decrement.
|
||||
/// - selection: The selected value.
|
||||
public init(
|
||||
id: String,
|
||||
title: String,
|
||||
detail: String? = nil,
|
||||
range: ClosedRange<Double>,
|
||||
step: Double = 1.0,
|
||||
selection: Double
|
||||
) {
|
||||
self.id = id
|
||||
self.title = title
|
||||
self.detail = detail
|
||||
self.sliderQuestionConfiguration = .doubleRange(range)
|
||||
self.step = step
|
||||
self.clientManagedSelection = .double(
|
||||
.init(get: { selection }, set: { _ in }))
|
||||
self.stateManagementType = .automatic(key: StepResultKey(id: id))
|
||||
self._sliderUIValue = State(wrappedValue: selection)
|
||||
}
|
||||
|
||||
/// Initializes an instance of ``SliderQuestion`` with the provided configuration for double values.
|
||||
/// - Parameters:
|
||||
/// - id: The unique identifier for this question.
|
||||
/// - title: The title for this question.
|
||||
/// - detail: The details for this question.
|
||||
/// - range: The range of selectable values for this question.
|
||||
/// - step: The amount of change between each increment or decrement.
|
||||
/// - selection: The selected value.
|
||||
public init(
|
||||
id: String,
|
||||
title: String,
|
||||
detail: String? = nil,
|
||||
range: ClosedRange<Double>,
|
||||
step: Double = 1.0,
|
||||
selection: Binding<Double?>
|
||||
) {
|
||||
self.id = id
|
||||
self.title = title
|
||||
self.detail = detail
|
||||
self.sliderQuestionConfiguration = .doubleRange(range)
|
||||
self.step = step
|
||||
self.clientManagedSelection = .double(selection)
|
||||
self.stateManagementType = .manual(
|
||||
.init(
|
||||
get: {
|
||||
Double(selection.wrappedValue ?? range.lowerBound)
|
||||
},
|
||||
set: { newValue in
|
||||
selection.wrappedValue = newValue
|
||||
}
|
||||
)
|
||||
)
|
||||
self._sliderUIValue = State(
|
||||
wrappedValue: selection.wrappedValue ?? range.lowerBound)
|
||||
}
|
||||
|
||||
/// Initializes an instance of ``SliderQuestion`` with the provided configuration for integer values.
|
||||
/// - Parameters:
|
||||
/// - id: The unique identifier for this question.
|
||||
/// - title: The title for this question.
|
||||
/// - detail: The details for this question.
|
||||
/// - range: The range of selectable values for this slider question.
|
||||
/// - step: The amount of change between each increment or decrement.
|
||||
/// - selection: The selected value.
|
||||
public init(
|
||||
id: String,
|
||||
title: String,
|
||||
detail: String? = nil,
|
||||
range: ClosedRange<Int>,
|
||||
step: Double = 1.0,
|
||||
selection: Int?
|
||||
) {
|
||||
self.id = id
|
||||
self.title = title
|
||||
self.detail = detail
|
||||
self.sliderQuestionConfiguration = .integerRange(range)
|
||||
self.step = step
|
||||
self.clientManagedSelection = .int(
|
||||
.init(get: { selection }, set: { _ in }))
|
||||
self.stateManagementType = .automatic(key: StepResultKey(id: id))
|
||||
self._sliderUIValue = State(
|
||||
wrappedValue: Double(selection ?? range.lowerBound))
|
||||
}
|
||||
|
||||
/// Initializes an instance of ``SliderQuestion`` with the provided configuration for integer values.
|
||||
/// - Parameters:
|
||||
/// - id: The unique identifier for this question.
|
||||
/// - title: The title for this question.
|
||||
/// - detail: The details for this question.
|
||||
/// - range: The range of selectable values for this slider question.
|
||||
/// - step: The amount of change between each increment or decrement.
|
||||
/// - selection: The selected value.
|
||||
public init(
|
||||
id: String,
|
||||
title: String,
|
||||
detail: String? = nil,
|
||||
range: ClosedRange<Int>,
|
||||
step: Double = 1.0,
|
||||
selection: Binding<Int?>
|
||||
) {
|
||||
self.id = id
|
||||
self.title = title
|
||||
self.detail = detail
|
||||
self.sliderQuestionConfiguration = .integerRange(range)
|
||||
self.step = step
|
||||
self.clientManagedSelection = .int(selection)
|
||||
self.stateManagementType = .manual(
|
||||
.init(
|
||||
get: {
|
||||
Double(selection.wrappedValue ?? range.lowerBound)
|
||||
},
|
||||
set: { newValue in
|
||||
selection.wrappedValue = Int(newValue)
|
||||
}
|
||||
)
|
||||
)
|
||||
self._sliderUIValue = State(
|
||||
wrappedValue: Double(selection.wrappedValue ?? range.lowerBound))
|
||||
}
|
||||
|
||||
/// Initializes an instance of ``SliderQuestion`` with the provided configuration for text values.
|
||||
/// - Parameters:
|
||||
/// - id: The unique identifier for this question.
|
||||
/// - title: The title for this question.
|
||||
/// - detail: The details for this question.
|
||||
/// - multipleChoiceOptions: The text options that this slider can represent.
|
||||
/// - selection: The selected value.
|
||||
@available(watchOS, unavailable)
|
||||
public init(
|
||||
id: String,
|
||||
title: String,
|
||||
detail: String? = nil,
|
||||
multipleChoiceOptions: [TextChoice],
|
||||
selection: TextChoice
|
||||
) {
|
||||
self.id = id
|
||||
self.title = title
|
||||
self.detail = detail
|
||||
self.sliderQuestionConfiguration = .textChoice(multipleChoiceOptions)
|
||||
self.step = 1.0
|
||||
self.clientManagedSelection = .textChoice(
|
||||
.init(get: { selection }, set: { _ in }))
|
||||
self.stateManagementType = .automatic(key: StepResultKey(id: id))
|
||||
|
||||
let index = multipleChoiceOptions.firstIndex(where: {
|
||||
selection.id == $0.id
|
||||
})
|
||||
let sliderValue = index ?? Array<TextChoice>.Index(0.0)
|
||||
self._sliderUIValue = State(wrappedValue: Double(sliderValue))
|
||||
}
|
||||
|
||||
/// Initializes an instance of ``SliderQuestion`` with the provided configuration for text values.
|
||||
/// - Parameters:
|
||||
/// - id: The unique identifier for this question.
|
||||
/// - title: The title for this question.
|
||||
/// - detail: The details for this question.
|
||||
/// - multipleChoiceOptions: The text options that this slider can represent.
|
||||
/// - selection: The selected value.
|
||||
@available(watchOS, unavailable)
|
||||
public init(
|
||||
id: String,
|
||||
title: String,
|
||||
detail: String? = nil,
|
||||
multipleChoiceOptions: [TextChoice],
|
||||
selection: Binding<TextChoice>
|
||||
) {
|
||||
self.id = id
|
||||
self.title = title
|
||||
self.detail = detail
|
||||
self.sliderQuestionConfiguration = .textChoice(multipleChoiceOptions)
|
||||
self.step = 1.0
|
||||
self.clientManagedSelection = .textChoice(selection)
|
||||
self.stateManagementType = .manual(
|
||||
.init(
|
||||
get: {
|
||||
guard
|
||||
let index = multipleChoiceOptions.firstIndex(
|
||||
where: { selection.wrappedValue.id == $0.id }
|
||||
)
|
||||
else {
|
||||
return 0
|
||||
}
|
||||
return Double(index)
|
||||
},
|
||||
set: { newValue in
|
||||
let index = Int(newValue)
|
||||
guard index >= 0, index < multipleChoiceOptions.count else {
|
||||
return
|
||||
}
|
||||
let newSelection = multipleChoiceOptions[index]
|
||||
selection.wrappedValue = newSelection
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
let index = multipleChoiceOptions.firstIndex(where: {
|
||||
selection.id == $0.id
|
||||
})
|
||||
let sliderValue = index ?? Array<TextChoice>.Index(0.0)
|
||||
self._sliderUIValue = State(wrappedValue: Double(sliderValue))
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
QuestionCard {
|
||||
Question(title: title, detail: detail) {
|
||||
scaleView(selectionConfiguration: sliderQuestionConfiguration)
|
||||
.onChange(of: sliderUIValue) { oldValue, newValue in
|
||||
isWaitingForUserFeedback = false
|
||||
switch resolvedBinding.wrappedValue {
|
||||
case .double(let doubleBinding):
|
||||
doubleBinding.wrappedValue = newValue
|
||||
case .int(let intBinding):
|
||||
intBinding.wrappedValue = Int(newValue)
|
||||
case .textChoice(let textChoiceBinding):
|
||||
guard
|
||||
case let .textChoice(array) =
|
||||
sliderQuestionConfiguration
|
||||
else {
|
||||
return
|
||||
}
|
||||
let index = Int(newValue)
|
||||
textChoiceBinding.wrappedValue = array[index]
|
||||
}
|
||||
}
|
||||
.onChange(of: clientManagedSelection) {
|
||||
oldValue, newValue in
|
||||
switch newValue {
|
||||
case .textChoice(let binding):
|
||||
guard
|
||||
case let .textChoice(array) =
|
||||
sliderQuestionConfiguration
|
||||
else {
|
||||
return
|
||||
}
|
||||
let selectedIndex =
|
||||
array.firstIndex(where: {
|
||||
$0.id == binding.wrappedValue.id
|
||||
}) ?? 0
|
||||
sliderUIValue = Double(selectedIndex)
|
||||
case .int(let binding):
|
||||
if let value = binding.wrappedValue {
|
||||
sliderUIValue = Double(value)
|
||||
}
|
||||
case .double(let binding):
|
||||
if let value = binding.wrappedValue {
|
||||
sliderUIValue = value
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.onAppear {
|
||||
guard case .automatic(let key) = stateManagementType,
|
||||
case .formEmbedded = questionContext,
|
||||
let sliderValue = managedFormResult.resultForStep(
|
||||
key: key)
|
||||
else {
|
||||
return
|
||||
}
|
||||
sliderUIValue = sliderValue
|
||||
}
|
||||
}
|
||||
.preference(
|
||||
key: QuestionRequiredPreferenceKey.self, value: isRequired
|
||||
)
|
||||
.preference(
|
||||
key: QuestionAnsweredPreferenceKey.self,
|
||||
value: !isWaitingForUserFeedback)
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func scaleView(selectionConfiguration: SliderQuestionConfiguration)
|
||||
-> some View
|
||||
{
|
||||
VStack {
|
||||
Text("\(value(for: selectionConfiguration))")
|
||||
.font(.title2)
|
||||
.fontWeight(.bold)
|
||||
.foregroundStyle(.sliderValueForegroundStyle)
|
||||
|
||||
slider(selectionConfiguration: selectionConfiguration)
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func sliderLabel(_ value: String) -> some View {
|
||||
Text(value)
|
||||
.fixedSize()
|
||||
.foregroundStyle(Color.choice(for: .label))
|
||||
.font(.subheadline)
|
||||
.fontWeight(.bold)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func slider(selectionConfiguration: SliderQuestionConfiguration)
|
||||
-> some View
|
||||
{
|
||||
Slider(
|
||||
value: $sliderUIValue,
|
||||
in: sliderBounds(for: selectionConfiguration),
|
||||
step: sliderStep(for: selectionConfiguration)
|
||||
) {
|
||||
Text("Slider for \(selectionConfiguration)")
|
||||
} minimumValueLabel: {
|
||||
sliderLabel(
|
||||
"\(minimumValueDescription(for: selectionConfiguration))")
|
||||
} maximumValueLabel: {
|
||||
sliderLabel(
|
||||
"\(maximumValueDescription(for: selectionConfiguration))")
|
||||
}
|
||||
}
|
||||
|
||||
private func value(for selectionConfiguration: SliderQuestionConfiguration)
|
||||
-> any CustomStringConvertible
|
||||
{
|
||||
let value: any CustomStringConvertible
|
||||
switch selectionConfiguration {
|
||||
case .integerRange:
|
||||
value = Int(sliderUIValue)
|
||||
case .doubleRange:
|
||||
value = String(format: "%.1f", sliderUIValue)
|
||||
case .textChoice(let choices):
|
||||
value = choices[Int(sliderUIValue)].choiceText
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
private func sliderBounds(
|
||||
for selectionConfiguration: SliderQuestionConfiguration
|
||||
) -> ClosedRange<Double> {
|
||||
let sliderBounds: ClosedRange<Double>
|
||||
switch selectionConfiguration {
|
||||
case .textChoice(let choices):
|
||||
sliderBounds = 0...Double(choices.count - 1)
|
||||
case .integerRange(let range):
|
||||
sliderBounds = Double(range.lowerBound)...Double(range.upperBound)
|
||||
case .doubleRange(let range):
|
||||
sliderBounds = range.lowerBound...range.upperBound
|
||||
}
|
||||
return sliderBounds
|
||||
}
|
||||
|
||||
private func sliderStep(
|
||||
for selectionConfiguration: SliderQuestionConfiguration
|
||||
) -> Double.Stride {
|
||||
let sliderStep: Double.Stride
|
||||
switch selectionConfiguration {
|
||||
case .textChoice:
|
||||
sliderStep = 1
|
||||
case .integerRange, .doubleRange:
|
||||
sliderStep = step
|
||||
}
|
||||
return sliderStep
|
||||
}
|
||||
|
||||
private func minimumValueDescription(
|
||||
for selectionConfiguration: SliderQuestionConfiguration
|
||||
) -> any CustomStringConvertible {
|
||||
let minimumValueLabel: any CustomStringConvertible
|
||||
switch selectionConfiguration {
|
||||
case .textChoice(let choices):
|
||||
minimumValueLabel = choices.first?.choiceText ?? ""
|
||||
case .integerRange(let range):
|
||||
minimumValueLabel = range.lowerBound
|
||||
case .doubleRange(let range):
|
||||
minimumValueLabel = range.lowerBound
|
||||
}
|
||||
return minimumValueLabel
|
||||
}
|
||||
|
||||
private func maximumValueDescription(
|
||||
for selectionConfiguration: SliderQuestionConfiguration
|
||||
) -> any CustomStringConvertible {
|
||||
let maximumValueDescription: any CustomStringConvertible
|
||||
switch selectionConfiguration {
|
||||
case .textChoice(let choices):
|
||||
maximumValueDescription = choices.last?.choiceText ?? ""
|
||||
case .integerRange(let range):
|
||||
maximumValueDescription = range.upperBound
|
||||
case .doubleRange(let range):
|
||||
maximumValueDescription = range.upperBound
|
||||
}
|
||||
return maximumValueDescription
|
||||
}
|
||||
}
|
||||
|
||||
struct ScaleSliderQuestionView_Previews: PreviewProvider {
|
||||
|
||||
static var previews: some View {
|
||||
ZStack {
|
||||
(Color.choice(for: .secondaryBackground))
|
||||
.ignoresSafeArea()
|
||||
|
||||
SliderQuestion(
|
||||
id: UUID().uuidString,
|
||||
title: "On a scale of 1-10, how would you rate today?",
|
||||
range: 1...10,
|
||||
selection: .constant(7)
|
||||
)
|
||||
.padding(.horizontal)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#Preview("Int") {
|
||||
ScrollView {
|
||||
SliderQuestion(
|
||||
id: UUID().uuidString,
|
||||
title: "On a scale of 1-10, how would you rate today?",
|
||||
range: 1...10,
|
||||
selection: .constant(7)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#Preview("Double") {
|
||||
@Previewable @State var selection: Double? = 0.0
|
||||
|
||||
ScrollView {
|
||||
SliderQuestion(
|
||||
id: UUID().uuidString,
|
||||
title: "Double Slider Question Example",
|
||||
range: 0.0...10.0,
|
||||
step: 0.1,
|
||||
selection: $selection
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#if !os(watchOS)
|
||||
#Preview("Text") {
|
||||
ScrollView {
|
||||
SliderQuestion(
|
||||
id: UUID().uuidString,
|
||||
title: "On a scale of Pun - Poem, how would rate today?",
|
||||
multipleChoiceOptions: [
|
||||
.init(id: "1", choiceText: "Pun", value: 1),
|
||||
.init(id: "2", choiceText: "Dad Joke", value: 2),
|
||||
.init(id: "3", choiceText: "Knock-Knock Joke", value: 3),
|
||||
.init(id: "4", choiceText: "One-Liner", value: 4),
|
||||
.init(id: "5", choiceText: "Parody", value: 5),
|
||||
.init(id: "5", choiceText: "Poem", value: 6),
|
||||
],
|
||||
selection: .constant(
|
||||
.init(id: "2", choiceText: "Dad Joke", value: 2)))
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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 SwiftUI
|
||||
|
||||
extension ShapeStyle where Self == SliderValueForegroundStyle {
|
||||
|
||||
/// This foreground style is used for labels that display values associated with sliders.
|
||||
static var sliderValueForegroundStyle: SliderValueForegroundStyle {
|
||||
SliderValueForegroundStyle()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct SliderValueForegroundStyle: ShapeStyle {
|
||||
|
||||
func resolve(in environment: EnvironmentValues) -> some ShapeStyle {
|
||||
#if os(visionOS)
|
||||
Color(.label)
|
||||
#else
|
||||
Color.accentColor
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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 SwiftUI
|
||||
|
||||
/// A question that allows for text input.
|
||||
public struct TextQuestion<Header: View>: View {
|
||||
|
||||
/// Represents the number of lines a text question can contain.
|
||||
public enum LineLimit {
|
||||
|
||||
/// A single line text question.
|
||||
case singleLine
|
||||
|
||||
/// A multiline text question.
|
||||
case multiline
|
||||
}
|
||||
|
||||
@EnvironmentObject
|
||||
private var managedFormResult: ResearchFormResult
|
||||
|
||||
@Environment(\.questionContext)
|
||||
private var questionContext: QuestionContext
|
||||
|
||||
@Environment(\.questionRequired)
|
||||
private var isRequired: Bool
|
||||
|
||||
@State
|
||||
private var managedText: String?
|
||||
|
||||
private enum FocusTarget {
|
||||
case textQuestion
|
||||
}
|
||||
|
||||
private let id: String
|
||||
private let header: Header
|
||||
private let multilineTextFieldPadding: Double = 54
|
||||
@FocusState private var focusTarget: FocusTarget?
|
||||
private let prompt: String?
|
||||
private let lineLimit: LineLimit
|
||||
private let characterLimit: Int
|
||||
private let hideCharacterCountLabel: Bool
|
||||
private let hideClearButton: Bool
|
||||
private let selection: StateManagementType<String?>
|
||||
|
||||
private var resolvedResult: Binding<String?> {
|
||||
switch selection {
|
||||
case let .automatic(key: key):
|
||||
return Binding(
|
||||
get: {
|
||||
let text: String?
|
||||
if let managedText {
|
||||
text = managedText
|
||||
} else {
|
||||
switch questionContext {
|
||||
case .formEmbedded:
|
||||
text = managedFormResult.resultForStep(key: key) ?? nil
|
||||
case .standalone:
|
||||
text = nil
|
||||
}
|
||||
}
|
||||
return text
|
||||
},
|
||||
set: {
|
||||
managedText = $0
|
||||
|
||||
if case .formEmbedded = questionContext {
|
||||
managedFormResult.setResultForStep(.text($0), key: key)
|
||||
}
|
||||
}
|
||||
)
|
||||
case let .manual(value):
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes an instance of ``TextQuestion`` with the provided configuration.
|
||||
/// - Parameters:
|
||||
/// - id: The unique identifier for this question.
|
||||
/// - header: The header for this question.
|
||||
/// - prompt: The placeholder for this question.
|
||||
/// - lineLimit: Specifies whether this text question is single line or multiline.
|
||||
/// - characterLimit: The number of characters that can be used for this question.
|
||||
/// - hideCharacterCountLabel: Whether or not the character count is displayed.
|
||||
/// - hideClearButton: Whether or not the clear button is displayed.
|
||||
/// - text: The entered text.
|
||||
public init(
|
||||
id: String,
|
||||
@ViewBuilder header: () -> Header,
|
||||
prompt: String?,
|
||||
lineLimit: LineLimit,
|
||||
characterLimit: Int,
|
||||
hideCharacterCountLabel: Bool = false,
|
||||
hideClearButton: Bool = false,
|
||||
text: Binding<String?>
|
||||
) {
|
||||
self.id = id
|
||||
self.header = header()
|
||||
self.prompt = prompt
|
||||
self.lineLimit = lineLimit
|
||||
self.characterLimit = characterLimit
|
||||
self.hideCharacterCountLabel = hideCharacterCountLabel
|
||||
self.hideClearButton = hideClearButton
|
||||
self.selection = .manual(text)
|
||||
}
|
||||
|
||||
/// Initializes an instance of ``TextQuestion`` with the provided configuration.
|
||||
/// - Parameters:
|
||||
/// - id: The unique identifier for this question.
|
||||
/// - header: The header for this question.
|
||||
/// - prompt: The placeholder for this question.
|
||||
/// - lineLimit: Specifies whether this text question is single line or multiline.
|
||||
/// - characterLimit: The number of characters that can be used for this text question.
|
||||
/// - hideCharacterCountLabel: Whether or not the character count is displayed.
|
||||
/// - hideClearButton: Whether or not the clear button is displayed.
|
||||
public init(
|
||||
id: String,
|
||||
@ViewBuilder header: () -> Header,
|
||||
prompt: String?,
|
||||
lineLimit: LineLimit,
|
||||
characterLimit: Int,
|
||||
hideCharacterCountLabel: Bool = false,
|
||||
hideClearButton: Bool = false
|
||||
) {
|
||||
self.id = id
|
||||
self.header = header()
|
||||
self.prompt = prompt
|
||||
self.lineLimit = lineLimit
|
||||
self.characterLimit = characterLimit > 0 ? characterLimit : .max
|
||||
self.hideCharacterCountLabel = hideCharacterCountLabel
|
||||
self.hideClearButton = hideClearButton
|
||||
self.selection = .automatic(key: .text(id: id))
|
||||
}
|
||||
|
||||
private var axis: Axis {
|
||||
switch lineLimit {
|
||||
case .singleLine:
|
||||
return .horizontal
|
||||
case .multiline:
|
||||
return .vertical
|
||||
}
|
||||
}
|
||||
|
||||
private var placeholder: Text? {
|
||||
if let prompt {
|
||||
return Text(prompt)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
QuestionCard {
|
||||
Question {
|
||||
header
|
||||
} content: {
|
||||
VStack {
|
||||
TextField(
|
||||
id, text: resolvedResult.unwrapped(defaultValue: ""),
|
||||
prompt: placeholder, axis: axis
|
||||
)
|
||||
.textFieldStyle(.plain) // Text binding's `didSet` called twice if this is not set.
|
||||
.focused($focusTarget, equals: .textQuestion)
|
||||
.padding(
|
||||
.bottom,
|
||||
axis == .vertical ? multilineTextFieldPadding : .zero
|
||||
)
|
||||
.contentShape(Rectangle())
|
||||
.onAppear(perform: {
|
||||
#if !os(watchOS)
|
||||
if lineLimit == .singleLine {
|
||||
UITextField.appearance().clearButtonMode =
|
||||
.whileEditing
|
||||
}
|
||||
#endif
|
||||
})
|
||||
|
||||
if lineLimit == .multiline {
|
||||
HStack {
|
||||
if hideCharacterCountLabel == false {
|
||||
Text(
|
||||
"\(resolvedResult.wrappedValue?.count ?? 0)/\(characterLimit)"
|
||||
)
|
||||
}
|
||||
Spacer()
|
||||
|
||||
if !hideClearButton {
|
||||
Button {
|
||||
resolvedResult.wrappedValue = .none
|
||||
} label: {
|
||||
Text("Clear")
|
||||
}
|
||||
}
|
||||
}
|
||||
.onChange(of: resolvedResult.wrappedValue) {
|
||||
oldValue, newValue in
|
||||
if resolvedResult.wrappedValue?.count ?? 0
|
||||
> characterLimit
|
||||
{
|
||||
resolvedResult.wrappedValue = oldValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
#if os(iOS)
|
||||
.doneKeyboardToolbar(
|
||||
condition: {
|
||||
focusTarget == .textQuestion
|
||||
},
|
||||
action: {
|
||||
focusTarget = nil
|
||||
}
|
||||
)
|
||||
#endif
|
||||
}
|
||||
.preference(
|
||||
key: QuestionRequiredPreferenceKey.self, value: isRequired
|
||||
)
|
||||
.preference(
|
||||
key: QuestionAnsweredPreferenceKey.self, value: isAnswered)
|
||||
}
|
||||
}
|
||||
|
||||
private var isAnswered: Bool {
|
||||
if let result = resolvedResult.wrappedValue {
|
||||
return !result.isEmpty
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
extension TextQuestion where Header == QuestionHeader {
|
||||
|
||||
/// Initializes an instance of ``TextQuestion`` with the provided configuration.
|
||||
/// - Parameters:
|
||||
/// - id: The unique identifier for this text question.
|
||||
/// - title: The title for this text question.
|
||||
/// - detail: The details for this text question.
|
||||
/// - prompt: The placeholder for this text question.
|
||||
/// - lineLimit: Specifies whether this text question is single line or multiline.
|
||||
/// - characterLimit: The number of characters that can be used for this text question.
|
||||
/// - hideCharacterCountLabel: Whether or not the character count is displayed.
|
||||
/// - hideClearButton: Whether or not the clear button is displayed.
|
||||
/// - text: The entered text.
|
||||
public init(
|
||||
id: String,
|
||||
title: String,
|
||||
detail: String?,
|
||||
prompt: String?,
|
||||
lineLimit: LineLimit,
|
||||
characterLimit: Int,
|
||||
hideCharacterCountLabel: Bool = false,
|
||||
hideClearButton: Bool = false,
|
||||
text: Binding<String?>
|
||||
) {
|
||||
self.id = id
|
||||
self.header = QuestionHeader(title: title, detail: detail)
|
||||
self.prompt = prompt
|
||||
self.lineLimit = lineLimit
|
||||
self.characterLimit = characterLimit
|
||||
self.hideCharacterCountLabel = hideCharacterCountLabel
|
||||
self.hideClearButton = hideClearButton
|
||||
self.selection = .manual(text)
|
||||
}
|
||||
|
||||
/// Initializes an instance of ``TextQuestion`` with the provided configuration.
|
||||
/// - Parameters:
|
||||
/// - id: The unique identifier for this text question.
|
||||
/// - title: The title for this text question.
|
||||
/// - detail: The details for this text question.
|
||||
/// - prompt: The placeholder for this text question.
|
||||
/// - lineLimit: Specifies whether this text question is single line or multiline.
|
||||
/// - characterLimit: The number of characters that can be used for this text question.
|
||||
/// - hideCharacterCountLabel: Whether or not the character count is displayed.
|
||||
/// - hideClearButton: Whether or not the clear button is displayed.
|
||||
/// - defaultTextAnswer: The initial text to display.
|
||||
public init(
|
||||
id: String,
|
||||
title: String,
|
||||
detail: String? = nil,
|
||||
prompt: String?,
|
||||
lineLimit: LineLimit,
|
||||
characterLimit: Int,
|
||||
hideCharacterCountLabel: Bool = false,
|
||||
hideClearButton: Bool = false,
|
||||
defaultTextAnswer: String? = nil
|
||||
) {
|
||||
self.id = id
|
||||
self.header = QuestionHeader(title: title, detail: detail)
|
||||
self.prompt = prompt
|
||||
self.lineLimit = lineLimit
|
||||
self.characterLimit = characterLimit
|
||||
self.hideCharacterCountLabel = hideCharacterCountLabel
|
||||
self.hideClearButton = hideClearButton
|
||||
self.selection = .automatic(key: StepResultKey(id: id))
|
||||
|
||||
if let defaultTextAnswer {
|
||||
self.resolvedResult.wrappedValue = defaultTextAnswer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
@Previewable @State var value: String? = "Tom Riddle"
|
||||
ScrollView {
|
||||
TextQuestion(
|
||||
id: UUID().uuidString,
|
||||
title: "What is your name?",
|
||||
detail: nil,
|
||||
prompt: "Tap to write",
|
||||
lineLimit: .singleLine,
|
||||
characterLimit: 10,
|
||||
hideCharacterCountLabel: true,
|
||||
text: $value
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,647 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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 SwiftUI
|
||||
|
||||
/// The precision representing the granularity of a measurement's value. (e.g. 68 kg vs. 68.04 kg, or 150 lbs vs. 150 lbs 5 oz)
|
||||
public enum NumericPrecision {
|
||||
case `default`, low, high
|
||||
}
|
||||
|
||||
private class WeightFormatter {
|
||||
|
||||
static let shared = WeightFormatter()
|
||||
|
||||
private let weightFormatter: NumberFormatter
|
||||
|
||||
private init() {
|
||||
let weightFormatter = NumberFormatter()
|
||||
weightFormatter.numberStyle = .decimal
|
||||
weightFormatter.roundingMode = .halfUp
|
||||
self.weightFormatter = weightFormatter
|
||||
}
|
||||
|
||||
func string(for number: Double, precision: NumericPrecision) -> String? {
|
||||
switch precision {
|
||||
case .default:
|
||||
weightFormatter.minimumFractionDigits = 1
|
||||
weightFormatter.maximumFractionDigits = 1
|
||||
case .low:
|
||||
weightFormatter.minimumFractionDigits = 0
|
||||
weightFormatter.maximumFractionDigits = 0
|
||||
case .high:
|
||||
weightFormatter.minimumFractionDigits = 2
|
||||
weightFormatter.maximumFractionDigits = 2
|
||||
}
|
||||
return weightFormatter.string(for: number)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// A question that allows for weight input.
|
||||
public struct WeightQuestion: View {
|
||||
|
||||
@EnvironmentObject
|
||||
private var managedFormResult: ResearchFormResult
|
||||
|
||||
@Environment(\.questionContext)
|
||||
private var questionContext: QuestionContext
|
||||
|
||||
@State private var isInputActive = false
|
||||
@State private var hasChanges: Bool
|
||||
|
||||
@Environment(\.questionRequired)
|
||||
private var isRequired: Bool
|
||||
|
||||
@State
|
||||
private var managedWeight: Double?
|
||||
|
||||
private let defaultWeightInKilograms = 68.039
|
||||
|
||||
private let id: String
|
||||
private let title: String
|
||||
private let detail: String?
|
||||
private let measurementSystem: MeasurementSystem
|
||||
private let precision: NumericPrecision
|
||||
private let defaultValue: Double?
|
||||
private let minimumValue: Double?
|
||||
private let maximumValue: Double?
|
||||
private let selection: StateManagementType<Double?>
|
||||
|
||||
private var resolvedResult: Binding<Double?> {
|
||||
switch selection {
|
||||
case let .automatic(key: key):
|
||||
return Binding(
|
||||
get: {
|
||||
let weight: Double?
|
||||
if let managedWeight {
|
||||
weight = managedWeight
|
||||
} else {
|
||||
switch questionContext {
|
||||
case .formEmbedded:
|
||||
weight = managedFormResult.resultForStep(key: key) ?? nil
|
||||
case .standalone:
|
||||
weight = nil
|
||||
}
|
||||
}
|
||||
return weight
|
||||
},
|
||||
set: {
|
||||
managedWeight = $0
|
||||
|
||||
if case .formEmbedded = questionContext {
|
||||
managedFormResult.setResultForStep(.numeric($0), key: key)
|
||||
}
|
||||
}
|
||||
)
|
||||
case let .manual(value):
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes an instance of ``WeightQuestion`` with the provided configuration.
|
||||
/// - Parameters:
|
||||
/// - id: The unique identifier for this question.
|
||||
/// - title: The title for this question.
|
||||
/// - detail: The details for this question.
|
||||
/// - measurementSystem: The measurement system for this question.
|
||||
/// - precision: The precision for this question.
|
||||
/// - defaultValue: The default weight.
|
||||
/// - minimumValue: The minimum selectable weight.
|
||||
/// - maximumValue: The maximum selectable weight.
|
||||
public init(
|
||||
id: String,
|
||||
title: String,
|
||||
detail: String? = nil,
|
||||
measurementSystem: MeasurementSystem,
|
||||
precision: NumericPrecision = .default,
|
||||
defaultValue: Double?,
|
||||
minimumValue: Double?,
|
||||
maximumValue: Double?
|
||||
) {
|
||||
self.id = id
|
||||
self.hasChanges = false
|
||||
self.title = title
|
||||
self.detail = detail
|
||||
|
||||
let system: MeasurementSystem = {
|
||||
switch measurementSystem {
|
||||
case .USC:
|
||||
return .USC
|
||||
case .local:
|
||||
if Locale.current.measurementSystem == .us {
|
||||
return .USC
|
||||
} else {
|
||||
return .metric
|
||||
}
|
||||
case .metric:
|
||||
return .metric
|
||||
}
|
||||
}()
|
||||
|
||||
self.measurementSystem = system
|
||||
self.precision = precision
|
||||
self.defaultValue = defaultValue
|
||||
self.minimumValue = minimumValue
|
||||
self.maximumValue = maximumValue
|
||||
self.selection = .automatic(key: .weight(id: id))
|
||||
}
|
||||
|
||||
/// Initializes an instance of ``WeightQuestion`` with the provided configuration.
|
||||
/// - Parameters:
|
||||
/// - id: The unique identifier for this question.
|
||||
/// - title: The title for this question.
|
||||
/// - detail: The details for this question.
|
||||
/// - measurementSystem: The measurement system for this question.
|
||||
/// - precision: The precision for this question.
|
||||
/// - defaultValue: The default weight.
|
||||
/// - minimumValue: The minimum selectable weight.
|
||||
/// - maximumValue: The maximum selectable weight.
|
||||
/// - weight: The selected weight.
|
||||
public init(
|
||||
id: String,
|
||||
title: String,
|
||||
detail: String? = nil,
|
||||
measurementSystem: MeasurementSystem,
|
||||
precision: NumericPrecision = .default,
|
||||
defaultValue: Double?,
|
||||
minimumValue: Double?,
|
||||
maximumValue: Double?,
|
||||
weight: Binding<Double?>
|
||||
) {
|
||||
self.id = id
|
||||
self.hasChanges = false
|
||||
self.title = title
|
||||
self.detail = detail
|
||||
|
||||
let system: MeasurementSystem = {
|
||||
switch measurementSystem {
|
||||
case .USC:
|
||||
return .USC
|
||||
case .local:
|
||||
if Locale.current.measurementSystem == .us {
|
||||
return .USC
|
||||
} else {
|
||||
return .metric
|
||||
}
|
||||
case .metric:
|
||||
return .metric
|
||||
}
|
||||
}()
|
||||
|
||||
self.measurementSystem = system
|
||||
self.precision = precision
|
||||
self.defaultValue = defaultValue
|
||||
self.minimumValue = minimumValue
|
||||
self.maximumValue = maximumValue
|
||||
self.selection = .manual(weight)
|
||||
}
|
||||
|
||||
private var selectionString: String {
|
||||
let selectedResult: Double
|
||||
if let result = resolvedResult.wrappedValue {
|
||||
selectedResult = result
|
||||
} else {
|
||||
selectedResult = defaultValue ?? defaultWeightInKilograms
|
||||
}
|
||||
|
||||
let (pounds, ounces) = convertKilogramsToPoundsAndOunces(selectedResult)
|
||||
if measurementSystem == .USC {
|
||||
switch precision {
|
||||
case .default, .low:
|
||||
return "\(Int(pounds)) lb"
|
||||
case .high:
|
||||
return "\(Int(pounds)) lb \(Int(ounces)) oz"
|
||||
}
|
||||
} else {
|
||||
if resolvedResult.wrappedValue == defaultWeightInKilograms {
|
||||
// 68.039 isn't exactly the prettiest value, but it maps
|
||||
// nice to 150 pounds, so if the user sticks with the default
|
||||
// we'll round to the nearest result which in our case would be 60kg.
|
||||
return "\(selectedResult.rounded()) kg"
|
||||
}
|
||||
|
||||
return
|
||||
"\(WeightFormatter.shared.string(for: selectedResult, precision: precision) ?? "") kg"
|
||||
}
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
QuestionCard {
|
||||
Question(title: title, detail: detail) {
|
||||
HStack {
|
||||
Text("Select Weight")
|
||||
.foregroundStyle(Color.primary)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
Button {
|
||||
isInputActive = true
|
||||
|
||||
#if !os(watchOS)
|
||||
UIApplication.shared.endEditing()
|
||||
#endif
|
||||
} label: {
|
||||
Text(selectionString)
|
||||
.foregroundStyle(Color.primary)
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.buttonBorderShape(.roundedRectangle)
|
||||
#if os(watchOS)
|
||||
.navigationDestination(isPresented: $isInputActive) {
|
||||
WeightPickerView(
|
||||
measurementSystem: measurementSystem,
|
||||
precision: precision,
|
||||
defaultValue: defaultValue,
|
||||
minimumValue: minimumValue,
|
||||
maximumValue: maximumValue,
|
||||
selection: .init(
|
||||
get: {
|
||||
resolvedResult.wrappedValue
|
||||
?? defaultValue
|
||||
?? defaultWeightInKilograms
|
||||
},
|
||||
set: {
|
||||
resolvedResult.wrappedValue = $0
|
||||
}
|
||||
),
|
||||
hasChanges: $hasChanges
|
||||
)
|
||||
}
|
||||
#else
|
||||
.popover(
|
||||
isPresented: $isInputActive,
|
||||
attachmentAnchor: .point(.bottom),
|
||||
arrowEdge: .top
|
||||
) {
|
||||
WeightPickerView(
|
||||
measurementSystem: measurementSystem,
|
||||
precision: precision,
|
||||
defaultValue: defaultValue
|
||||
?? defaultWeightInKilograms,
|
||||
minimumValue: minimumValue,
|
||||
maximumValue: maximumValue,
|
||||
selection: .init(
|
||||
get: {
|
||||
resolvedResult.wrappedValue
|
||||
?? defaultValue
|
||||
?? defaultWeightInKilograms
|
||||
},
|
||||
set: {
|
||||
resolvedResult.wrappedValue = $0
|
||||
}
|
||||
),
|
||||
hasChanges: $hasChanges
|
||||
)
|
||||
.frame(width: 300)
|
||||
.presentationCompactAdaptation((.popover))
|
||||
}
|
||||
#endif
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
.preference(
|
||||
key: QuestionRequiredPreferenceKey.self, value: isRequired
|
||||
)
|
||||
.preference(
|
||||
key: QuestionAnsweredPreferenceKey.self, value: isAnswered)
|
||||
}
|
||||
}
|
||||
|
||||
private var isAnswered: Bool {
|
||||
resolvedResult.wrappedValue != nil
|
||||
}
|
||||
}
|
||||
|
||||
struct WeightPickerView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
||||
private let measurementSystem: MeasurementSystem
|
||||
private let precision: NumericPrecision
|
||||
private let defaultValue: Double?
|
||||
private let minimumValue: Double?
|
||||
private let maximumValue: Double?
|
||||
|
||||
@Binding private var selection: Double?
|
||||
@Binding private var hasChanges: Bool
|
||||
|
||||
@State private var highPrecisionSelection: Int = 0
|
||||
@State private var selectionOne: Double
|
||||
@State private var selectionTwo: Double
|
||||
|
||||
private static let defaultValueInKilograms: Double = 68.0
|
||||
private static let defaultValueInPounds: Double = 150.0
|
||||
|
||||
private var lowerValue: Double {
|
||||
guard let minimumValue else { return 0 }
|
||||
return minimumValue
|
||||
}
|
||||
|
||||
private var upperValue: Double {
|
||||
if measurementSystem == .USC {
|
||||
guard let maximumValue else { return 1_450 }
|
||||
return maximumValue
|
||||
} else {
|
||||
guard let maximumValue else {
|
||||
switch precision {
|
||||
case .low, .high:
|
||||
return 657
|
||||
case .default:
|
||||
return 657.5
|
||||
}
|
||||
}
|
||||
return maximumValue
|
||||
}
|
||||
}
|
||||
|
||||
private var primaryStep: Double {
|
||||
if measurementSystem != .USC {
|
||||
switch precision {
|
||||
case .default:
|
||||
return 0.5
|
||||
case .low, .high:
|
||||
return 1
|
||||
}
|
||||
} else {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
private var secondaryStep: Double {
|
||||
if measurementSystem == .USC {
|
||||
return 1
|
||||
} else {
|
||||
return 0.01
|
||||
}
|
||||
}
|
||||
|
||||
private var primaryUnit: String {
|
||||
if measurementSystem == .USC {
|
||||
return "lb"
|
||||
} else {
|
||||
return "kg"
|
||||
}
|
||||
}
|
||||
|
||||
private var primaryRange: [Double] {
|
||||
var range: [Double] = []
|
||||
for i in stride(from: lowerValue, through: upperValue, by: primaryStep)
|
||||
{
|
||||
range.append(i)
|
||||
}
|
||||
return range
|
||||
}
|
||||
|
||||
private var secondaryRange: [Double] {
|
||||
let upperValue = measurementSystem == .USC ? 15 : 0.99
|
||||
var range: [Double] = []
|
||||
for i in stride(
|
||||
from: lowerValue, through: upperValue, by: secondaryStep)
|
||||
{
|
||||
if case .USC = measurementSystem {
|
||||
range.append(i)
|
||||
} else {
|
||||
let decimal = Decimal(i).rounded(2, .plain)
|
||||
range.append(
|
||||
NSDecimalNumber(decimal: decimal).doubleValue
|
||||
)
|
||||
}
|
||||
}
|
||||
return range
|
||||
}
|
||||
|
||||
init(
|
||||
measurementSystem: MeasurementSystem = .metric,
|
||||
precision: NumericPrecision = .default,
|
||||
defaultValue: Double? = nil,
|
||||
minimumValue: Double? = nil,
|
||||
maximumValue: Double? = nil,
|
||||
selection: Binding<Double?>,
|
||||
hasChanges: Binding<Bool>
|
||||
) {
|
||||
self.measurementSystem = measurementSystem
|
||||
self.precision = precision
|
||||
self.defaultValue = defaultValue
|
||||
self.minimumValue = minimumValue
|
||||
self.maximumValue = maximumValue
|
||||
self._selection = selection
|
||||
self._hasChanges = hasChanges
|
||||
|
||||
let displayedValue = selection.wrappedValue ?? defaultValue ?? 0
|
||||
|
||||
let selectionOneValue: Double = {
|
||||
if measurementSystem == .USC {
|
||||
return convertKilogramsToPoundsAndOunces(displayedValue).pounds
|
||||
} else {
|
||||
let roundedSelectionOne: Double
|
||||
switch precision {
|
||||
case .default:
|
||||
roundedSelectionOne = (displayedValue * 10).rounded() / 10
|
||||
case .low:
|
||||
roundedSelectionOne = displayedValue.rounded()
|
||||
case .high:
|
||||
roundedSelectionOne = floor(displayedValue)
|
||||
}
|
||||
return roundedSelectionOne
|
||||
}
|
||||
}()
|
||||
self.selectionOne = selectionOneValue
|
||||
|
||||
let selectionTwoValue: Double = {
|
||||
if measurementSystem == .USC {
|
||||
return convertKilogramsToPoundsAndOunces(displayedValue).ounces
|
||||
} else {
|
||||
if case .high = precision {
|
||||
let selectionTwo = Decimal(displayedValue)
|
||||
let selectionTwoFraction =
|
||||
selectionTwo.rounded(2, .plain)
|
||||
- selectionTwo.rounded(0, .down)
|
||||
return NSDecimalNumber(decimal: selectionTwoFraction)
|
||||
.doubleValue
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}()
|
||||
self.selectionTwo = selectionTwoValue
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
HStack(spacing: .zero) {
|
||||
|
||||
Picker(selection: $selectionOne) {
|
||||
ForEach(primaryRange, id: \.self) { i in
|
||||
Text(primaryPickerString(for: i))
|
||||
.tag(i)
|
||||
}
|
||||
} label: {
|
||||
Text(primaryUnit)
|
||||
}
|
||||
.pickerStyle(.wheel)
|
||||
.onChange(of: selectionOne) { _, _ in
|
||||
selection = standardizedWeight((selectionOne, selectionTwo))
|
||||
hasChanges = true
|
||||
}
|
||||
|
||||
if precision == .high {
|
||||
Picker(selection: $selectionTwo) {
|
||||
ForEach(secondaryRange, id: \.self) { i in
|
||||
Text(secondaryPickerString(for: i))
|
||||
.tag(i)
|
||||
}
|
||||
} label: {
|
||||
if measurementSystem == .USC {
|
||||
Text("oz")
|
||||
}
|
||||
}
|
||||
.pickerStyle(.wheel)
|
||||
.onChange(of: selectionTwo) { _, _ in
|
||||
selection = standardizedWeight((selectionOne, selectionTwo))
|
||||
hasChanges = true
|
||||
}
|
||||
}
|
||||
|
||||
if measurementSystem != .USC,
|
||||
precision == .high
|
||||
{
|
||||
Picker(selection: $highPrecisionSelection) {
|
||||
ForEach(0..<1, id: \.self) { i in
|
||||
Text("\(primaryUnit)")
|
||||
.tag(i)
|
||||
}
|
||||
} label: {
|
||||
Text("Tap Here")
|
||||
}
|
||||
.pickerStyle(.wheel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func primaryPickerString(
|
||||
for value: Double
|
||||
) -> String {
|
||||
let formatter = NumberFormatter()
|
||||
|
||||
let fractionalDigits: Int = {
|
||||
if measurementSystem != .USC && precision == .default {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}()
|
||||
|
||||
formatter.minimumFractionDigits = fractionalDigits
|
||||
formatter.minimumIntegerDigits =
|
||||
measurementSystem != .USC && self.precision == .high ? 0 : 1
|
||||
|
||||
let string = formatter.string(for: value) ?? "Unknown"
|
||||
|
||||
let includeUnit: Bool = {
|
||||
if measurementSystem != .USC && precision == .high {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}()
|
||||
|
||||
let finalString = includeUnit ? "\(string) \(primaryUnit)" : string
|
||||
return finalString
|
||||
}
|
||||
|
||||
private func secondaryPickerString(
|
||||
for value: Double
|
||||
) -> String {
|
||||
let formatter = NumberFormatter()
|
||||
|
||||
let fractionalDigits: Int = {
|
||||
if measurementSystem != .USC && precision == .high {
|
||||
return 2
|
||||
}
|
||||
return 0
|
||||
}()
|
||||
|
||||
formatter.minimumFractionDigits = fractionalDigits
|
||||
formatter.minimumIntegerDigits =
|
||||
measurementSystem != .USC && self.precision == .high ? 0 : 1
|
||||
|
||||
let string = formatter.string(for: value) ?? "Unknown"
|
||||
|
||||
let includeUnit: Bool = {
|
||||
if measurementSystem == .USC && precision == .high {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}()
|
||||
|
||||
let finalString = includeUnit ? "\(string) oz" : string
|
||||
return finalString
|
||||
}
|
||||
|
||||
private func standardizedWeight(_ weight: (Double, Double)) -> Double {
|
||||
if measurementSystem == .USC {
|
||||
return convertPoundsAndOuncesToKilograms(
|
||||
pounds: weight.0, ounces: weight.1)
|
||||
} else {
|
||||
switch precision {
|
||||
case .low, .default:
|
||||
return weight.0
|
||||
case .high:
|
||||
return weight.0 + weight.1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 18.0, *)
|
||||
#Preview {
|
||||
@Previewable @State var selection: Double? = 133
|
||||
NavigationStack {
|
||||
WeightQuestion(
|
||||
id: UUID().uuidString,
|
||||
title: "Weight question here",
|
||||
detail: nil,
|
||||
measurementSystem: .USC,
|
||||
precision: .high,
|
||||
defaultValue: 150,
|
||||
minimumValue: 0,
|
||||
maximumValue: 1430,
|
||||
weight: $selection
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension Decimal {
|
||||
|
||||
fileprivate func rounded(
|
||||
_ scale: Int, _ roundingMode: NSDecimalNumber.RoundingMode
|
||||
) -> Decimal {
|
||||
var result = Decimal()
|
||||
var localCopy = self
|
||||
NSDecimalRound(&result, &localCopy, scale, roundingMode)
|
||||
return result
|
||||
}
|
||||
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
# ``ResearchKitSwiftUI``
|
||||
|
||||
## Overview
|
||||
|
||||
ResearchKit SwiftUI marks the beginning of a gradual transition from UIKit to SwiftUI. The goal is to provide the same solutions that the UIKit implementation of ResearchKit offers today in a way that is aligned with SwiftUI's philosophies. We are starting this transition with new additions for a form, step, and some frequently used question formats.
|
||||
|
||||
## Topics
|
||||
|
||||
### Adding Questions to a Form
|
||||
|
||||
- ``ResearchForm``
|
||||
- ``ResearchFormStep``
|
||||
- ``ResearchFormCompletion``
|
||||
|
||||
### Headers
|
||||
|
||||
- ``StepHeader``
|
||||
- ``QuestionHeader``
|
||||
|
||||
### Multiple Choice Question
|
||||
|
||||
- ``MultipleChoiceQuestion``
|
||||
|
||||
### Measurement Questions
|
||||
|
||||
- ``HeightQuestion``
|
||||
- ``WeightQuestion``
|
||||
|
||||
### Slider Question
|
||||
|
||||
- ``SliderQuestion``
|
||||
|
||||
### Text Question
|
||||
|
||||
- ``TextQuestion``
|
||||
|
||||
### Date and Time Question
|
||||
|
||||
- ``DateTimeQuestion``
|
||||
|
||||
### Numeric Question
|
||||
|
||||
- ``NumericQuestion``
|
||||
|
||||
### Image Choice Question
|
||||
|
||||
- ``ImageChoiceQuestion``
|
||||
- ``ImageChoice``
|
||||
|
||||
### Instructions
|
||||
|
||||
- ``InstructionBodyItem``
|
||||
|
||||
### Results
|
||||
|
||||
- ``ResearchFormResult``
|
||||
- ``AnswerFormat``
|
||||
- ``Result``
|
||||
- ``ResultValue``
|
||||
|
||||
### Selection Type
|
||||
|
||||
- ``ChoiceSelectionLimit``
|
||||
|
||||
### Text Choice
|
||||
|
||||
- ``TextChoice``
|
||||
|
||||
### Measurement Type
|
||||
|
||||
- ``MeasurementSystem``
|
||||
|
||||
### Precision Type
|
||||
|
||||
- ``NumericPrecision``
|
||||
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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 Combine
|
||||
import SwiftUI
|
||||
|
||||
/// Represents responses for the different kinds of questions.
|
||||
public enum AnswerFormat {
|
||||
case text(String?)
|
||||
case numeric(Double?)
|
||||
case date(Date?)
|
||||
case weight(Double?)
|
||||
case height(Double?)
|
||||
case multipleChoice([ResultValue]?)
|
||||
case image([ResultValue]?)
|
||||
case scale(Double?)
|
||||
}
|
||||
|
||||
extension AnswerFormat: Codable {}
|
||||
|
||||
extension AnswerFormat: Equatable {
|
||||
|
||||
public static func == (lhs: AnswerFormat, rhs: AnswerFormat) -> Bool {
|
||||
let isEqual: Bool
|
||||
|
||||
switch (lhs, rhs) {
|
||||
case let (.text(lhsValue), .text(rhsValue)):
|
||||
isEqual = lhsValue == rhsValue
|
||||
case let (.numeric(lhsValue), .numeric(rhsValue)):
|
||||
isEqual = lhsValue == rhsValue
|
||||
case let (.date(lhsValue), .date(rhsValue)):
|
||||
isEqual = lhsValue == rhsValue
|
||||
case let (.weight(lhsValue), .weight(rhsValue)):
|
||||
isEqual = lhsValue == rhsValue
|
||||
case let (.height(lhsValue), .height(rhsValue)):
|
||||
isEqual = lhsValue == rhsValue
|
||||
case let (.multipleChoice(lhsValue), .multipleChoice(rhsValue)):
|
||||
isEqual = lhsValue == rhsValue
|
||||
case let (.image(lhsValue), .image(rhsValue)):
|
||||
isEqual = lhsValue == rhsValue
|
||||
case let (.scale(lhsValue), .scale(rhsValue)):
|
||||
isEqual = lhsValue == rhsValue
|
||||
default:
|
||||
isEqual = false
|
||||
|
||||
}
|
||||
|
||||
return isEqual
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Captures the responses to questions for which no binding has been provided. It is passed through ``ResearchForm``'s `onResearchFormCompletion` handler, which is passed through the initializer, upon survey completion.
|
||||
public final class ResearchFormResult: ObservableObject {
|
||||
|
||||
private var stepResults: [String: AnswerFormat]
|
||||
|
||||
/// Initializes an instance of ``ResearchFormResult`` that contains no responses.
|
||||
public convenience init() {
|
||||
self.init(results: [])
|
||||
}
|
||||
|
||||
/// Initializes an instance of ``ResearchFormResult`` with the provided configuration.
|
||||
/// - Parameter results: The results from which an instance of `ResearchFormResult` is created.
|
||||
public init(results: [Result]) {
|
||||
stepResults = results.reduce(into: [String: AnswerFormat]()) {
|
||||
partialResult, result in
|
||||
partialResult[result.identifier] = result.answer
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes an instance of ``ResearchFormResult`` with the provided configuration.
|
||||
/// - Parameter decoder: The decoder used to deserialize an instance of `ResearchFormResult`.
|
||||
public init(from decoder: any Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
stepResults = try container.decode(
|
||||
[String: AnswerFormat].self, forKey: .stepResults)
|
||||
}
|
||||
|
||||
func resultForStep<Result>(key: StepResultKey<Result>) -> Result? {
|
||||
let answerFormat = stepResults[key.id]
|
||||
switch answerFormat {
|
||||
case let .text(answer):
|
||||
return answer as? Result
|
||||
case .numeric(let decimal):
|
||||
return decimal as? Result
|
||||
case .date(let date):
|
||||
return date as? Result
|
||||
case .height(let height):
|
||||
return height as? Result
|
||||
case .weight(let weight):
|
||||
return weight as? Result
|
||||
case .image(let image):
|
||||
return image as? Result
|
||||
case .multipleChoice(let multipleChoice):
|
||||
return multipleChoice as? Result
|
||||
case .scale(let double):
|
||||
return double as? Result
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func setResultForStep<Result>(
|
||||
_ format: AnswerFormat, key: StepResultKey<Result>
|
||||
) {
|
||||
stepResults[key.id] = format
|
||||
}
|
||||
|
||||
/// Maps the captured responses to a type of your choice.
|
||||
/// - Parameter transform: The mapping function used to transform a response to a type of your choice.
|
||||
/// - Returns: An array containing the type to which the responses were transformed.
|
||||
public func compactMap<T>(_ transform: (Result) -> T?) -> [T] {
|
||||
stepResults.compactMap { entry in
|
||||
transform(
|
||||
Result(identifier: entry.key, answer: entry.value)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The response context for a question.
|
||||
public struct Result {
|
||||
|
||||
/// The question identifier associated with this result.
|
||||
public let identifier: String
|
||||
|
||||
/// The response to the question associated with the `identifier`.
|
||||
public let answer: AnswerFormat
|
||||
|
||||
/// Initializes an instance of ``Result`` with the provided configuration.
|
||||
/// - Parameters:
|
||||
/// - identifier: The question identifier associated with this response.
|
||||
/// - answer: The response to the question associated with the `identifier`.
|
||||
public init(identifier: String, answer: AnswerFormat) {
|
||||
self.identifier = identifier
|
||||
self.answer = answer
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension ResearchFormResult: Codable {
|
||||
|
||||
enum CodingKeys: CodingKey {
|
||||
|
||||
case stepResults
|
||||
|
||||
}
|
||||
|
||||
public func encode(to encoder: any Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(stepResults, forKey: .stepResults)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension ResearchFormResult: Equatable {
|
||||
|
||||
public static func == (lhs: ResearchFormResult, rhs: ResearchFormResult)
|
||||
-> Bool
|
||||
{
|
||||
lhs.stepResults == rhs.stepResults
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension EnvironmentValues {
|
||||
|
||||
var questionContext: QuestionContext {
|
||||
get {
|
||||
self[QuestionContextKey.self]
|
||||
}
|
||||
set {
|
||||
self[QuestionContextKey.self] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
enum QuestionContext {
|
||||
|
||||
case standalone, formEmbedded
|
||||
|
||||
}
|
||||
|
||||
private struct QuestionContextKey: EnvironmentKey {
|
||||
|
||||
static var defaultValue: QuestionContext = .standalone
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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 Foundation
|
||||
|
||||
/// `ResultValue` is limited to a few question types that have different representations for their selected values.
|
||||
public enum ResultValue: Codable {
|
||||
case int(Int)
|
||||
case string(String)
|
||||
case date(Date)
|
||||
}
|
||||
|
||||
extension ResultValue: Equatable {
|
||||
|
||||
public static func == (lhs: ResultValue, rhs: ResultValue) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
case (.int(let a), .int(let b)):
|
||||
return a == b
|
||||
case (.string(let a), .string(let b)):
|
||||
return a == b
|
||||
case (.date(let a), .date(let b)):
|
||||
return a == b
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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 Foundation
|
||||
|
||||
struct StepResultKey<Result> {
|
||||
|
||||
let id: String
|
||||
|
||||
static func text(id: String) -> StepResultKey<String?> {
|
||||
return StepResultKey<String?>(id: id)
|
||||
}
|
||||
|
||||
static func imageChoice(id: String) -> StepResultKey<[ResultValue]?> {
|
||||
return StepResultKey<[ResultValue]?>(id: id)
|
||||
}
|
||||
|
||||
static func multipleChoice(id: String) -> StepResultKey<[ResultValue]?> {
|
||||
return StepResultKey<[ResultValue]?>(id: id)
|
||||
}
|
||||
|
||||
static func numeric(id: String) -> StepResultKey<Double?> {
|
||||
return StepResultKey<Double?>(id: id)
|
||||
}
|
||||
|
||||
static func height(id: String) -> StepResultKey<Double?> {
|
||||
return StepResultKey<Double?>(id: id)
|
||||
}
|
||||
|
||||
static func weight(id: String) -> StepResultKey<Double?> {
|
||||
return StepResultKey<Double?>(id: id)
|
||||
}
|
||||
|
||||
static func date(id: String) -> StepResultKey<Date?> {
|
||||
return StepResultKey<Date?>(id: id)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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 SwiftUI
|
||||
|
||||
extension StickyScrollView {
|
||||
|
||||
struct StickyFooterLayout: Layout {
|
||||
|
||||
let safeAreaInsets: EdgeInsets
|
||||
|
||||
func sizeThatFits(
|
||||
proposal: ProposedViewSize,
|
||||
subviews: Subviews,
|
||||
cache: inout Void
|
||||
) -> CGSize {
|
||||
|
||||
let contentHeight = subviews[0].dimensions(in: proposal).height
|
||||
|
||||
return CGSize(
|
||||
width: proposal.replacingUnspecifiedDimensions().width,
|
||||
height: contentHeight + safeAreaInsets.bottom
|
||||
)
|
||||
}
|
||||
|
||||
func placeSubviews(
|
||||
in bounds: CGRect,
|
||||
proposal: ProposedViewSize,
|
||||
subviews: Subviews,
|
||||
cache: inout Void
|
||||
) {
|
||||
let content = subviews[0]
|
||||
let contentPlacementProposal = ProposedViewSize(
|
||||
width: bounds.width,
|
||||
height: .infinity
|
||||
)
|
||||
content.place(
|
||||
at: CGPoint(
|
||||
x: bounds.origin.x,
|
||||
y: bounds.origin.y
|
||||
),
|
||||
anchor: .topLeading,
|
||||
proposal: contentPlacementProposal
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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 SwiftUI
|
||||
|
||||
extension StickyScrollView {
|
||||
|
||||
struct StickyLayout: Layout {
|
||||
|
||||
let allowsExtendedLayout: Bool
|
||||
|
||||
let size: CGSize
|
||||
|
||||
let bodySize: CGSize
|
||||
|
||||
var offset: CGFloat
|
||||
|
||||
let safeAreaInsets: EdgeInsets
|
||||
|
||||
let keyboardIgnoringSafeAreaInsets: EdgeInsets
|
||||
|
||||
let isContentCenteringEnabled: Bool
|
||||
|
||||
@Binding
|
||||
var totalLayoutHeight: CGFloat
|
||||
|
||||
@Binding
|
||||
var availableContentHeight: CGFloat
|
||||
|
||||
@Binding
|
||||
var isFooterBackgroundVisible: Bool
|
||||
|
||||
func sizeThatFits(
|
||||
proposal: ProposedViewSize,
|
||||
subviews: Subviews,
|
||||
cache: inout Void
|
||||
) -> CGSize {
|
||||
|
||||
let bottomInset = safeAreaInsets.bottom
|
||||
|
||||
let contentHeight = subviews[0].dimensions(in: proposal).height
|
||||
|
||||
let stickyHeight = subviews[1].dimensions(in: proposal).height
|
||||
|
||||
if contentHeight + stickyHeight > size.height {
|
||||
return CGSize(
|
||||
width: proposal.replacingUnspecifiedDimensions().width,
|
||||
height: contentHeight + stickyHeight - bottomInset
|
||||
)
|
||||
}
|
||||
|
||||
return CGSize(
|
||||
width: proposal.replacingUnspecifiedDimensions().width,
|
||||
height: contentHeight + stickyHeight + bottomInset
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
func placeSubviews(
|
||||
in bounds: CGRect,
|
||||
proposal: ProposedViewSize,
|
||||
subviews: Subviews,
|
||||
cache: inout Void
|
||||
) {
|
||||
|
||||
let stickyContent = subviews[1]
|
||||
let stickyContentHeight = stickyContent.dimensions(in: proposal)
|
||||
.height
|
||||
|
||||
let content = subviews[0]
|
||||
let contentHeight = content.dimensions(in: proposal).height
|
||||
|
||||
let totalHeight = contentHeight + stickyContentHeight
|
||||
let bodyHeight = bodySize.height
|
||||
let topWhiteSpace =
|
||||
isContentCenteringEnabled == false
|
||||
? 0
|
||||
: (contentHeight - bodyHeight) / 2
|
||||
|
||||
// This is the bottom inset to the top of the keyboard (and attachments)
|
||||
let bottomInset = safeAreaInsets.bottom
|
||||
|
||||
// This is the bottom inset with no keyboard
|
||||
let keyboardIgnoringBottomInset = keyboardIgnoringSafeAreaInsets
|
||||
.bottom
|
||||
|
||||
let shouldFooterFixPosition =
|
||||
allowsExtendedLayout == false
|
||||
|| contentHeight + (stickyContentHeight - safeAreaInsets.bottom)
|
||||
< size.height
|
||||
|
||||
let shouldFooterBackgroundShow: Bool = {
|
||||
let bottomOfBodyFromTop =
|
||||
bodySize.height + topWhiteSpace + self.offset
|
||||
let topOfStickyFooter =
|
||||
size.height + bottomInset - stickyContentHeight
|
||||
let isHeightGreaterThanAvailableSpace =
|
||||
bottomOfBodyFromTop > topOfStickyFooter
|
||||
return isHeightGreaterThanAvailableSpace
|
||||
&& shouldFooterFixPosition
|
||||
}()
|
||||
|
||||
DispatchQueue.main.async {
|
||||
isFooterBackgroundVisible = shouldFooterBackgroundShow
|
||||
|
||||
// The following conditionals are a hack to deal with lack of keyboard management support in SwiftUI.
|
||||
// The point of these conditionals is to allow bottom spacing for the sticky section, but not
|
||||
// allow that space when the keyboard is visible.
|
||||
|
||||
// There are 3 cases for the layout:
|
||||
if totalHeight > size.height {
|
||||
if bottomInset > keyboardIgnoringBottomInset {
|
||||
// 1. The content is greater than the view size, and the keyboard is visible.
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
|
||||
self.totalLayoutHeight =
|
||||
totalHeight - bottomInset - stickyContentHeight
|
||||
+ bottomInset
|
||||
}
|
||||
// Need to cancel these (for quick dismisses)
|
||||
} else {
|
||||
// 2. The content is greater than the view size, and the keyboard is not visible
|
||||
self.totalLayoutHeight = totalHeight - bottomInset
|
||||
}
|
||||
} else {
|
||||
// 3. The content is not greater than the view size, doesn't matter if the keyboard is visible
|
||||
self.totalLayoutHeight = contentHeight
|
||||
}
|
||||
self.availableContentHeight =
|
||||
size.height - stickyContentHeight + bottomInset
|
||||
}
|
||||
|
||||
let contentPlacementProposal = ProposedViewSize(
|
||||
width: bounds.width,
|
||||
height: .infinity
|
||||
)
|
||||
|
||||
let stickyPlacementProposal = ProposedViewSize(
|
||||
width: bounds.width,
|
||||
height: .infinity
|
||||
)
|
||||
|
||||
content.place(
|
||||
at: CGPoint(
|
||||
x: bounds.origin.x,
|
||||
y: bounds.origin.y
|
||||
),
|
||||
anchor: .topLeading,
|
||||
proposal: contentPlacementProposal
|
||||
)
|
||||
|
||||
let offset =
|
||||
shouldFooterFixPosition
|
||||
? size.height - stickyContentHeight - self.offset + bottomInset
|
||||
: max(contentHeight, size.height - stickyContentHeight)
|
||||
|
||||
stickyContent.place(
|
||||
at: CGPoint(
|
||||
x: bounds.origin.x,
|
||||
y: bounds.origin.y + offset + safeAreaInsets.bottom
|
||||
- keyboardIgnoringSafeAreaInsets.bottom
|
||||
),
|
||||
anchor: .topLeading,
|
||||
proposal: stickyPlacementProposal
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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 SwiftUI
|
||||
|
||||
/// A vertical ScrollView with a sticky footer, that returns to flow inline if
|
||||
/// the content is longer than the available height.
|
||||
/// Usage:
|
||||
/// ```
|
||||
/// StickyScrollView {
|
||||
/// allowsExtendedLayout: true,
|
||||
/// bodyContent: { /* Your Content */ },
|
||||
/// footerContent: { /* Will stick to bottom if bodyContent is short */ }
|
||||
/// }
|
||||
/// ```
|
||||
struct StickyScrollView<BodyContent: View, FooterContent: View>: View {
|
||||
|
||||
/// Create a StickyScrollView with footer content that can stick to the content bounds if the content is longer
|
||||
/// than the scrollView frame.
|
||||
/// - Parameters:
|
||||
/// - allowsExtendedLayout: Allow the footer to stick to bottom of content if the content is longer
|
||||
/// than the container height. On watchOS, the footer will never stick to the bottom.
|
||||
/// - bodyContent: The body content for the ScrollView
|
||||
/// - footerContent: The footer content.
|
||||
init(
|
||||
allowsExtendedLayout: Bool = false,
|
||||
paddingAboveKeyboard: CGFloat = 0.0,
|
||||
centerContentIfFits: Bool = false,
|
||||
bodyContent: @escaping (CGSize) -> BodyContent,
|
||||
footerContent: @escaping () -> FooterContent
|
||||
) {
|
||||
#if os(watchOS)
|
||||
self.allowsExtendedLayout = true
|
||||
#else
|
||||
self.allowsExtendedLayout = allowsExtendedLayout
|
||||
#endif
|
||||
self.bodyContent = bodyContent
|
||||
self.footerContent = footerContent
|
||||
self.paddingAboveKeyboard = paddingAboveKeyboard
|
||||
self.centerContentIfFits = centerContentIfFits
|
||||
}
|
||||
|
||||
init(
|
||||
allowsExtendedLayout: Bool = false,
|
||||
paddingAboveKeyboard: CGFloat = 0.0,
|
||||
centerContentIfFits: Bool = false,
|
||||
bodyContent: @escaping () -> BodyContent,
|
||||
footerContent: @escaping () -> FooterContent
|
||||
) {
|
||||
self.init(
|
||||
allowsExtendedLayout: allowsExtendedLayout,
|
||||
paddingAboveKeyboard: paddingAboveKeyboard,
|
||||
centerContentIfFits: centerContentIfFits,
|
||||
bodyContent: { _ in bodyContent() },
|
||||
footerContent: footerContent
|
||||
)
|
||||
}
|
||||
|
||||
@ViewBuilder let bodyContent: (CGSize) -> BodyContent
|
||||
|
||||
@ViewBuilder let footerContent: () -> FooterContent
|
||||
|
||||
let paddingAboveKeyboard: CGFloat
|
||||
|
||||
let centerContentIfFits: Bool
|
||||
|
||||
private let allowsExtendedLayout: Bool
|
||||
|
||||
@Namespace
|
||||
var scrollCoordinateSpace
|
||||
|
||||
@State
|
||||
private var offset = CGFloat.zero
|
||||
|
||||
@State
|
||||
private var frameSize = CGSize.zero
|
||||
|
||||
@State
|
||||
private var totalLayoutHeight = CGFloat.zero
|
||||
|
||||
@State
|
||||
private var availableContentHeight = CGFloat.zero
|
||||
|
||||
@State
|
||||
private var bodySize = CGSize.zero
|
||||
|
||||
@State
|
||||
private var safeAreaInsets = EdgeInsets(
|
||||
top: 0, leading: 0, bottom: 0, trailing: 0)
|
||||
|
||||
@State
|
||||
private var keyboardIgnoringSafeAreaInsets = EdgeInsets(
|
||||
top: 0, leading: 0, bottom: 0, trailing: 0)
|
||||
|
||||
@State
|
||||
private var isFooterBackgroundVisible = false
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { outerGeo in
|
||||
ScrollView {
|
||||
GeometryReader { geo in
|
||||
let offset = geo.frame(in: .named(scrollCoordinateSpace))
|
||||
.origin.y
|
||||
StickyLayout(
|
||||
allowsExtendedLayout: allowsExtendedLayout,
|
||||
size: frameSize,
|
||||
bodySize: bodySize,
|
||||
offset: offset,
|
||||
safeAreaInsets: safeAreaInsets,
|
||||
keyboardIgnoringSafeAreaInsets:
|
||||
keyboardIgnoringSafeAreaInsets,
|
||||
isContentCenteringEnabled: centerContentIfFits,
|
||||
totalLayoutHeight: $totalLayoutHeight,
|
||||
availableContentHeight: $availableContentHeight,
|
||||
isFooterBackgroundVisible: $isFooterBackgroundVisible
|
||||
) {
|
||||
|
||||
let bodySize = CGSize(
|
||||
width: geo.size.width - geo.safeAreaInsets.leading
|
||||
- geo.safeAreaInsets.trailing,
|
||||
height: availableContentHeight
|
||||
)
|
||||
VStack(spacing: 0) {
|
||||
bodyContent(bodySize)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.background(
|
||||
GeometryReader {
|
||||
Color.clear.preference(
|
||||
key: BodySizeKey.self,
|
||||
value: $0.size
|
||||
)
|
||||
})
|
||||
}
|
||||
.frame(
|
||||
maxWidth: bodySize.width,
|
||||
minHeight: centerContentIfFits
|
||||
? outerGeo.size.height
|
||||
- outerGeo.safeAreaInsets.top
|
||||
- outerGeo.safeAreaInsets.bottom : nil
|
||||
)
|
||||
StickyFooterLayout(
|
||||
safeAreaInsets: safeAreaInsets
|
||||
) {
|
||||
VStack(spacing: 0) {
|
||||
Rectangle()
|
||||
.frame(
|
||||
maxWidth: .infinity,
|
||||
minHeight: 1,
|
||||
maxHeight: 1,
|
||||
alignment: .topLeading
|
||||
)
|
||||
.background(Color.gray)
|
||||
.opacity(
|
||||
isFooterBackgroundVisible ? 0.1 : 0)
|
||||
footerContent()
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
.edgesIgnoringSafeArea(.bottom)
|
||||
#if !os(watchOS)
|
||||
.background(
|
||||
Material.bar.opacity(
|
||||
isFooterBackgroundVisible ? 100 : 0))
|
||||
#endif
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
}
|
||||
.frame(height: totalLayoutHeight, alignment: .topLeading)
|
||||
}
|
||||
.safeAreaInset(edge: .bottom) {
|
||||
Color.clear.frame(
|
||||
height: safeAreaInsets != keyboardIgnoringSafeAreaInsets
|
||||
? paddingAboveKeyboard
|
||||
: 0.0
|
||||
)
|
||||
}
|
||||
.background(
|
||||
GeometryReader {
|
||||
Color.clear.preference(
|
||||
key: FrameSizeKey.self,
|
||||
value: $0.size
|
||||
)
|
||||
Color.clear.preference(
|
||||
key: SafeAreaInsetsKey.self,
|
||||
value: $0.safeAreaInsets
|
||||
)
|
||||
}
|
||||
)
|
||||
.overlay {
|
||||
GeometryReader {
|
||||
Color.clear.preference(
|
||||
key: KeyboardIgnoringSafeAreaInsets.self,
|
||||
value: $0.safeAreaInsets
|
||||
)
|
||||
}
|
||||
.ignoresSafeArea(.keyboard, edges: .all)
|
||||
}
|
||||
.coordinateSpace(name: scrollCoordinateSpace)
|
||||
.onPreferenceChange(FrameSizeKey.self) { value in
|
||||
self.frameSize = value
|
||||
}
|
||||
.onPreferenceChange(SafeAreaInsetsKey.self) { value in
|
||||
self.safeAreaInsets = value
|
||||
}
|
||||
.onPreferenceChange(KeyboardIgnoringSafeAreaInsets.self) { value in
|
||||
self.keyboardIgnoringSafeAreaInsets = value
|
||||
}
|
||||
.onPreferenceChange(
|
||||
BodySizeKey.self,
|
||||
perform: { value in
|
||||
self.bodySize = value
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct FrameSizeKey: PreferenceKey {
|
||||
typealias Value = CGSize
|
||||
static var defaultValue = CGSize.zero
|
||||
static func reduce(value: inout Value, nextValue: () -> Value) {
|
||||
value = CGSize(
|
||||
width: value.width + nextValue().width,
|
||||
height: value.height + nextValue().height
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private struct BodySizeKey: PreferenceKey {
|
||||
typealias Value = CGSize
|
||||
static var defaultValue = CGSize.zero
|
||||
static func reduce(value: inout Value, nextValue: () -> Value) {
|
||||
value = CGSize(
|
||||
width: value.width + nextValue().width,
|
||||
height: value.height + nextValue().height
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private struct KeyboardIgnoringSafeAreaInsets: PreferenceKey {
|
||||
typealias Value = EdgeInsets
|
||||
static var defaultValue = EdgeInsets(
|
||||
top: 0, leading: 0, bottom: 0, trailing: 0)
|
||||
static func reduce(value: inout Value, nextValue: () -> Value) {
|
||||
let nextValue = nextValue()
|
||||
value = EdgeInsets(
|
||||
top: value.top + nextValue.top,
|
||||
leading: value.leading + nextValue.leading,
|
||||
bottom: value.bottom + nextValue.bottom,
|
||||
trailing: value.trailing + nextValue.trailing
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private struct SafeAreaInsetsKey: PreferenceKey {
|
||||
typealias Value = EdgeInsets
|
||||
static var defaultValue = EdgeInsets(
|
||||
top: 0, leading: 0, bottom: 0, trailing: 0)
|
||||
static func reduce(value: inout Value, nextValue: () -> Value) {
|
||||
let nextValue = nextValue()
|
||||
value = EdgeInsets(
|
||||
top: value.top + nextValue.top,
|
||||
leading: value.leading + nextValue.leading,
|
||||
bottom: value.bottom + nextValue.bottom,
|
||||
trailing: value.trailing + nextValue.trailing
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct ToolbarButton: ButtonStyle {
|
||||
let isDisabled: Bool
|
||||
|
||||
public init(isDisabled: Bool = false) {
|
||||
self.isDisabled = isDisabled
|
||||
}
|
||||
|
||||
var buttonColor: Color {
|
||||
return isDisabled ? .gray : .accentColor
|
||||
}
|
||||
|
||||
public func makeBody(configuration: Configuration) -> some View {
|
||||
configuration.label
|
||||
.fontWeight(.semibold)
|
||||
.foregroundColor(.white)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(12)
|
||||
.background(
|
||||
configuration.isPressed
|
||||
? buttonColor.opacity(0.8).cornerRadius(12)
|
||||
: buttonColor.cornerRadius(12)
|
||||
)
|
||||
.disabled(isDisabled)
|
||||
.scaleEffect(configuration.isPressed ? 1.05 : 1)
|
||||
.animation(.easeOut(duration: 0.2), value: configuration.isPressed)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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 SwiftUI
|
||||
|
||||
/// Represents a step in a survey and lays out the header and questions on one page. Question numbers (e.g. 1 of 3) are automatically added at the top of each question to denote progress in a step.
|
||||
public struct ResearchFormStep<Header: View, Content: View>: View {
|
||||
|
||||
@State
|
||||
private var shouldWrapInQuestionCard = true
|
||||
|
||||
private let header: Header
|
||||
private let content: Content
|
||||
|
||||
/// Initializes an instance of ``ResearchFormStep`` with the provided configuration.
|
||||
/// - Parameters:
|
||||
/// - header: The header that is displayed at the top of the step.
|
||||
/// - content: The questions that are displayed below the header.
|
||||
public init(
|
||||
@ViewBuilder header: () -> Header,
|
||||
@ViewBuilder content: () -> Content
|
||||
) {
|
||||
self.header = header()
|
||||
self.content = content()
|
||||
}
|
||||
|
||||
@State
|
||||
private var visibleQuestions = Set<Subview.ID>()
|
||||
|
||||
@State
|
||||
private var requiredQuestions = Set<Subview.ID>()
|
||||
|
||||
@State
|
||||
private var answeredQuestions = Set<Subview.ID>()
|
||||
|
||||
private var canMoveToNextStep: Bool {
|
||||
requiredQuestions
|
||||
.filter { visibleQuestions.contains($0) }
|
||||
.subtracting(answeredQuestions).isEmpty
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
VStack(alignment: .leading, spacing: 16) {
|
||||
header
|
||||
|
||||
Group(
|
||||
subviews: cardConsideredContent()
|
||||
) { questions in
|
||||
ForEach(subviews: questions) { question in
|
||||
Group {
|
||||
if shouldWrapInQuestionCard {
|
||||
QuestionCard {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
if let questionIndex = questions.firstIndex(
|
||||
where: { $0.id == question.id })
|
||||
{
|
||||
let questionNumber = questionIndex + 1
|
||||
Text(
|
||||
"Question \(questionNumber) of \(questions.count)"
|
||||
)
|
||||
.foregroundColor(.secondary)
|
||||
.font(.footnote)
|
||||
#if os(watchOS)
|
||||
.padding([.horizontal])
|
||||
.padding(.top, 4)
|
||||
#else
|
||||
.fontWeight(.bold)
|
||||
.padding([.horizontal, .top])
|
||||
#endif
|
||||
}
|
||||
|
||||
question
|
||||
}
|
||||
}
|
||||
} else {
|
||||
question
|
||||
}
|
||||
}
|
||||
.onPreferenceChange(QuestionRequiredPreferenceKey.self) {
|
||||
if $0 == true {
|
||||
requiredQuestions.insert(question.id)
|
||||
}
|
||||
}
|
||||
.onPreferenceChange(QuestionAnsweredPreferenceKey.self) {
|
||||
if $0 == true {
|
||||
answeredQuestions.insert(question.id)
|
||||
} else {
|
||||
answeredQuestions.remove(question.id)
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
visibleQuestions.insert(question.id)
|
||||
}
|
||||
.onDisappear {
|
||||
visibleQuestions.remove(question.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.preference(
|
||||
key: StepCompletedPreferenceKey.self, value: canMoveToNextStep
|
||||
)
|
||||
|
||||
#if os(iOS)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
#endif
|
||||
.onPreferenceChange(QuestionCardPreferenceKey.self) {
|
||||
shouldWrapInQuestionCard in
|
||||
self.shouldWrapInQuestionCard = shouldWrapInQuestionCard
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func cardConsideredContent() -> some View {
|
||||
if shouldWrapInQuestionCard {
|
||||
content.environment(\.isQuestionCardEnabled, false)
|
||||
} else {
|
||||
content
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension ResearchFormStep where Header == EmptyView {
|
||||
|
||||
/// Initializes an instance of ``ResearchFormStep`` with just the questions and no header.
|
||||
/// - Parameter content: The questions for the step.
|
||||
public init(@ViewBuilder content: () -> Content) {
|
||||
self.init(
|
||||
header: {
|
||||
EmptyView()
|
||||
},
|
||||
content: content
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension ResearchFormStep where Header == StepHeader, Content == EmptyView {
|
||||
|
||||
/// Initializes an instance of ``ResearchFormStep`` with the provided configuration. There are no questions displayed beneath the header.
|
||||
/// - Parameters:
|
||||
/// - image: The image displayed in the header.
|
||||
/// - title: The title displayed in the header.
|
||||
/// - subtitle: The subtitle displayed in the header.
|
||||
public init(
|
||||
image: Image? = nil,
|
||||
title: String? = nil,
|
||||
subtitle: String? = nil
|
||||
) {
|
||||
self.init(
|
||||
image: image,
|
||||
title: title,
|
||||
subtitle: subtitle,
|
||||
content: {
|
||||
EmptyView()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension ResearchFormStep where Header == StepHeader {
|
||||
|
||||
/// Initializes an instance of ``ResearchFormStep`` with the provided configuration. The questions are displayed beneath the header.
|
||||
/// - Parameters:
|
||||
/// - image: The image displayed in the header.
|
||||
/// - title: The title displayed in the header.
|
||||
/// - subtitle: The subtitle displayed in the header.
|
||||
/// - content: The questions for the step that are displayed beneath the header.
|
||||
public init(
|
||||
image: Image? = nil,
|
||||
title: String? = nil,
|
||||
subtitle: String? = nil,
|
||||
@ViewBuilder content: () -> Content
|
||||
) {
|
||||
let titleText: Text?
|
||||
if let title, !title.isEmpty {
|
||||
titleText = Text(title)
|
||||
} else {
|
||||
titleText = nil
|
||||
}
|
||||
|
||||
let subtitleText: Text?
|
||||
if let subtitle, !subtitle.isEmpty {
|
||||
subtitleText = Text(subtitle)
|
||||
} else {
|
||||
subtitleText = nil
|
||||
}
|
||||
|
||||
self.init(
|
||||
header: {
|
||||
StepHeader(
|
||||
image: image,
|
||||
title: titleText,
|
||||
subtitle: subtitleText
|
||||
)
|
||||
},
|
||||
content: content
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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 SwiftUI
|
||||
|
||||
struct ResearchFormStepContentView<Content: View>: View {
|
||||
@EnvironmentObject
|
||||
private var managedFormResult: ResearchFormResult
|
||||
|
||||
private let content: Content
|
||||
|
||||
private let isLastStep: Bool
|
||||
private var onStepCompletion: ((ResearchFormCompletion) -> Void)?
|
||||
|
||||
@State
|
||||
private var doneButtonEnabled: Bool = true
|
||||
|
||||
init(
|
||||
isLastStep: Bool,
|
||||
onStepCompletion: ((ResearchFormCompletion) -> Void)? = nil,
|
||||
@ViewBuilder content: () -> Content
|
||||
) {
|
||||
self.isLastStep = isLastStep
|
||||
self.onStepCompletion = onStepCompletion
|
||||
self.content = content()
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
StickyScrollView {
|
||||
content
|
||||
.onPreferenceChange(StepCompletedPreferenceKey.self) {
|
||||
doneButtonEnabled = $0
|
||||
}
|
||||
.padding()
|
||||
#if !os(watchOS)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
Button {
|
||||
onStepCompletion?(.discarded)
|
||||
} label: {
|
||||
Text("Cancel")
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} footerContent: {
|
||||
Button {
|
||||
if isLastStep {
|
||||
onStepCompletion?(.completed(managedFormResult))
|
||||
} else {
|
||||
onStepCompletion?(.saved(managedFormResult))
|
||||
}
|
||||
} label: {
|
||||
Text(isLastStep ? "Done" : "Next")
|
||||
.fontWeight(.bold)
|
||||
.frame(maxWidth: maxWidthForDoneButton)
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
.buttonStyle(.researchFormStep)
|
||||
.disabled(!doneButtonEnabled)
|
||||
}
|
||||
.background(Color.choice(for: .secondaryBackground))
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
|
||||
private var maxWidthForDoneButton: CGFloat {
|
||||
#if os(visionOS)
|
||||
300
|
||||
#else
|
||||
.infinity
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private struct ResearchFormStepButtonStyle: ButtonStyle {
|
||||
|
||||
@Environment(\.isEnabled)
|
||||
private var isEnabled
|
||||
|
||||
func makeBody(configuration: Configuration) -> some View {
|
||||
#if os(iOS)
|
||||
configuration.label
|
||||
.foregroundStyle(.white)
|
||||
.frame(height: 50)
|
||||
.background(
|
||||
isEnabled ? Color.accentColor : Color.accentColor.opacity(0.5),
|
||||
in: RoundedRectangle(cornerRadius: 12)
|
||||
)
|
||||
#elseif os(visionOS)
|
||||
configuration.label
|
||||
.foregroundStyle(isEnabled ? .white : .gray)
|
||||
.frame(height: 50)
|
||||
.background(
|
||||
.ultraThinMaterial,
|
||||
in: RoundedRectangle(cornerRadius: 25)
|
||||
)
|
||||
.hoverEffect(.highlight)
|
||||
#elseif os(watchOS)
|
||||
configuration.label
|
||||
.foregroundStyle(isEnabled ? .white : .gray)
|
||||
.frame(height: 50)
|
||||
.background(
|
||||
Color.choice(for: .systemGray4),
|
||||
in: RoundedRectangle(cornerRadius: 25)
|
||||
)
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension ButtonStyle where Self == ResearchFormStepButtonStyle {
|
||||
|
||||
fileprivate static var researchFormStep: ResearchFormStepButtonStyle {
|
||||
.init()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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 SwiftUI
|
||||
|
||||
/// A step header containing an image, title, and subtitle.
|
||||
public struct StepHeader: View {
|
||||
|
||||
private let image: Image?
|
||||
private let title: Text?
|
||||
private let subtitle: Text?
|
||||
|
||||
/// Initializes an instance of ``StepHeader`` with the provided configuration.
|
||||
/// - Parameters:
|
||||
/// - image: The image for this step header.
|
||||
/// - title: The title for this step header.
|
||||
/// - subtitle: The subtitle for this header.
|
||||
init(image: Image? = nil, title: Text? = nil, subtitle: Text? = nil) {
|
||||
self.image = image
|
||||
self.title = title
|
||||
self.subtitle = subtitle
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
#if os(watchOS)
|
||||
compactBody()
|
||||
#else
|
||||
defaultBody()
|
||||
#endif
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func compactBody() -> some View {
|
||||
VStack(alignment: .leading) {
|
||||
HStack(alignment: .center) {
|
||||
icon(size: 20)
|
||||
|
||||
title?
|
||||
.font(.title3)
|
||||
.fontWeight(.bold)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
|
||||
subtitle
|
||||
.font(.body)
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func defaultBody() -> some View {
|
||||
VStack(alignment: .leading, spacing: 16) {
|
||||
icon(size: 80)
|
||||
|
||||
title?
|
||||
.font(.title)
|
||||
.fontWeight(.bold)
|
||||
|
||||
subtitle
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func icon(size: CGFloat) -> some View {
|
||||
image?
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: size, height: size)
|
||||
.foregroundStyle(.stepIconForegroundStyle)
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
StepHeader(
|
||||
image: Image(systemName: "hand.wave"),
|
||||
title: Text("Welcome"),
|
||||
subtitle: Text("Hello")
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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 SwiftUI
|
||||
|
||||
extension ShapeStyle where Self == StepIconForegroundStyle {
|
||||
|
||||
/// This foreground style is used for labels that display values associated with sliders.
|
||||
static var stepIconForegroundStyle: StepIconForegroundStyle {
|
||||
StepIconForegroundStyle()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct StepIconForegroundStyle: ShapeStyle {
|
||||
|
||||
func resolve(in environment: EnvironmentValues) -> some ShapeStyle {
|
||||
#if os(visionOS)
|
||||
.white
|
||||
#else
|
||||
Color.accentColor
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
Copyright (c) 2024, 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 Testing
|
||||
|
||||
struct ResearchKitSwiftUITests {
|
||||
|
||||
@Test func example() async throws {
|
||||
// Write your test here and use APIs like `#expect(...)` to check expected conditions.
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1448,11 +1448,13 @@
|
||||
XCTAssertEqual([regexAnswerFormat isAnswerValidWithString:incorrectPhoneNumber], NO, @"Should return NO since it is not in the correct format");
|
||||
}
|
||||
|
||||
#if ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION
|
||||
- (void)testLocationAnswerFormat {
|
||||
ORKLocationAnswerFormat *answerFormat = [ORKAnswerFormat locationAnswerFormat];
|
||||
[answerFormat setUseCurrentLocation:YES];
|
||||
XCTAssertEqual(answerFormat.useCurrentLocation, YES);
|
||||
}
|
||||
#endif
|
||||
|
||||
- (void)testWeightAnswerFormat {
|
||||
ORKWeightAnswerFormat *answerFormat = [ORKAnswerFormat weightAnswerFormatWithMeasurementSystem:ORKMeasurementSystemMetric
|
||||
|
||||
@@ -55,7 +55,7 @@ static BOOL ORKIsResearchKitClass(Class class) {
|
||||
|
||||
+ (NSArray<NSString *> *)_fetchExclusionList {
|
||||
NSArray<NSString *> *classesToExclude = @[];
|
||||
|
||||
|
||||
#if !ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION
|
||||
NSArray<NSString *> *locationClasses = @[
|
||||
@"ORKLocation",
|
||||
|
||||
@@ -40,6 +40,9 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@class ORKColorChoiceAnswerFormat;
|
||||
@class ORKChoiceViewCell;
|
||||
@class ORKTextChoiceAnswerFormat;
|
||||
@class ORKScaleAnswerFormat;
|
||||
@class ORKContinuousScaleAnswerFormat;
|
||||
@class ORKTextScaleAnswerFormat;
|
||||
|
||||
|
||||
@interface ORKColorChoiceCellGroup : NSObject
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION
|
||||
#if ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION && TARGET_OS_IOS
|
||||
|
||||
#import "ORKLocationSelectionView.h"
|
||||
|
||||
@@ -356,7 +356,7 @@ static const NSString *FormattedAddressLines = @"FormattedAddressLines";
|
||||
ORKLocation *location = isAnswerClassORKLocation ? (ORKLocation *)_answer : nil;
|
||||
|
||||
if (location) {
|
||||
|
||||
|
||||
if (!location.userInput || !location.region |!location.postalAddress) {
|
||||
// redo geo decoding if any of them is missing
|
||||
[self reverseGeocodeAndDisplay:location];
|
||||
|
||||
@@ -29,8 +29,8 @@
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
import ResearchKit
|
||||
import SwiftUI
|
||||
|
||||
@objc
|
||||
public class SwiftUIViewFactory: NSObject {
|
||||
|
||||
@@ -28,8 +28,8 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
import SwiftUI
|
||||
import ResearchKit
|
||||
import SwiftUI
|
||||
|
||||
struct TextChoiceView: View {
|
||||
@ObservedObject var textChoiceHelper: SwiftUITextChoiceHelper
|
||||
|
||||
@@ -19,12 +19,6 @@
|
||||
51F210FE2B49D04F0072B536 /* TaskListRowConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F210FD2B49D04F0072B536 /* TaskListRowConstants.swift */; };
|
||||
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 */; };
|
||||
8C67958C2BC3B09B00DE7286 /* TaskListRowConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F210FD2B49D04F0072B536 /* TaskListRowConstants.swift */; };
|
||||
9622283924F04DBF0056E74D /* TaskScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9622283824F04DBF0056E74D /* TaskScreen.swift */; };
|
||||
9622283B24F04DFE0056E74D /* CommonElements.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9622283A24F04DFE0056E74D /* CommonElements.swift */; };
|
||||
9622283D24F04E6F0056E74D /* AllowScreens.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9622283C24F04E6F0056E74D /* AllowScreens.swift */; };
|
||||
9622283F24F04EBA0056E74D /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9622283E24F04EBA0056E74D /* Helpers.swift */; };
|
||||
9622284124F04EEF0056E74D /* PreSubmissionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9622284024F04EEF0056E74D /* PreSubmissionTests.swift */; };
|
||||
B125E3D41B1BA47600FF8759 /* tap.aif in Resources */ = {isa = PBXBuildFile; fileRef = B125E3D31B1BA47600FF8759 /* tap.aif */; };
|
||||
BAAC3FD620C4EF1C0065B9E1 /* ResearchKit.pdf in Resources */ = {isa = PBXBuildFile; fileRef = BAAC3FD520C4EF1C0065B9E1 /* ResearchKit.pdf */; };
|
||||
BC2A3CD91C58F1CA00DA64B7 /* ResearchKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BC2A3CD81C58F1CA00DA64B7 /* ResearchKit.framework */; };
|
||||
@@ -49,13 +43,6 @@
|
||||
remoteGlobalIDString = 869230BD1AAA890A00BFE11B;
|
||||
remoteInfo = ORKCatalog;
|
||||
};
|
||||
9622282F24F04D780056E74D /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 869230B61AAA890A00BFE11B /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 869230BD1AAA890A00BFE11B;
|
||||
remoteInfo = ORKCatalog;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
@@ -131,14 +118,6 @@
|
||||
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 = "<group>"; };
|
||||
86B89A9C1AB2C0A5001626A4 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = ORKCatalog/AppDelegate.swift; sourceTree = SOURCE_ROOT; };
|
||||
9622282A24F04D780056E74D /* ORKCatalogUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ORKCatalogUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
9622282E24F04D780056E74D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
9622283824F04DBF0056E74D /* TaskScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskScreen.swift; sourceTree = "<group>"; };
|
||||
9622283A24F04DFE0056E74D /* CommonElements.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommonElements.swift; sourceTree = "<group>"; };
|
||||
9622283C24F04E6F0056E74D /* AllowScreens.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllowScreens.swift; sourceTree = "<group>"; };
|
||||
9622283E24F04EBA0056E74D /* Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Helpers.swift; sourceTree = "<group>"; };
|
||||
9622284024F04EEF0056E74D /* PreSubmissionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreSubmissionTests.swift; sourceTree = "<group>"; };
|
||||
9632B5D524F093070010818C /* ORKCatalogUI.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = ORKCatalogUI.xctestplan; sourceTree = "<group>"; };
|
||||
B125E3D31B1BA47600FF8759 /* tap.aif */ = {isa = PBXFileReference; lastKnownFileType = file; path = tap.aif; sourceTree = "<group>"; };
|
||||
BAAC3FD520C4EF1C0065B9E1 /* ResearchKit.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = ResearchKit.pdf; sourceTree = "<group>"; };
|
||||
BAFAD7AA1EFB2907004DEC45 /* fr-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "fr-CA"; path = fr_CA.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
@@ -172,13 +151,6 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
9622282724F04D780056E74D /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
@@ -233,7 +205,6 @@
|
||||
children = (
|
||||
5116D0BF2677FB780010FAB1 /* ResearchKit-Shared.xcconfig */,
|
||||
3E39B9E81ABF675000C2ABE5 /* README.md */,
|
||||
9622282B24F04D780056E74D /* ORKCatalogUITests */,
|
||||
51AF19782B59F1E500D3B399 /* ORKCatalogTests */,
|
||||
BC2A3C9E1C58E81500DA64B7 /* Frameworks */,
|
||||
869230C01AAA890A00BFE11B /* ORKCatalog */,
|
||||
@@ -245,7 +216,6 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
869230BE1AAA890A00BFE11B /* ORKCatalog.app */,
|
||||
9622282A24F04D780056E74D /* ORKCatalogUITests.xctest */,
|
||||
51AF19772B59F1E400D3B399 /* ORKCatalogTests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
@@ -274,28 +244,6 @@
|
||||
path = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8CDBF98A2AFD9FC800AF767B /* Screens */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9622283824F04DBF0056E74D /* TaskScreen.swift */,
|
||||
9622283A24F04DFE0056E74D /* CommonElements.swift */,
|
||||
9622283C24F04E6F0056E74D /* AllowScreens.swift */,
|
||||
);
|
||||
path = Screens;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9622282B24F04D780056E74D /* ORKCatalogUITests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8CDBF98A2AFD9FC800AF767B /* Screens */,
|
||||
9622282E24F04D780056E74D /* Info.plist */,
|
||||
9622283E24F04EBA0056E74D /* Helpers.swift */,
|
||||
9622284024F04EEF0056E74D /* PreSubmissionTests.swift */,
|
||||
9632B5D524F093070010818C /* ORKCatalogUI.xctestplan */,
|
||||
);
|
||||
path = ORKCatalogUITests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BC2A3C9E1C58E81500DA64B7 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -360,24 +308,6 @@
|
||||
productReference = 869230BE1AAA890A00BFE11B /* ORKCatalog.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
9622282924F04D780056E74D /* ORKCatalogUITests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 9622283624F04D780056E74D /* Build configuration list for PBXNativeTarget "ORKCatalogUITests" */;
|
||||
buildPhases = (
|
||||
9622282624F04D780056E74D /* Sources */,
|
||||
9622282724F04D780056E74D /* Frameworks */,
|
||||
9622282824F04D780056E74D /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
9622283024F04D780056E74D /* PBXTargetDependency */,
|
||||
);
|
||||
name = ORKCatalogUITests;
|
||||
productName = ORKCatalogUITests;
|
||||
productReference = 9622282A24F04D780056E74D /* ORKCatalogUITests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.ui-testing";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
@@ -398,12 +328,6 @@
|
||||
LastSwiftMigration = 1530;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
9622282924F04D780056E74D = {
|
||||
CreatedOnToolsVersion = 11.6;
|
||||
DevelopmentTeam = "";
|
||||
ProvisioningStyle = Automatic;
|
||||
TestTargetID = 869230BD1AAA890A00BFE11B;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 869230B91AAA890A00BFE11B /* Build configuration list for PBXProject "ORKCatalog" */;
|
||||
@@ -467,7 +391,6 @@
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
869230BD1AAA890A00BFE11B /* ORKCatalog */,
|
||||
9622282924F04D780056E74D /* ORKCatalogUITests */,
|
||||
51AF19762B59F1E400D3B399 /* ORKCatalogTests */,
|
||||
);
|
||||
};
|
||||
@@ -495,13 +418,6 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
9622282824F04D780056E74D /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
@@ -531,19 +447,6 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
9622282624F04D780056E74D /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
9622283924F04DBF0056E74D /* TaskScreen.swift in Sources */,
|
||||
9622283B24F04DFE0056E74D /* CommonElements.swift in Sources */,
|
||||
8C67958C2BC3B09B00DE7286 /* TaskListRowConstants.swift in Sources */,
|
||||
9622284124F04EEF0056E74D /* PreSubmissionTests.swift in Sources */,
|
||||
9622283F24F04EBA0056E74D /* Helpers.swift in Sources */,
|
||||
9622283D24F04E6F0056E74D /* AllowScreens.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
@@ -552,11 +455,6 @@
|
||||
target = 869230BD1AAA890A00BFE11B /* ORKCatalog */;
|
||||
targetProxy = 51AF197B2B59F1E500D3B399 /* PBXContainerItemProxy */;
|
||||
};
|
||||
9622283024F04D780056E74D /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 869230BD1AAA890A00BFE11B /* ORKCatalog */;
|
||||
targetProxy = 9622282F24F04D780056E74D /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
@@ -860,68 +758,6 @@
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
9622283124F04D780056E74D /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
INFOPLIST_FILE = ORKCatalogUITests/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.6;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.HRP.ORKCatalogUITests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TEST_TARGET_NAME = ORKCatalog;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
9622283424F04D780056E74D /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
INFOPLIST_FILE = ORKCatalogUITests/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.6;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.HRP.ORKCatalogUITests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TEST_TARGET_NAME = ORKCatalog;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
@@ -952,15 +788,6 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Debug;
|
||||
};
|
||||
9622283624F04D780056E74D /* Build configuration list for PBXNativeTarget "ORKCatalogUITests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
9622283124F04D780056E74D /* Debug */,
|
||||
9622283424F04D780056E74D /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Debug;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 869230B61AAA890A00BFE11B /* Project object */;
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1500"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "869230BD1AAA890A00BFE11B"
|
||||
BuildableName = "ORKCatalog.app"
|
||||
BlueprintName = "ORKCatalog"
|
||||
ReferencedContainer = "container:ORKCatalog.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<TestPlans>
|
||||
<TestPlanReference
|
||||
reference = "container:ORKCatalogUITests/ORKCatalogUI.xctestplan"
|
||||
default = "YES">
|
||||
</TestPlanReference>
|
||||
</TestPlans>
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "9622282924F04D780056E74D"
|
||||
BuildableName = "ORKCatalogUITests.xctest"
|
||||
BlueprintName = "ORKCatalogUITests"
|
||||
ReferencedContainer = "container:ORKCatalog.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
<TestableReference
|
||||
skipped = "NO"
|
||||
parallelizable = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "51AF19762B59F1E400D3B399"
|
||||
BuildableName = "ORKCatalogTests.xctest"
|
||||
BlueprintName = "ORKCatalogTests"
|
||||
ReferencedContainer = "container:ORKCatalog.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "869230BD1AAA890A00BFE11B"
|
||||
BuildableName = "ORKCatalog.app"
|
||||
BlueprintName = "ORKCatalog"
|
||||
ReferencedContainer = "container:ORKCatalog.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "869230BD1AAA890A00BFE11B"
|
||||
BuildableName = "ORKCatalog.app"
|
||||
BlueprintName = "ORKCatalog"
|
||||
ReferencedContainer = "container:ORKCatalog.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -126,3 +126,5 @@ extension ResultViewController: ResultProviderDelegate {
|
||||
present(shareSheet, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -286,3 +286,5 @@ class TaskListViewController: UITableViewController, ORKTaskViewControllerDelega
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,297 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2015, 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 Foundation
|
||||
import XCTest
|
||||
|
||||
class Helpers: XCTestCase {
|
||||
let app = XCUIApplication()
|
||||
let commonElements = CommonElements()
|
||||
let taskScreen = TaskScreen()
|
||||
|
||||
enum SliderTesting {
|
||||
case slider1
|
||||
case slider2
|
||||
case slider3
|
||||
case slider4
|
||||
case slider5
|
||||
case slider6
|
||||
}
|
||||
enum SwipeDirection {
|
||||
case up
|
||||
case down
|
||||
case left
|
||||
case right
|
||||
case skip
|
||||
}
|
||||
|
||||
//Verify existence of any XCUIElement with an option to tap that element
|
||||
func verifyElement(_ element: XCUIElement, _ tap: Bool = false) -> Bool {
|
||||
if element.exists {
|
||||
if tap && element.isEnabled {
|
||||
element.tap()
|
||||
}
|
||||
return true
|
||||
}
|
||||
XCTFail("Unable to confirm \(element) exists")
|
||||
return false
|
||||
}
|
||||
|
||||
//Verify an element exists based on static text label, option to tap the element after verification
|
||||
func verifyElementByText(identifier: String, _ tap: Bool = false) -> Bool {
|
||||
let item = app.staticTexts["\(identifier)"]
|
||||
if item.exists {
|
||||
if tap && item.isEnabled {
|
||||
item.tap()
|
||||
}
|
||||
return true
|
||||
}
|
||||
XCTFail("Unable to locate Element based on text \(identifier)")
|
||||
return false
|
||||
}
|
||||
|
||||
//Verify an element matching XCUIElement.ElementType exists based on an identifying string existing in the label or value of the element.
|
||||
func verifyElementByType(_ elementType: XCUIElement.ElementType, _ identifier: String, _ tap: Bool = false) -> Bool {
|
||||
let identifyingOptions = ["label", "value"]
|
||||
for option in identifyingOptions {
|
||||
let predicate = NSPredicate(format: "\(option) CONTAINS '\(identifier)'")
|
||||
let target = app.descendants(matching: elementType).element(matching: predicate)
|
||||
if target.exists {
|
||||
if tap && target.isEnabled {
|
||||
target.tap()
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
XCTFail("Unable to locate Element based on text \(identifier)")
|
||||
return false
|
||||
}
|
||||
|
||||
//Verify any XCUIElement exists and return that element for assignment, option to tap available
|
||||
func verifyAndAssign(_ element: XCUIElement, _ tap: Bool = false) -> XCUIElement? {
|
||||
if element.exists {
|
||||
if tap && element.isEnabled {
|
||||
element.tap()
|
||||
}
|
||||
return element
|
||||
}
|
||||
XCTFail("Unable to confirm \(element) exists")
|
||||
return nil
|
||||
}
|
||||
|
||||
//Verify an element exists based on static text label, option to tap the element after verification. Element returned for assignment.
|
||||
func verifyAndAssignByText(_ identifier: String, _ tap: Bool = false) -> XCUIElement? {
|
||||
let item = app.staticTexts["\(identifier)"]
|
||||
if item.exists {
|
||||
if tap && item.isEnabled {
|
||||
item.tap()
|
||||
}
|
||||
return item
|
||||
}
|
||||
XCTFail("Unable to locate Element based on text \(identifier)")
|
||||
return nil
|
||||
}
|
||||
|
||||
//Verify an element matching XCUIElement.ElementType exists based on an identifying string existing in the label or value of the element.
|
||||
//Element returned for assignment.
|
||||
func verifyAndAssignByType(_ elementType: XCUIElement.ElementType, _ identifier: String, _ tap: Bool = false) -> XCUIElement? {
|
||||
let identifyingOptions = ["label", "value"]
|
||||
for option in identifyingOptions {
|
||||
let predicate = NSPredicate(format: "\(option) CONTAINS '\(identifier)'")
|
||||
let target = app.descendants(matching: elementType).element(matching: predicate)
|
||||
if target.exists {
|
||||
if tap && target.isEnabled {
|
||||
target.tap()
|
||||
}
|
||||
return target
|
||||
}
|
||||
}
|
||||
XCTFail("Unable to locate Element based on text \(identifier)")
|
||||
return nil
|
||||
}
|
||||
|
||||
//Enter any ORKCatalog task based on the name of the task and leave without taking action
|
||||
func launchAndLeave(_ task: String) -> Bool {
|
||||
XCTAssert(verifyElement(taskScreen.mainTaskScreen))
|
||||
let currentTask = verifyAndAssignByText(task)!
|
||||
currentTask.tap()
|
||||
|
||||
sleep(1)
|
||||
guard let cancelButton = commonElements.cancelButton else {
|
||||
XCTFail("Unable to locate Cancel Button")
|
||||
return false
|
||||
}
|
||||
cancelButton.tap()
|
||||
|
||||
sleep(1)
|
||||
guard let exitButton = commonElements.getExitButton() else {
|
||||
XCTFail("Unable to locate End Task or Discard Results button")
|
||||
return false
|
||||
}
|
||||
exitButton.tap()
|
||||
|
||||
return verifyElement(taskScreen.mainTaskScreen)
|
||||
}
|
||||
|
||||
//Deletes the ORKCatalog app from the simulator instance.
|
||||
func deleteORKCatalog() {
|
||||
app.terminate()
|
||||
let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
|
||||
let icon = springboard.icons["ORKCatalog"]
|
||||
if icon.exists {
|
||||
icon.press(forDuration: 1.3)
|
||||
springboard.buttons["Delete App"].tap()
|
||||
sleep(2)
|
||||
springboard.buttons["Delete"].tap()
|
||||
}
|
||||
}
|
||||
|
||||
//Verify that the Navigation bar includes a page count in the form of "pageNumber of pageTotal"
|
||||
func verifyPageByCount(_ pageNumber: Int, _ pageTotal: Int) -> Bool {
|
||||
let pageCount = app.navigationBars["\(String(pageNumber)) of \(String(pageTotal))"]
|
||||
XCTAssert(pageCount.exists)
|
||||
return true
|
||||
}
|
||||
|
||||
//Return an XCUIElement based on page number of the Scale test
|
||||
func scaleTitle(_ pageNumber: Int) -> XCUIElement? {
|
||||
let titles: [Int: String] = [1: "Discrete Scale", 2: "Continuous Scale", 3: "Discrete Vertical Scale", 4: "Continuous Vertical Scale", 5: "Text Scale", 6: "Text Vertical Scale"]
|
||||
return app.scrollViews.otherElements.staticTexts[titles[pageNumber] ?? "Invalid Page Number"]
|
||||
}
|
||||
|
||||
//Select and interact with sliders in the Scale Question task. Function verifies expected outputs of each slider.
|
||||
func sliderValuesCheck(_ sliderScreen:SliderTesting, _ screenNum: Int, _ direction:SwipeDirection) -> Bool {
|
||||
let sliderValues: [SliderTesting:String] = [
|
||||
.slider1:"8",
|
||||
.slider3:"8",
|
||||
.slider4:"4.23",
|
||||
.slider5:"Above Average",
|
||||
.slider6:"Above Average"
|
||||
]
|
||||
|
||||
let slider = verifyAndAssignByType(.slider, "Response slider")!
|
||||
XCTAssert(slider.waitForExistence(timeout: 2), "Unable to locate slider on page \(screenNum)")
|
||||
XCTAssert(verifyPageByCount(screenNum, 6), "Unable to locate \"\(screenNum) of 6\"")
|
||||
XCTAssert(scaleTitle(screenNum)!.waitForExistence(timeout: 2), "Unable to locate \(scaleTitle(screenNum)!)")
|
||||
|
||||
switch direction {
|
||||
case .up:
|
||||
slider.swipeUp()
|
||||
case .right:
|
||||
slider.swipeRight()
|
||||
case .left:
|
||||
XCTFail("Unexpected Swipe Direction of Left Entered")
|
||||
case .down:
|
||||
XCTFail("Unexpected Swipe Direction of Down Entered")
|
||||
case .skip:
|
||||
XCTAssert(commonElements.skipButton!.waitForExistence(timeout: 2))
|
||||
commonElements.skipButton!.tap()
|
||||
|
||||
}
|
||||
|
||||
if sliderScreen != .slider2 {
|
||||
XCTAssert(verifyElementByType(.slider, sliderValues[sliderScreen]!))
|
||||
if commonElements.doneButton!.exists {
|
||||
commonElements.doneButton!.tap()
|
||||
return true
|
||||
}
|
||||
XCTAssert(commonElements.nextButton!.waitForExistence(timeout: 2))
|
||||
commonElements.nextButton!.tap()
|
||||
return true
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
//Returns true if sliders behave as expected on any given screen in the Scale Question task.
|
||||
func sliderScreenCheck(_ sliderScreen:SliderTesting) -> Bool {
|
||||
XCTAssert(verifyElement(app.scrollViews.otherElements.staticTexts["Scale"]))
|
||||
XCTAssert(verifyElement(app.scrollViews.otherElements.staticTexts["Your question here."]))
|
||||
|
||||
switch sliderScreen {
|
||||
case .slider1:
|
||||
XCTAssert(sliderValuesCheck(sliderScreen, 1, .right), "Slider1 Values Check Failed")
|
||||
case .slider2:
|
||||
XCTAssert(sliderValuesCheck(sliderScreen, 2, .skip), "Slider2 Values Check Failed")
|
||||
case .slider3:
|
||||
XCTAssert(sliderValuesCheck(sliderScreen, 3, .up), "Slider3 Values Check Failed")
|
||||
case .slider4:
|
||||
XCTAssert(sliderValuesCheck(sliderScreen, 4, .up), "Slider4 Values Check Failed")
|
||||
case .slider5:
|
||||
XCTAssert(sliderValuesCheck(sliderScreen, 5, .right), "Slider5 Values Check Failed")
|
||||
case .slider6:
|
||||
XCTAssert(sliderValuesCheck(sliderScreen, 6, .up), "Slider6 Values Check Failed")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
//Monitors for any alerts that may appear and interupt the normal flow of the applicaiton.
|
||||
func monitorAlerts() {
|
||||
addUIInterruptionMonitor(withDescription: "Alert") { element in
|
||||
do {
|
||||
// Push Notification
|
||||
let button = element.buttons["Allow"]
|
||||
let title = element.staticTexts["“ORKCatalog” Would Like to Send You Notifications"]
|
||||
if title.exists && button.exists {
|
||||
button.tap()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
// Location
|
||||
let button = element.buttons["Allow While Using App"]
|
||||
if button.exists {
|
||||
button.tap()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
// Microphone
|
||||
let button = element.buttons["OK"]
|
||||
let title = element.staticTexts["“ORKCatalog” Would Like to Access the Microphone"]
|
||||
if title.exists && button.exists {
|
||||
button.tap()
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func verifyElementByText(_ text: String, _ tap: Bool = false) -> Bool {
|
||||
let item = app.staticTexts["\(text)"]
|
||||
XCTAssert(item.waitForExistence(timeout: 3))
|
||||
if tap && item.isEnabled {
|
||||
item.tap()
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,46 +0,0 @@
|
||||
{
|
||||
"configurations" : [
|
||||
{
|
||||
"id" : "CE2DCF31-672A-4F3F-A538-AF0ADA4806F4",
|
||||
"name" : "Configuration 1",
|
||||
"options" : {
|
||||
|
||||
}
|
||||
}
|
||||
],
|
||||
"defaultOptions" : {
|
||||
"codeCoverage" : false,
|
||||
"targetForVariableExpansion" : {
|
||||
"containerPath" : "container:ORKCatalog.xcodeproj",
|
||||
"identifier" : "869230BD1AAA890A00BFE11B",
|
||||
"name" : "ORKCatalog"
|
||||
}
|
||||
},
|
||||
"testTargets" : [
|
||||
{
|
||||
"selectedTests" : [
|
||||
"PreSubmissionTests\/testAccessActiveTasks()",
|
||||
"PreSubmissionTests\/testAccessSurveyQuestions()",
|
||||
"PreSubmissionTests\/testAccessSurveyTasks()",
|
||||
"PreSubmissionTests\/testImageMultipleChoice()",
|
||||
"PreSubmissionTests\/testSQPickerWheel()",
|
||||
"PreSubmissionTests\/testSQSliders()",
|
||||
"PreSubmissionTests\/testSQTextEntry()",
|
||||
"PreSubmissionTests\/testWrittenMultipleChoice()"
|
||||
],
|
||||
"target" : {
|
||||
"containerPath" : "container:ORKCatalog.xcodeproj",
|
||||
"identifier" : "9622282924F04D780056E74D",
|
||||
"name" : "ORKCatalogUITests"
|
||||
}
|
||||
},
|
||||
{
|
||||
"target" : {
|
||||
"containerPath" : "container:ORKCatalog.xcodeproj",
|
||||
"identifier" : "51AF19762B59F1E400D3B399",
|
||||
"name" : "ORKCatalogTests"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 1
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user