diff --git a/.firebaserc b/.firebaserc
new file mode 100644
index 0000000..a46b46e
--- /dev/null
+++ b/.firebaserc
@@ -0,0 +1,5 @@
+{
+ "projects": {
+ "default": "stanfordspezitemplateapp"
+ }
+}
diff --git a/.firebaserc.license b/.firebaserc.license
new file mode 100644
index 0000000..9f5551c
--- /dev/null
+++ b/.firebaserc.license
@@ -0,0 +1,5 @@
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2022 Stanford University and the project authors (see CONTRIBUTORS.md)
+
+SPDX-License-Identifier: MIT
diff --git a/.github/workflows/beta-deployment.yml b/.github/workflows/beta-deployment.yml
new file mode 100644
index 0000000..1ff3698
--- /dev/null
+++ b/.github/workflows/beta-deployment.yml
@@ -0,0 +1,40 @@
+#
+# This source file is part of the Stanford Spezi Template Application open-source project
+#
+# SPDX-FileCopyrightText: 2023 Stanford University
+#
+# SPDX-License-Identifier: MIT
+#
+
+name: Beta Deployment
+
+on:
+ push:
+ branches:
+ - main
+ workflow_dispatch:
+
+jobs:
+ buildandtest:
+ name: Build and Test
+ uses: ./.github/workflows/build-and-test.yml
+ permissions:
+ security-events: write
+ actions: read
+ iosapptestflightdeployment:
+ name: iOS App TestFlight Deployment
+ needs: buildandtest
+ uses: StanfordSpezi/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2
+ secrets: inherit
+ with:
+ googleserviceinfoplistpath: 'TemplateApplication/Supporting Files/GoogleService-Info.plist'
+ setupsigning: true
+ fastlanelane: beta
+ deployfirebase:
+ name: Deploy Firebase Project
+ needs: iosapptestflightdeployment
+ uses: StanfordBDHG/.github/.github/workflows/firebase-deploy.yml@v2
+ with:
+ arguments: '--debug'
+ secrets:
+ GOOGLE_APPLICATION_CREDENTIALS_BASE64: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS_BASE64 }}
diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml
new file mode 100644
index 0000000..82a483f
--- /dev/null
+++ b/.github/workflows/build-and-test.yml
@@ -0,0 +1,39 @@
+#
+# This source file is part of the Stanford Spezi Template Application open-source project
+#
+# SPDX-FileCopyrightText: 2023 Stanford University
+#
+# SPDX-License-Identifier: MIT
+#
+
+name: Build and Test
+
+on:
+ pull_request:
+ workflow_dispatch:
+ workflow_call:
+
+jobs:
+ reuse_action:
+ name: REUSE Compliance Check
+ uses: StanfordBDHG/.github/.github/workflows/reuse.yml@v2
+ swiftlint:
+ name: SwiftLint
+ uses: StanfordBDHG/.github/.github/workflows/swiftlint.yml@v2
+ markdownlinkcheck:
+ name: Markdown Link Check
+ uses: StanfordBDHG/.github/.github/workflows/markdown-link-check.yml@v2
+ buildandtest:
+ name: Build and Test
+ uses: StanfordSpezi/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2
+ with:
+ artifactname: TemplateApplication.xcresult
+ runsonlabels: '["macOS", "self-hosted"]'
+ setupfirebaseemulator: true
+ customcommand: "firebase emulators:exec 'fastlane test'"
+ uploadcoveragereport:
+ name: Upload Coverage Report
+ needs: buildandtest
+ uses: StanfordBDHG/.github/.github/workflows/create-and-upload-coverage-report.yml@v2
+ with:
+ coveragereports: TemplateApplication.xcresult
diff --git a/.github/workflows/documentation-deployment.yml b/.github/workflows/documentation-deployment.yml
new file mode 100644
index 0000000..7236351
--- /dev/null
+++ b/.github/workflows/documentation-deployment.yml
@@ -0,0 +1,61 @@
+#
+# This source file is part of the Stanford Spezi open-source project
+#
+# SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md)
+#
+# SPDX-License-Identifier: MIT
+#
+
+name: Documentation Deployment
+
+on:
+ push:
+ branches:
+ - main
+ workflow_dispatch:
+
+permissions:
+ contents: read
+ pages: write
+ id-token: write
+
+concurrency:
+ group: "pages"
+ cancel-in-progress: true
+
+jobs:
+ deploydocs:
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ runs-on: macos-13
+ steps:
+ - uses: actions/checkout@v4
+ - uses: maxim-lobanov/setup-xcode@v1
+ with:
+ xcode-version: latest-stable
+ - name: Check environment
+ run: |
+ xcodebuild -version
+ swift --version
+ echo "env.selfhosted: ${{ env.selfhosted }}"
+ - name: Build DocC
+ run: |
+ xcodebuild docbuild -scheme TemplateApplication \
+ -derivedDataPath .derivedData \
+ -destination 'generic/platform=iOS' \
+ -skipPackagePluginValidation \
+ CODE_SIGN_IDENTITY="" \
+ CODE_SIGNING_REQUIRED=NO
+ $(xcrun --find docc) process-archive \
+ transform-for-static-hosting .derivedData/Build/Products/Debug-iphoneos/TemplateApplication.doccarchive \
+ --hosting-base-path SpeziTemplateApplication \
+ --output-path .docs
+ echo "" > .docs/index.html
+ - name: Upload artifact
+ uses: actions/upload-pages-artifact@v3
+ with:
+ path: '.docs'
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v4
diff --git a/.github/workflows/monthly-markdown-link-check.yml.yml b/.github/workflows/monthly-markdown-link-check.yml.yml
new file mode 100644
index 0000000..5a4792c
--- /dev/null
+++ b/.github/workflows/monthly-markdown-link-check.yml.yml
@@ -0,0 +1,19 @@
+#
+# This source file is part of the Stanford Spezi open-source project
+#
+# SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md)
+#
+# SPDX-License-Identifier: MIT
+#
+
+name: Monthly Markdown Link Check
+
+on:
+ # Runs at midnight on the first of every month
+ schedule:
+ - cron: "0 0 1 * *"
+
+jobs:
+ markdown_link_check:
+ name: Markdown Link Check
+ uses: StanfordBDHG/.github/.github/workflows/markdown-link-check.yml@v2
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ba04bcf
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,48 @@
+#
+# This source file is part of the Stanford Spezi Template Application open-source project
+#
+# SPDX-FileCopyrightText: 2023 Stanford University
+#
+# SPDX-License-Identifier: MIT
+#
+
+# Swift Package Manager
+*.xcodeproj
+.swiftpm
+.derivedData
+.build
+.docs
+!TemplateApplication.xcodeproj
+
+# IDE related folders
+.idea
+
+# Xcode
+xcuserdata/
+*.ipa
+*.dSYM
+*.dSYM.zip
+
+# Other files
+.DS_Store
+.env
+
+# Tests
+report.junit
+report.html
+TemplateApplication.xcresult
+
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+firebase-debug.log*
+firebase-debug.*.log*
+
+# Firebase cache
+.firebase/
+
+# Swift Package List
+TemplateApplication/package-list.json
diff --git a/.swiftlint.yml b/.swiftlint.yml
new file mode 100644
index 0000000..9c6c5a8
--- /dev/null
+++ b/.swiftlint.yml
@@ -0,0 +1,454 @@
+#
+# This source file is part of the Stanford Spezi Template Application open-source project
+#
+# SPDX-FileCopyrightText: 2023 Stanford University
+#
+# SPDX-License-Identifier: MIT
+#
+
+# The whitelist_rules configuration also includes rules that are enabled by default to provide a good overview of all rules.
+only_rules:
+ # All Images that provide context should have an accessibility label. Purely decorative images can be hidden from accessibility.
+ - accessibility_label_for_image
+ # Attributes should be on their own lines in functions and types, but on the same line as variables and imports.
+ - attributes
+ # Prefer using Array(seq) over seq.map { $0 } to convert a sequence into an Array.
+ - array_init
+ # Prefer the new block based KVO API with keypaths when using Swift 3.2 or later.
+ - block_based_kvo
+ # Non-constant variables should not be listed in a closure’s capture list to avoid confusion about closures capturing variables at creation time.
+ - capture_variable
+ # Delegate protocols should be class-only so they can be weakly referenced.
+ - class_delegate_protocol
+ # Closing brace with closing parenthesis should not have any whitespaces in the middle.
+ - closing_brace
+ # Closure bodies should not span too many lines.
+ - closure_body_length
+ # Closure end should have the same indentation as the line that started it.
+ - closure_end_indentation
+ # Closure parameters should be on the same line as opening brace.
+ - closure_parameter_position
+ # Closure expressions should have a single space inside each brace.
+ - closure_spacing
+ # Use commas to separate types in inheritance lists
+ - comma_inheritance
+ # Prefer at least one space after slashes for comments.
+ - comment_spacing
+ # All elements in a collection literal should be vertically aligned
+ - collection_alignment
+ # Colons should be next to the identifier when specifying a type and next to the key in dictionary literals.
+ - colon
+ # There should be no space before and one after any comma.
+ - comma
+ # The initializers declared in compiler protocols such as ExpressibleByArrayLiteral shouldn't be called directly.
+ - compiler_protocol_init
+ # Getter and setters in computed properties and subscripts should be in a consistent order.
+ - computed_accessors_order
+ # Conditional statements should always return on the next line
+ - conditional_returns_on_newline
+ # Prefer contains over comparing filter(where:).count to 0.
+ - contains_over_filter_count
+ # Prefer contains over using filter(where:).isEmpty
+ - contains_over_filter_is_empty
+ # Prefer `contains` over `first(where:) != nil`
+ - contains_over_first_not_nil
+ # Prefer contains over range(of:) != nil and range(of:) == nil
+ - contains_over_range_nil_comparison
+ # if, for, guard, switch, while, and catch statements shouldn't unnecessarily wrap their conditionals or arguments in parentheses.
+ - control_statement
+ # Types used for hosting only static members should be implemented as a caseless enum to avoid instantiation.
+ - convenience_type
+ # Complexity of function bodies should be limited.
+ - cyclomatic_complexity
+ # Availability checks or attributes shouldn’t be using older versions that are satisfied by the deployment target.
+ - deployment_target
+ # When registering for a notification using a block, the opaque observer that is returned should be stored so it can be removed later.
+ - discarded_notification_center_observer
+ # Discouraged direct initialization of types that can be harmful. e.g. UIDevice(), Bundle()
+ - discouraged_direct_init
+ # Prefer initializers over object literals.
+ - discouraged_object_literal
+ # Prefer non-optional booleans over optional booleans.
+ - discouraged_optional_boolean
+ # Prefer empty collection over optional collection.
+ - discouraged_optional_collection
+ # Dictionary literals with duplicated keys will crash in runtime.
+ - duplicated_key_in_dictionary_literal
+ # Duplicate Imports
+ - duplicate_imports
+ # Avoid using 'dynamic' and '@inline(__always)' together.
+ - dynamic_inline
+ # Prefer checking isEmpty over comparing collection to an empty array or dictionary literal.
+ - empty_collection_literal
+ # Prefer checking `isEmpty` over comparing `count` to zero.
+ - empty_count
+ # Arguments can be omitted when matching enums with associated types if they are not used.
+ - empty_enum_arguments
+ # Prefer () -> over Void ->.
+ - empty_parameters
+ # When using trailing closures, empty parentheses should be avoided after the method call.
+ - empty_parentheses_with_trailing_closure
+ # Prefer checking `isEmpty` over comparing string to an empty string literal.
+ - empty_string
+ # Empty XCTest method should be avoided.
+ - empty_xctest_method
+ # Number of associated values in an enum case should be low
+ - enum_case_associated_values_count
+ # Explicitly calling .init() should be avoided.
+ - explicit_init
+ # A fatalError call should have a message.
+ - fatal_error_message
+ # Files should not span too many lines.
+ # See file_length below for the exact configuration.
+ - file_length
+ # File name should not contain any whitespace.
+ - file_name_no_space
+ # Specifies how the types within a file should be ordered.
+ - file_types_order
+ # Prefer using ``.first(where:)`` over ``.filter { }.first` in collections.
+ - first_where
+ # Prefer flatMap over map followed by reduce([], +).
+ - flatmap_over_map_reduce
+ # where clauses are preferred over a single if inside a for.
+ - for_where
+ # Force casts should be avoided.
+ - force_cast
+ # Force tries should be avoided.
+ - force_try
+ # Force unwrapping should be avoided.
+ - force_unwrapping
+ # Prefer to locate parameters with defaults toward the end of the parameter list.
+ - function_default_parameter_at_end
+ # Functions bodies should not span too many lines.
+ # See function_body_length below for the exact configuration.
+ - function_body_length
+ # Number of function parameters should be low.
+ # See function_parameter_count below for the exact configuration.
+ - function_parameter_count
+ # Generic type name should only contain alphanumeric characters, start with an uppercase character and span between 1 and 20 characters in length.
+ - generic_type_name
+ # Comparing two identical operands is likely a mistake.
+ - identical_operands
+ # Identifier names should only contain alphanumeric characters and start with a lowercase character or should only contain capital letters.
+ # In an exception to the above, variable names may start with a capital letter when they are declared static and immutable.
+ # Variable names should not be too long or too short. Excluded names are listed below.
+ - identifier_name
+ # Computed read-only properties and subscripts should avoid using the get keyword.
+ - implicit_getter
+ # Prefer implicit returns in closures.
+ - implicit_return
+ # Implicitly unwrapped optionals should be avoided when possible.
+ - implicitly_unwrapped_optional
+ # Identifiers should use inclusive language that avoids discrimination against groups of people based on race, gender, or socioeconomic status
+ - inclusive_language
+ # Prefer using Set.isDisjoint(with:) over Set.intersection(_:).isEmpty.
+ - is_disjoint
+ # Discouraged explicit usage of the default separator.
+ - joined_default_parameter
+ # Tuples shouldn't have too many members. Create a custom type instead.
+ # See large_tuple below for the exact configuration.
+ - large_tuple
+ # Prefer using .last(where:) over .filter { }.last in collections.
+ - last_where
+ # Files should not contain leading whitespace.
+ - leading_whitespace
+ # CGGeometry: Struct extension properties and methods are preferred over legacy functions
+ - legacy_cggeometry_functions
+ # Struct-scoped constants are preferred over legacy global constants (CGSize, CGRect, NSPoint, ...).
+ - legacy_constant
+ # Swift constructors are preferred over legacy convenience functions (CGPointMake, CGSizeMake, UIOffsetMake, ...).
+ - legacy_constructor
+ # Prefer using the hash(into:) function instead of overriding hashValue
+ - legacy_hashing
+ # Prefer using the isMultiple(of:) function instead of using the remainder operator (%).
+ - legacy_multiple
+ # Struct extension properties and methods are preferred over legacy functions
+ - legacy_nsgeometry_functions
+ # Prefer Swift value types to bridged Objective-C reference types
+ - legacy_objc_type
+ # Prefer using type.random(in:) over legacy functions.
+ - legacy_random
+ # Lines should not span too many characters.
+ # See line_length below for the exact configuration.
+ - line_length
+ # Array and dictionary literal end should have the same indentation as the line that started it.
+ - literal_expression_end_indentation
+ # Ensure definitions have a lower access control level than their enclosing parent
+ - lower_acl_than_parent
+ # MARK comment should be in valid format. e.g. '// MARK: ...' or '// MARK: - ...'
+ - mark
+ # Declarations should be documented.
+ - missing_docs
+ # Modifier order should be consistent.
+ - modifier_order
+ # Arguments should be either on the same line, or one per line.
+ - multiline_arguments
+ # Multiline arguments should have their surrounding brackets in a new line.
+ - multiline_arguments_brackets
+ # Chained function calls should be either on the same line, or one per line.
+ - multiline_function_chains
+ # Multiline literals should have their surrounding brackets in a new line.
+ - multiline_literal_brackets
+ # Functions and methods parameters should be either on the same line, or one per line.
+ - multiline_parameters
+ # Multiline parameters should have their surrounding brackets in a new line.
+ - multiline_parameters_brackets
+ # Types and functions should only be nested to a certain level deep.
+ # See nesting below for the exact configuration.
+ - nesting
+ # Prefer Nimble operator overloads over free matcher functions.
+ - nimble_operator
+ # Prefer not to use extension access modifiers
+ - no_extension_access_modifier
+ # Fallthroughs can only be used if the case contains at least one other statement.
+ - no_fallthrough_only
+ # Don’t add a space between the method name and the parentheses.
+ - no_space_in_method_call
+ # An object should only remove itself as an observer in deinit.
+ - notification_center_detachment
+ # Static strings should be used as key in NSLocalizedString in order to genstrings work.
+ - nslocalizedstring_key
+ # NSObject subclasses should implement isEqual instead of ==.
+ - nsobject_prefer_isequal
+ # Prefer object literals over image and color inits.
+ - object_literal
+ # Opening braces should be preceded by a single space and on the same line as the declaration.
+ - opening_brace
+ # Operators should be surrounded by a single whitespace when they are being used.
+ - operator_usage_whitespace
+ # Operators should be surrounded by a single whitespace when defining them.
+ - operator_whitespace
+ # Matching an enum case against an optional enum without ‘?’ is supported on Swift 5.1 and above.
+ - optional_enum_case_matching
+ # A doc comment should be attached to a declaration.
+ - orphaned_doc_comment
+ # Extensions shouldn’t override declarations.
+ - override_in_extension
+ # Some overridden methods should always call super
+ - overridden_super_call
+ # Combine multiple pattern matching bindings by moving keywords out of tuples.
+ - pattern_matching_keywords
+ # Prefer Self over type(of: self) when accessing properties or calling methods.
+ - prefer_self_type_over_type_of_self
+ # Prefer .zero over explicit init with zero parameters (e.g. CGPoint(x: 0, y: 0))
+ - prefer_zero_over_explicit_init
+ # Prefer private over fileprivate declarations.
+ - private_over_fileprivate
+ # Combine Subject should be private.
+ - private_subject
+ # Unit tests marked private are silently skipped.
+ - private_unit_test
+ # Creating views using Interface Builder should be avoided.
+ - prohibited_interface_builder
+ # Some methods should not call super (
+ # NSFileProviderExtension: providePlaceholder(at:completionHandler:)
+ # NSTextInput doCommand(by:)
+ # NSView updateLayer()
+ # UIViewController loadView())
+ - prohibited_super_call
+ # When declaring properties in protocols, the order of accessors should be get set.
+ - protocol_property_accessors_order
+ # Prefer using .allSatisfy() or .contains() over reduce(true) or reduce(false)
+ - reduce_boolean
+ # Prefer reduce(into:_:) over reduce(_:_:) for copy-on-write types
+ - reduce_into
+ # Prefer _ = foo() over let _ = foo() when discarding a result from a function.
+ - redundant_discardable_let
+ # nil coalescing operator is only evaluated if the lhs is nil, coalescing operator with nil as rhs is redundant
+ - redundant_nil_coalescing
+ # Objective-C attribute (@objc) is redundant in declaration.
+ - redundant_objc_attribute
+ # Initializing an optional variable with nil is redundant.
+ - redundant_optional_initialization
+ # Property setter access level shouldn't be explicit if it's the same as the variable access level.
+ - redundant_set_access_control
+ # String enum values can be omitted when they are equal to the enumcase name.
+ - redundant_string_enum_value
+ # Variables should not have redundant type annotation
+ - redundant_type_annotation
+ # Returning Void in a function declaration is redundant.
+ - redundant_void_return
+ # Return arrow and return type should be separated by a single space or on a separate line.
+ - return_arrow_whitespace
+ # Returning values from Void functions should be avoided.
+ - return_value_from_void_function
+ # Re-bind self to a consistent identifier name.
+ - self_binding
+ # Prefer shorthand operators (+=, -=, *=, /=) over doing the operation and assigning.
+ - shorthand_operator
+ # Test files should contain a single QuickSpec or XCTestCase class.
+ - single_test_class
+ # Prefer using `min()`` or `max()`` over `sorted().first` or `sorted().last`
+ - sorted_first_last
+ # Imports should be sorted.
+ - sorted_imports
+ # Else and catch should be on the same line, one space after the previous declaration.
+ - statement_position
+ # Operators should be declared as static functions, not free functions.
+ - static_operator
+ # SwiftLint ‘disable’ commands are superfluous when the disabled rule would not have triggered a violation in the disabled region. Use “ - ” if you wish to document a command.
+ - superfluous_disable_command
+ # Case statements should vertically align with their enclosing switch statement, or indented if configured otherwise.
+ - switch_case_alignment
+ # Shorthand syntactic sugar should be used, i.e. [Int] instead of Array.
+ - syntactic_sugar
+ # TODOs and FIXMEs should be resolved.
+ - todo
+ # Prefer someBool.toggle() over someBool = !someBool.
+ - toggle_bool
+ # Trailing closure syntax should be used whenever possible.
+ - trailing_closure
+ # Trailing commas in arrays and dictionaries should be avoided/enforced.
+ - trailing_comma
+ # Files should have a single trailing newline.
+ - trailing_newline
+ # Lines should not have trailing semicolons.
+ - trailing_semicolon
+ # Lines should not have trailing whitespace.
+ # Ignored lines are specified below.
+ - trailing_whitespace
+ # Type bodies should not span too many lines.
+ # See large_tuple below for the exact configuration.
+ - type_body_length
+ # Specifies the order of subtypes, properties, methods & more within a type.
+ - type_contents_order
+ # Type name should only contain alphanumeric characters, start with an uppercase character and span between 3 and 40 characters in length.
+ # Excluded types are listed below.
+ - type_name
+ # Prefer using Array(seq) over seq.map { $0 } to convert a sequence into an Array.
+ - typesafe_array_init
+ # Use #unavailable/#available instead of #available/#unavailable with an empty body.
+ - unavailable_condition
+ # Unimplemented functions should be marked as unavailable.
+ - unavailable_function
+ # Avoid using unneeded break statements.
+ - unneeded_break_in_switch
+ # Parentheses are not needed when declaring closure arguments.
+ - unneeded_parentheses_in_closure_argument
+ # Prefer capturing references as weak to avoid potential crashes.
+ - unowned_variable_capture
+ # Catch statements should not declare error variables without type casting.
+ - untyped_error_in_catch
+ # Unused parameter in a closure should be replaced with _.
+ - unused_closure_parameter
+ # Unused control flow label should be removed.
+ - unused_control_flow_label
+ # Declarations should be referenced at least once within all files linted.
+ - unused_declaration
+ # When the index or the item is not used, .enumerated() can be removed.
+ - unused_enumerated
+ # All imported modules should be required to make the file compile.
+ - unused_import
+ # Prefer != nil over let _ =
+ - unused_optional_binding
+ # Setter value is not used.
+ - unused_setter_value
+ # @IBInspectable should be applied to variables only, have its type explicit and be of a supported type
+ - valid_ibinspectable
+ # Function parameters should be aligned vertically if they're in multiple lines in a declaration.
+ - vertical_parameter_alignment
+ # Function parameters should be aligned vertically if they're in multiple lines in a method call.
+ - vertical_parameter_alignment_on_call
+ # Limit vertical whitespace to a single empty line.
+ # See vertical_whitespace below for the exact configuration.
+ - vertical_whitespace
+ # Don’t include vertical whitespace (empty line) before closing braces.
+ - vertical_whitespace_closing_braces
+ # Don’t include vertical whitespace (empty line) after opening braces.
+ - vertical_whitespace_opening_braces
+ # Using ternary to call Void functions should be avoided.
+ - void_function_in_ternary
+ # Prefer -> Void over -> ().
+ - void_return
+ # Delegates should be weak to avoid reference cycles.
+ - weak_delegate
+ # Prefer specific XCTest matchers over XCTAssertEqual and XCTAssertNotEqual
+ - xct_specific_matcher
+ # An XCTFail call should include a description of the assertion.
+ - xctfail_message
+ # The variable should be placed on the left, the constant on the right of a comparison operator.
+ - yoda_condition
+
+attributes:
+ attributes_with_arguments_always_on_line_above: false
+
+deployment_target: # Availability checks or attributes shouldn’t be using older versions that are satisfied by the deployment target.
+ iOSApplicationExtension_deployment_target: 16.0
+ iOS_deployment_target: 16.0
+
+excluded: # paths to ignore during linting. Takes precedence over `included`.
+ - .build
+ - .swiftpm
+ - .codeql
+ - .derivedData
+
+closure_body_length: # Closure bodies should not span too many lines.
+ - 35 # warning - default: 20
+ - 35 # error - default: 100
+
+enum_case_associated_values_count: # Number of associated values in an enum case should be low
+ - 5 # warning - default: 5
+ - 5 # error - default: 6
+
+file_length: # Files should not span too many lines.
+ - 500 # warning - default: 400
+ - 500 # error - default: 1000
+
+function_body_length: # Functions bodies should not span too many lines.
+ - 50 # warning - default: 40
+ - 50 # error - default: 100
+
+function_parameter_count: # Number of function parameters should be low.
+ - 5 # warning - default: 5
+ - 5 # error - default: 8
+
+identifier_name:
+ excluded: # excluded names
+ - id
+ - ok
+ - or
+ - p8
+ - of
+ - s3
+ - at
+ - to
+ - in
+
+large_tuple: # Tuples shouldn't have too many members. Create a custom type instead.
+ - 2 # warning - default: 2
+ - 2 # error - default: 3
+
+line_length: # Lines should not span too many characters.
+ warning: 150 # default: 120
+ error: 150 # default: 200
+ ignores_comments: true # default: false
+ ignores_urls: true # default: false
+ ignores_function_declarations: false # default: false
+ ignores_interpolated_strings: true # default: false
+
+nesting: # Types should be nested at most 2 level deep, and functions should be nested at most 5 levels deep.
+ type_level:
+ warning: 2 # warning - default: 1
+ function_level:
+ warning: 5 # warning - default: 5
+
+trailing_closure:
+ only_single_muted_parameter: true
+
+type_body_length: # Type bodies should not span too many lines.
+ - 250 # warning - default: 200
+ - 250 # error - default: 200
+
+type_name:
+ excluded: # excluded names
+ - ID
+
+trailing_whitespace:
+ ignores_empty_lines: true # default: false
+ ignores_comments: true # default: false
+
+unused_optional_binding:
+ ignore_optional_try: true
+
+vertical_whitespace: # Limit vertical whitespace to a single empty line.
+ max_empty_lines: 2 # warning - default: 1
diff --git a/CITATION.cff b/CITATION.cff
new file mode 100644
index 0000000..7d68827
--- /dev/null
+++ b/CITATION.cff
@@ -0,0 +1,23 @@
+#
+# This source file is part of the Stanford Spezi Template Application open-source project
+#
+# SPDX-FileCopyrightText: 2023 Stanford University
+#
+# SPDX-License-Identifier: MIT
+#
+
+cff-version: 1.2.0
+message: "If you use this software, please cite it as below."
+authors:
+- family-names: "Schmiedmayer"
+ given-names: "Paul"
+ orcid: "https://orcid.org/0000-0002-8607-9148"
+- family-names: "Ravi"
+ given-names: "Vishnu"
+ orcid: "https://orcid.org/0000-0003-0359-1275"
+- family-names: "Aalami"
+ given-names: "Oliver"
+ orcid: "https://orcid.org/0000-0002-7799-2429"
+title: "Spezi Template Application"
+doi: doi.org/10.5281/zenodo.7600783
+url: "https://github.com/StanfordSpezi/SpeziTemplateApplication"
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
new file mode 100644
index 0000000..4bae8d7
--- /dev/null
+++ b/CONTRIBUTORS.md
@@ -0,0 +1,17 @@
+
+
+Template Application Contributors
+=================================
+
+* [Paul Schmiedmayer](https://github.com/PSchmiedmayer)
+* [Andreas Bauer](https://github.com/Supereg)
+* [Philipp Zagar](https://github.com/philippzagar)
+* [Nikolai Madlener](https://github.com/NikolaiMadlener)
\ No newline at end of file
diff --git a/Gemfile b/Gemfile
new file mode 100644
index 0000000..3c25e0f
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,11 @@
+#
+# This source file is part of the Stanford Spezi Template Application open-source project
+#
+# SPDX-FileCopyrightText: 2023 Stanford University
+#
+# SPDX-License-Identifier: MIT
+#
+
+source "https://rubygems.org"
+
+gem "fastlane"
\ No newline at end of file
diff --git a/Gemfile.lock b/Gemfile.lock
new file mode 100644
index 0000000..8f90740
--- /dev/null
+++ b/Gemfile.lock
@@ -0,0 +1,220 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ CFPropertyList (3.0.6)
+ rexml
+ addressable (2.8.5)
+ public_suffix (>= 2.0.2, < 6.0)
+ artifactory (3.0.15)
+ atomos (0.1.3)
+ aws-eventstream (1.2.0)
+ aws-partitions (1.809.0)
+ aws-sdk-core (3.181.0)
+ aws-eventstream (~> 1, >= 1.0.2)
+ aws-partitions (~> 1, >= 1.651.0)
+ aws-sigv4 (~> 1.5)
+ jmespath (~> 1, >= 1.6.1)
+ aws-sdk-kms (1.71.0)
+ aws-sdk-core (~> 3, >= 3.177.0)
+ aws-sigv4 (~> 1.1)
+ aws-sdk-s3 (1.133.0)
+ aws-sdk-core (~> 3, >= 3.181.0)
+ aws-sdk-kms (~> 1)
+ aws-sigv4 (~> 1.6)
+ aws-sigv4 (1.6.0)
+ aws-eventstream (~> 1, >= 1.0.2)
+ babosa (1.0.4)
+ claide (1.1.0)
+ colored (1.2)
+ colored2 (3.1.2)
+ commander (4.6.0)
+ highline (~> 2.0.0)
+ declarative (0.0.20)
+ digest-crc (0.6.5)
+ rake (>= 12.0.0, < 14.0.0)
+ domain_name (0.5.20190701)
+ unf (>= 0.0.5, < 1.0.0)
+ dotenv (2.8.1)
+ emoji_regex (3.2.3)
+ excon (0.100.0)
+ faraday (1.10.3)
+ faraday-em_http (~> 1.0)
+ faraday-em_synchrony (~> 1.0)
+ faraday-excon (~> 1.1)
+ faraday-httpclient (~> 1.0)
+ faraday-multipart (~> 1.0)
+ faraday-net_http (~> 1.0)
+ faraday-net_http_persistent (~> 1.0)
+ faraday-patron (~> 1.0)
+ faraday-rack (~> 1.0)
+ faraday-retry (~> 1.0)
+ ruby2_keywords (>= 0.0.4)
+ faraday-cookie_jar (0.0.7)
+ faraday (>= 0.8.0)
+ http-cookie (~> 1.0.0)
+ faraday-em_http (1.0.0)
+ faraday-em_synchrony (1.0.0)
+ faraday-excon (1.1.0)
+ faraday-httpclient (1.0.1)
+ faraday-multipart (1.0.4)
+ multipart-post (~> 2)
+ faraday-net_http (1.0.1)
+ faraday-net_http_persistent (1.2.0)
+ faraday-patron (1.0.0)
+ faraday-rack (1.0.0)
+ faraday-retry (1.0.3)
+ faraday_middleware (1.2.0)
+ faraday (~> 1.0)
+ fastimage (2.2.7)
+ fastlane (2.214.0)
+ CFPropertyList (>= 2.3, < 4.0.0)
+ addressable (>= 2.8, < 3.0.0)
+ artifactory (~> 3.0)
+ aws-sdk-s3 (~> 1.0)
+ babosa (>= 1.0.3, < 2.0.0)
+ bundler (>= 1.12.0, < 3.0.0)
+ colored
+ commander (~> 4.6)
+ dotenv (>= 2.1.1, < 3.0.0)
+ emoji_regex (>= 0.1, < 4.0)
+ excon (>= 0.71.0, < 1.0.0)
+ faraday (~> 1.0)
+ faraday-cookie_jar (~> 0.0.6)
+ faraday_middleware (~> 1.0)
+ fastimage (>= 2.1.0, < 3.0.0)
+ gh_inspector (>= 1.1.2, < 2.0.0)
+ google-apis-androidpublisher_v3 (~> 0.3)
+ google-apis-playcustomapp_v1 (~> 0.1)
+ google-cloud-storage (~> 1.31)
+ highline (~> 2.0)
+ json (< 3.0.0)
+ jwt (>= 2.1.0, < 3)
+ mini_magick (>= 4.9.4, < 5.0.0)
+ multipart-post (>= 2.0.0, < 3.0.0)
+ naturally (~> 2.2)
+ optparse (~> 0.1.1)
+ plist (>= 3.1.0, < 4.0.0)
+ rubyzip (>= 2.0.0, < 3.0.0)
+ security (= 0.1.3)
+ simctl (~> 1.6.3)
+ terminal-notifier (>= 2.0.0, < 3.0.0)
+ terminal-table (>= 1.4.5, < 2.0.0)
+ tty-screen (>= 0.6.3, < 1.0.0)
+ tty-spinner (>= 0.8.0, < 1.0.0)
+ word_wrap (~> 1.0.0)
+ xcodeproj (>= 1.13.0, < 2.0.0)
+ xcpretty (~> 0.3.0)
+ xcpretty-travis-formatter (>= 0.0.3)
+ gh_inspector (1.1.3)
+ google-apis-androidpublisher_v3 (0.48.0)
+ google-apis-core (>= 0.11.0, < 2.a)
+ google-apis-core (0.11.1)
+ addressable (~> 2.5, >= 2.5.1)
+ googleauth (>= 0.16.2, < 2.a)
+ httpclient (>= 2.8.1, < 3.a)
+ mini_mime (~> 1.0)
+ representable (~> 3.0)
+ retriable (>= 2.0, < 4.a)
+ rexml
+ webrick
+ google-apis-iamcredentials_v1 (0.17.0)
+ google-apis-core (>= 0.11.0, < 2.a)
+ google-apis-playcustomapp_v1 (0.13.0)
+ google-apis-core (>= 0.11.0, < 2.a)
+ google-apis-storage_v1 (0.19.0)
+ google-apis-core (>= 0.9.0, < 2.a)
+ google-cloud-core (1.6.0)
+ google-cloud-env (~> 1.0)
+ google-cloud-errors (~> 1.0)
+ google-cloud-env (1.6.0)
+ faraday (>= 0.17.3, < 3.0)
+ google-cloud-errors (1.3.1)
+ google-cloud-storage (1.44.0)
+ addressable (~> 2.8)
+ digest-crc (~> 0.4)
+ google-apis-iamcredentials_v1 (~> 0.1)
+ google-apis-storage_v1 (~> 0.19.0)
+ google-cloud-core (~> 1.6)
+ googleauth (>= 0.16.2, < 2.a)
+ mini_mime (~> 1.0)
+ googleauth (1.7.0)
+ faraday (>= 0.17.3, < 3.a)
+ jwt (>= 1.4, < 3.0)
+ memoist (~> 0.16)
+ multi_json (~> 1.11)
+ os (>= 0.9, < 2.0)
+ signet (>= 0.16, < 2.a)
+ highline (2.0.3)
+ http-cookie (1.0.5)
+ domain_name (~> 0.5)
+ httpclient (2.8.3)
+ jmespath (1.6.2)
+ json (2.6.3)
+ jwt (2.7.1)
+ memoist (0.16.2)
+ mini_magick (4.12.0)
+ mini_mime (1.1.5)
+ multi_json (1.15.0)
+ multipart-post (2.3.0)
+ nanaimo (0.3.0)
+ naturally (2.2.1)
+ optparse (0.1.1)
+ os (1.1.4)
+ plist (3.7.0)
+ public_suffix (5.0.3)
+ rake (13.0.6)
+ representable (3.2.0)
+ declarative (< 0.1.0)
+ trailblazer-option (>= 0.1.1, < 0.2.0)
+ uber (< 0.2.0)
+ retriable (3.1.2)
+ rexml (3.2.6)
+ rouge (2.0.7)
+ ruby2_keywords (0.0.5)
+ rubyzip (2.3.2)
+ security (0.1.3)
+ signet (0.17.0)
+ addressable (~> 2.8)
+ faraday (>= 0.17.5, < 3.a)
+ jwt (>= 1.5, < 3.0)
+ multi_json (~> 1.10)
+ simctl (1.6.10)
+ CFPropertyList
+ naturally
+ terminal-notifier (2.0.0)
+ terminal-table (1.8.0)
+ unicode-display_width (~> 1.1, >= 1.1.1)
+ trailblazer-option (0.1.2)
+ tty-cursor (0.7.1)
+ tty-screen (0.8.1)
+ tty-spinner (0.9.3)
+ tty-cursor (~> 0.7)
+ uber (0.1.0)
+ unf (0.1.4)
+ unf_ext
+ unf_ext (0.0.8.2)
+ unicode-display_width (1.8.0)
+ webrick (1.8.1)
+ word_wrap (1.0.0)
+ xcodeproj (1.22.0)
+ CFPropertyList (>= 2.3.3, < 4.0)
+ atomos (~> 0.1.3)
+ claide (>= 1.0.2, < 2.0)
+ colored2 (~> 3.1)
+ nanaimo (~> 0.3.0)
+ rexml (~> 3.2.4)
+ xcpretty (0.3.0)
+ rouge (~> 2.0.7)
+ xcpretty-travis-formatter (1.0.1)
+ xcpretty (~> 0.2, >= 0.0.7)
+
+PLATFORMS
+ arm64-darwin-22
+ universal-darwin-21
+ x86_64-darwin-20
+
+DEPENDENCIES
+ fastlane
+
+BUNDLED WITH
+ 2.4.10
diff --git a/Gemfile.lock.license b/Gemfile.lock.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/Gemfile.lock.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 0000000..c76f2c7
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,9 @@
+MIT License
+
+Copyright (c) 2023 Stanford University
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/LICENSES/MIT.txt b/LICENSES/MIT.txt
new file mode 100644
index 0000000..c76f2c7
--- /dev/null
+++ b/LICENSES/MIT.txt
@@ -0,0 +1,9 @@
+MIT License
+
+Copyright (c) 2023 Stanford University
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..30fd08a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,61 @@
+
+
+# Spezi Template Application
+
+[![Beta Deployment](https://github.com/StanfordSpezi/SpeziTemplateApplication/actions/workflows/beta-deployment.yml/badge.svg)](https://github.com/StanfordSpezi/SpeziTemplateApplication/actions/workflows/beta-deployment.yml)
+[![codecov](https://codecov.io/gh/StanfordSpezi/SpeziTemplateApplication/branch/main/graph/badge.svg?token=9fvSAiFJUY)](https://codecov.io/gh/StanfordSpezi/SpeziTemplateApplication)
+[![DOI](https://zenodo.org/badge/589846478.svg)](https://zenodo.org/badge/latestdoi/589846478)
+
+This repository contains the Spezi Template Application.
+It demonstrates using the [Spezi](https://github.com/StanfordSpezi/Spezi) ecosystem and builds on top of the [Stanford Biodesign Digital Health Template Application](https://github.com/StanfordBDHG/TemplateApplication).
+
+> [!NOTE]
+> Do you want to try out the Spezi Template Application? You can download it to your iOS device using [TestFlight](https://testflight.apple.com/join/ipEezBY1)!
+
+
+## Application Content
+
+The following screenshots show a wide variety of features based on Spezi Modules that are part of the Spezi Template Application.
+
+|![A screen displaying welcome information.](TemplateApplication/Supporting%20Files/TemplateApplication.docc/Resources/Onboarding/Welcome.png#gh-light-mode-only) ![A screen displaying welcome information.](TemplateApplication/Supporting%20Files/TemplateApplication.docc/Resources/Onboarding/Welcome~dark.png#gh-dark-mode-only)|![A screen showing an overview of the modules used in the Spezi Template Application.](TemplateApplication/Supporting%20Files/TemplateApplication.docc/Resources/Onboarding/InterestingModules.png#gh-light-mode-only) ![A screen showing an overview of the modules used in the Spezi Template Application.](TemplateApplication/Supporting%20Files/TemplateApplication.docc/Resources/Onboarding/InterestingModules~dark.png#gh-dark-mode-only)|![A screen displaying the consent view.](TemplateApplication/Supporting%20Files/TemplateApplication.docc/Resources/Onboarding/Consent.png#gh-light-mode-only) ![A screen displaying the consent view.](TemplateApplication/Supporting%20Files/TemplateApplication.docc/Resources/Onboarding/Consent~dark.png#gh-dark-mode-only)
+|:--:|:--:|:--:|
+|Welcome View|Interesting Modules|Consent Signature|
+
+|![HealthKit Onboarding Flow](TemplateApplication/Supporting%20Files/TemplateApplication.docc/Resources/Onboarding/HealthKitAccess.png#gh-light-mode-only) ![HealthKit Onboarding Flow](TemplateApplication/Supporting%20Files/TemplateApplication.docc/Resources/Onboarding/HealthKitAccess~dark.png#gh-dark-mode-only)|![Permissions screen of the HealthKit framework](TemplateApplication/Supporting%20Files/TemplateApplication.docc/Resources/Onboarding/HealthKitSheet.png#gh-light-mode-only) ![Permissions screen of the HealthKit framework](TemplateApplication/Supporting%20Files/TemplateApplication.docc/Resources/Onboarding/HealthKitSheet~dark.png#gh-dark-mode-only)|![A screen displaying the Scheduler UI.](TemplateApplication/Supporting%20Files/TemplateApplication.docc/Resources/Schedule/Schedule.png#gh-light-mode-only) ![A screen displaying the Scheduler UI.](TemplateApplication/Supporting%20Files/TemplateApplication.docc/Resources/Schedule/Schedule~dark.png#gh-dark-mode-only)
+|:--:|:--:|:--:|
+|HealthKit Access|Granular HealthKit Share Control|Schedule Tasks|
+
+|![Onboarding screen showing the Notifications permission screen.](TemplateApplication/Supporting%20Files/TemplateApplication.docc/Resources/Schedule/Notifications.png#gh-light-mode-only) ![Onboarding screen showing the Notifications permission screen.](TemplateApplication/Supporting%20Files/TemplateApplication.docc/Resources/Schedule/Notifications~dark.png#gh-dark-mode-only)|![A screen showing a questionnaire using ResearchKit.](TemplateApplication/Supporting%20Files/TemplateApplication.docc/Resources/Schedule/Questionnaire.png#gh-light-mode-only) ![A screen showing a questionnaire using ResearchKit.](TemplateApplication/Supporting%20Files/TemplateApplication.docc/Resources/Schedule/Questionnaire~dark.png#gh-dark-mode-only)|![The scheduler screen showing the completed UI](TemplateApplication/Supporting%20Files/TemplateApplication.docc/Resources/Schedule/ScheduleComplete.png#gh-light-mode-only) ![The scheduler screen showing the completed UI](TemplateApplication/Supporting%20Files/TemplateApplication.docc/Resources/Schedule/ScheduleComplete~dark.png#gh-dark-mode-only)
+|:--:|:--:|:--:|
+|Trigger Local Notifications|Display Questionnaires|Keep Track of Tasks|
+
+|![A screen displaying the Contact UI.](TemplateApplication/Supporting%20Files/TemplateApplication.docc/Resources/Context/Contacts.png#gh-light-mode-only) ![A screen displaying the Contact UI.](TemplateApplication/Supporting%20Files/TemplateApplication.docc/Resources/Context/Contacts~dark.png#gh-dark-mode-only)|![License information to list all used Swift Packages](TemplateApplication/Supporting%20Files/TemplateApplication.docc/Resources/Context/License.png#gh-light-mode-only) ![License information to list all used Swift Packages](TemplateApplication/Supporting%20Files/TemplateApplication.docc/Resources/Context/License~dark.png#gh-dark-mode-only)|![User Interface of the Mock Web Service](TemplateApplication/Supporting%20Files/TemplateApplication.docc/Resources/Context/Request.png#gh-light-mode-only) ![User Interface of the Mock Web Service](TemplateApplication/Supporting%20Files/TemplateApplication.docc/Resources/Context/Request~dark.png#gh-dark-mode-only)
+|:--:|:--:|:--:|
+|Contact Information|License Information|Mock Web Service Requests|
+
+> [!NOTE]
+> You can find all the used Spezi Modules in the [Stanford Spezi GitHub Organization](https://github.com/StanfordSpezi).
+
+The [DocC documentation of the Spezi Template Application contains information on how to use the application as the basis for your Spezi-based application, run the application, and modify the application](https://stanfordspezi.github.io/SpeziTemplateApplication).
+
+## Contributing
+
+Contributions to this project are welcome. Please make sure to read the [contribution guidelines](https://github.com/StanfordSpezi/.github/blob/main/CONTRIBUTING.md) and the [contributor covenant code of conduct](https://github.com/StanfordSpezi/.github/blob/main/CODE_OF_CONDUCT.md) first.
+
+This project is based on [ContinuousDelivery Example by Paul Schmiedmayer](https://github.com/PSchmiedmayer/ContinousDelivery) and the [Stanford Biodesign Digital Health Template Application](https://github.com/StanfordBDHG/TemplateApplication) provided using the MIT license.
+
+
+## License
+
+This project is licensed under the MIT License. See [Licenses](LICENSES) for more information.
+
+![Spezi Footer](https://raw.githubusercontent.com/StanfordSpezi/.github/main/assets/FooterLight.png#gh-light-mode-only)
+![Spezi Footer](https://raw.githubusercontent.com/StanfordSpezi/.github/main/assets/FooterDark.png#gh-dark-mode-only)
diff --git a/Scripts/TEMPLATEREADME.md b/Scripts/TEMPLATEREADME.md
new file mode 100644
index 0000000..edf8df8
--- /dev/null
+++ b/Scripts/TEMPLATEREADME.md
@@ -0,0 +1,32 @@
+
+
+# Spezi Template Application
+
+This repository contains the Spezi Template Application.
+The Spezi Template Application is using the [Spezi](https://github.com/StanfordSpezi/Spezi) ecosystem and builds on top of the [{{SSTA}}](https://github.com/StanfordSpezi/Spezi{{TA}}).
+
+> [!NOTE]
+> Do you want to learn more about the {{SSTA}} and how to use, extend, and modify this application? Check out the [{{SSTA}} documentation](https://stanfordspezi.github.io/Spezi{{TA}})
+
+
+## Spezi Template Application Features
+
+*Provide a comprehensive description of your application, including figures showing the application. You can learn more on how to structure a README in the [Stanford Spezi Documentation Guide](https://swiftpackageindex.com/stanfordspezi/spezi/documentation/spezi/documentation-guide)*
+
+
+## Contributing
+
+*Ensure that you add an adequate contribution section to this README.*
+
+
+## License
+
+This project is licensed under the MIT License. See [Licenses](LICENSES) for more information.
diff --git a/Scripts/create.sh b/Scripts/create.sh
new file mode 100644
index 0000000..c2d845d
--- /dev/null
+++ b/Scripts/create.sh
@@ -0,0 +1,196 @@
+#!/bin/bash
+#
+# This source file is part of the Stanford Spezi Template Application open-source project
+#
+# SPDX-FileCopyrightText: 2022 Stanford University and the project authors (see CONTRIBUTORS.md)
+#
+# SPDX-License-Identifier: MIT
+#
+
+export LC_CTYPE=UTF-8
+export LANG=en_US.UTF-8
+export LC_ALL=en_US.UTF-8
+
+
+# Function to display an error message, show the help, and exit
+error_exit_help() {
+ echo "Error: $1" >&2
+ show_help
+ exit 1
+}
+
+# Function to display help message
+show_help() {
+ echo "Usage: $0 --name --bundleIdentifier [--provisioningProfile ]"
+ echo
+ echo "Options:"
+ echo " --name Name of the application. (required)"
+ echo " --bundleIdentifier The iOS bundle identifier of the application. (required)"
+ echo " --provisioningProfile The name of the iOS provisioning profile to build the application. (optional, defaults to the value of --name)"
+ echo " --help Display this help and exit."
+}
+
+# Initialize variables
+appName=""
+bundleIdentifier=""
+provisioningProfile=""
+
+# Parse named arguments
+while [[ $# -gt 0 ]]; do
+ case "$1" in
+ --name)
+ appName="$2"
+ shift # past argument
+ shift # past value
+ ;;
+ --bundleIdentifier)
+ bundleIdentifier="$2"
+ shift # past argument
+ shift # past value
+ ;;
+ --provisioningProfile)
+ provisioningProfile="$2"
+ shift # past argument
+ shift # past value
+ ;;
+ --help)
+ show_help
+ exit 0
+ ;;
+ *)
+ error_exit_help "Unknown option: $1"
+ ;;
+ esac
+done
+
+# Check for required arguments
+if [ -z "$appName" ]; then
+ error_exit_help "The --name argument is required."
+fi
+
+if [ -z "$bundleIdentifier" ]; then
+ error_exit_help "The --bundleIdentifier argument is required."
+fi
+
+# Set default value for provisioningProfile if not provided
+if [ -z "$provisioningProfile" ]; then
+ provisioningProfile="$appName"
+fi
+
+# Remove spaces from appName
+appNameNoSpaces="${appName// /}"
+
+# Convert appName to lowercase and remove spaces
+appNameLowerNoSpaces=$(echo "$appName" | tr '[:upper:]' '[:lower:]' | tr -d ' ')
+
+# Testing the input:
+echo "Application Name: $appName"
+echo "Bundle Identifier: $bundleIdentifier"
+echo "Provisioning Profile: $provisioningProfile"
+echo "Application Name (No Spaces): $appNameNoSpaces"
+echo "Application Name (Lowercase, No Spaces): $appNameLowerNoSpaces"
+
+# Rename the bundle identifier:
+oldBundleIdentifierEscaped=$(sed 's:/:\\/:g' <<< "edu.stanford.spezi.templateapplication")
+bundleIdentifierEscaped=$(sed 's:/:\\/:g' <<< "$bundleIdentifier")
+
+find . -type f -not \( -path '*/.git/*' \) -not \( -path '*/Scripts/create.sh' \) -exec grep -Iq . {} \; -print | while read -r file; do
+ sed -i '' "s/${oldBundleIdentifierEscaped}/${bundleIdentifierEscaped}/g" "$file" || echo "Failed to process $file"
+done
+
+
+# Rename the provisioning profile:
+oldProvisioningProfileEscaped=$(sed 's:/:\\/:g' <<< "\"Spezi Template Application\"")
+provisioningProfileEscaped=$(sed 's:/:\\/:g' <<< "\"$provisioningProfile\"")
+
+sed -i '' "s/${oldProvisioningProfileEscaped}/${provisioningProfileEscaped}/g" "./fastlane/Fastfile"
+sed -i '' "s/${oldProvisioningProfileEscaped}/${provisioningProfileEscaped}/g" "./TemplateApplication.xcodeproj/project.pbxproj"
+
+
+# Firebase project name:
+firebaseProjectNameEscaped=$(sed 's:/:\\/:g' <<< "stanfordspezitemplateapp")
+appNameLowerNoSpacesEscaped=$(sed 's:/:\\/:g' <<< "$appNameLowerNoSpaces")
+
+sed -i '' "s/${firebaseProjectNameEscaped}/${appNameLowerNoSpacesEscaped}/g" ".firebaserc"
+sed -i '' "s/${firebaseProjectNameEscaped}/${appNameLowerNoSpacesEscaped}/g" "./TemplateApplication/Supporting Files/GoogleService-Info.plist"
+
+
+# Rename project and code:
+projectNameLowercaseEscaped=$(sed 's:/:\\/:g' <<< "templateapplication")
+headerFileEscaped=$(sed 's:/:\\/:g' <<< "Stanford Spezi Template Application open-source")
+projectNameNoSpacesEscaped=$(sed 's:/:\\/:g' <<< "TemplateApplication")
+projectNameSpeziEscaped=$(sed 's:/:\\/:g' <<< "Spezi Template Application")
+projectNameEscaped=$(sed 's:/:\\/:g' <<< "Template Application")
+templateEscaped=$(sed 's:/:\\/:g' <<< "Template")
+sstaEscaped=$(sed 's:/:\\/:g' <<< "{{SSTA}}")
+taEscaped=$(sed 's:/:\\/:g' <<< "{{TA}}")
+
+sstaFullEscaped=$(sed 's:/:\\/:g' <<< "Stanford Spezi Template Application")
+taFullEscaped=$(sed 's:/:\\/:g' <<< "TemplateApplication")
+newHeaderFileEscaped=$(sed 's:/:\\/:g' <<< "$appName based on the $sstaEscaped")
+appNameEscaped=$(sed 's:/:\\/:g' <<< "$appName")
+appNameNoSpacesEscaped=$(sed 's:/:\\/:g' <<< "$appNameNoSpaces")
+
+find . -type f -not \( -path '*/.git/*' \) -not \( -path '*/Scripts/create.sh' \) -exec grep -Iq . {} \; -print | while read -r file; do
+ sed -i '' "s/${projectNameLowercaseEscaped}/${appNameLowerNoSpacesEscaped}/g" "$file" || echo "Failed to process $file"
+ sed -i '' "s/${headerFileEscaped}/${newHeaderFileEscaped}/g" "$file" || echo "Failed to process $file"
+ sed -i '' "s/${projectNameNoSpacesEscaped}/${appNameNoSpacesEscaped}/g" "$file" || echo "Failed to process $file"
+ sed -i '' "s/${projectNameSpeziEscaped}/${appNameEscaped}/g" "$file" || echo "Failed to process $file"
+ sed -i '' "s/${projectNameEscaped}/${appNameEscaped}/g" "$file" || echo "Failed to process $file"
+ sed -i '' "s/${templateEscaped}/${appNameNoSpacesEscaped}/g" "$file" || echo "Failed to process $file"
+ sed -i '' "s/${sstaEscaped}/${sstaFullEscaped}/g" "$file" || echo "Failed to process $file"
+ sed -i '' "s/${taEscaped}/${taFullEscaped}/g" "$file" || echo "Failed to process $file"
+done
+
+# Remove the repo link and DOI from the citation file:
+# Specify the file name
+citationFile="CITATION.cff"
+total_lines=$(wc -l < "$citationFile")
+lines_to_keep=$((total_lines - 2))
+
+# Check if the file has more than 3 lines
+if [ "$lines_to_keep" -ge 1 ]; then
+ # Output the first N lines to a temporary file
+ head -n "$lines_to_keep" "$citationFile" > ".$citationFile"
+ # Replace the original file with the temporary file
+ mv ".$citationFile" "$citationFile"
+else
+ echo "$citationFile has less than 3 lines, nothing will be removed."
+fi
+
+
+# Rename files and directories
+# Function to recursively rename directories
+rename_directories() {
+ base_dir=$1
+ find "$base_dir" -depth -type d -name "*${projectNameNoSpacesEscaped}*" | while read -r dir; do
+ new_dir=$(echo "$dir" | sed "s/${projectNameNoSpacesEscaped}/${appNameNoSpacesEscaped}/g")
+ mv "$dir" "$new_dir"
+ # Prevent reprocessing of already renamed directories
+ rename_directories "$new_dir"
+ done
+}
+
+# Rename directories
+rename_directories "."
+
+# Rename files
+find . -type f -name "*${projectNameNoSpacesEscaped}*" | while read -r file; do
+ new_file=$(echo "$file" | sed "s/${projectNameNoSpacesEscaped}/${appNameNoSpacesEscaped}/g")
+ # Check if the new file path's directory exists before moving
+ new_dir=$(dirname "$new_file")
+ if [ -d "$new_dir" ]; then
+ mv "$file" "$new_file"
+ fi
+done
+
+# Remove the DocC documentation, Figures, and replace the README with a placeholder README
+rm -rf "./${appNameNoSpacesEscaped}/Supporting Files/${appNameNoSpacesEscaped}.docc"
+mv "./Scripts/TEMPLATEREADME.md" "./README.md"
+
+linkCheckDisabledEscaped=$(sed 's:/:\\/:g' <<< "")
+sed -i '' "s/${linkCheckDisabledEscaped}//g" "./README.md"
+
+
+rm -rf "./Scripts"
+rm -f "./.github/workflows/documentation-deployment.yml"
diff --git a/Scripts/setup.sh b/Scripts/setup.sh
new file mode 100644
index 0000000..351de55
--- /dev/null
+++ b/Scripts/setup.sh
@@ -0,0 +1,47 @@
+#!/bin/s
+#
+# This source file is part of the Stanford Spezi Template Application open-source project
+#
+# SPDX-FileCopyrightText: 2022 Stanford University and the project authors (see CONTRIBUTORS.md)
+#
+# SPDX-License-Identifier: MIT
+#
+
+# Script to document and automate the installation of software needed for the Spezi Template Application
+#
+# It is required that Xcode is installed on the macOS instance.
+
+# 1. Install homebrew
+export NONINTERACTIVE=1
+/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
+echo; echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile
+eval "$(/opt/homebrew/bin/brew shellenv)"
+
+
+# 2. Install tools
+brew install java
+sudo ln -sfn /opt/homebrew/opt/openjdk/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk.jdk
+echo 'export PATH="/opt/homebrew/opt/openjdk/bin:$PATH"' >> ~/.zshrc
+
+brew install node
+brew install firebase-cli
+brew install fastlane
+# Set the local correctly to work with fastlane
+echo 'export LC_ALL=en_US.UTF-8' >> ~/.zshrc
+echo 'export LANG=en_US.UTF-8' >> ~/.zshrc
+
+brew install swiftlint
+
+brew install git-lfs
+git lfs install
+git lfs install --system
+
+brew tap FelixHerrmann/tap
+brew install swift-package-list
+
+# Ensure that everything on the system is up-to-date
+brew upgrade
+
+
+# 3. Test and start the firebase emulator
+firebase emulators:exec --project test "echo 'Firebase emulator installed and started successfully!'"
diff --git a/TemplateApplication.xcodeproj/project.pbxproj b/TemplateApplication.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..2ca6636
--- /dev/null
+++ b/TemplateApplication.xcodeproj/project.pbxproj
@@ -0,0 +1,1414 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 55;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 27FA29902A388E9B009CAC45 /* ModalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27FA298F2A388E9B009CAC45 /* ModalView.swift */; };
+ 2F1AC9DF2B4E840E00C24973 /* TemplateApplication.docc in Sources */ = {isa = PBXBuildFile; fileRef = 2F1AC9DE2B4E840E00C24973 /* TemplateApplication.docc */; };
+ 2F1B52CE2A4F5CCE003AE151 /* MockUploadTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F1B52CD2A4F5CCE003AE151 /* MockUploadTests.swift */; };
+ 2F3D4ABC2A4E7C290068FB2F /* SpeziScheduler in Frameworks */ = {isa = PBXBuildFile; productRef = 2F3D4ABB2A4E7C290068FB2F /* SpeziScheduler */; };
+ 2F49B7762980407C00BCB272 /* Spezi in Frameworks */ = {isa = PBXBuildFile; productRef = 2F49B7752980407B00BCB272 /* Spezi */; };
+ 2F4E237E2989A2FE0013F3D9 /* OnboardingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F4E237D2989A2FE0013F3D9 /* OnboardingTests.swift */; };
+ 2F4E23832989D51F0013F3D9 /* TemplateApplicationTestingSetup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F4E23822989D51F0013F3D9 /* TemplateApplicationTestingSetup.swift */; };
+ 2F4E23872989DB360013F3D9 /* ContactsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F4E23862989DB360013F3D9 /* ContactsTests.swift */; };
+ 2F4FC8D729EE69D300BFFE26 /* MockUpload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F4FC8D629EE69D300BFFE26 /* MockUpload.swift */; };
+ 2F5E32BD297E05EA003432F8 /* TemplateApplicationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F5E32BC297E05EA003432F8 /* TemplateApplicationDelegate.swift */; };
+ 2F6025CB29BBE70F0045459E /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 2F6025CA29BBE70F0045459E /* GoogleService-Info.plist */; };
+ 2F65B44E2A3B8B0600A36932 /* NotificationPermissions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F65B44D2A3B8B0600A36932 /* NotificationPermissions.swift */; };
+ 2FA0BFED2ACC977500E0EF83 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 2FA0BFEC2ACC977500E0EF83 /* Localizable.xcstrings */; };
+ 2FB099AF2A875DF100B20952 /* FirebaseAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 2FB099AE2A875DF100B20952 /* FirebaseAuth */; };
+ 2FB099B12A875DF100B20952 /* FirebaseFirestore in Frameworks */ = {isa = PBXBuildFile; productRef = 2FB099B02A875DF100B20952 /* FirebaseFirestore */; };
+ 2FB099B32A875DF100B20952 /* FirebaseFirestoreSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 2FB099B22A875DF100B20952 /* FirebaseFirestoreSwift */; };
+ 2FB099B62A875E2B00B20952 /* HealthKitOnFHIR in Frameworks */ = {isa = PBXBuildFile; productRef = 2FB099B52A875E2B00B20952 /* HealthKitOnFHIR */; };
+ 2FBD738C2A3BD150004228E7 /* SpeziScheduler in Frameworks */ = {isa = PBXBuildFile; productRef = 2FBD738B2A3BD150004228E7 /* SpeziScheduler */; };
+ 2FC3439029EE6346002D773C /* SocialSupportQuestionnaire.json in Resources */ = {isa = PBXBuildFile; fileRef = 2FE5DC5529EDD811004B9AB4 /* SocialSupportQuestionnaire.json */; };
+ 2FC3439129EE6349002D773C /* AppIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = 2FE5DC2A29EDD78D004B9AB4 /* AppIcon.png */; };
+ 2FC3439229EE634B002D773C /* ConsentDocument.md in Resources */ = {isa = PBXBuildFile; fileRef = 2FE5DC2C29EDD78E004B9AB4 /* ConsentDocument.md */; };
+ 2FC975A82978F11A00BA99FE /* Home.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FC975A72978F11A00BA99FE /* Home.swift */; };
+ 2FE5DC2629EDD38A004B9AB4 /* Contacts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FE5DC2529EDD38A004B9AB4 /* Contacts.swift */; };
+ 2FE5DC3529EDD7CA004B9AB4 /* Consent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FE5DC2F29EDD7CA004B9AB4 /* Consent.swift */; };
+ 2FE5DC3629EDD7CA004B9AB4 /* HealthKitPermissions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FE5DC3029EDD7CA004B9AB4 /* HealthKitPermissions.swift */; };
+ 2FE5DC3729EDD7CA004B9AB4 /* OnboardingFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FE5DC3129EDD7CA004B9AB4 /* OnboardingFlow.swift */; };
+ 2FE5DC3829EDD7CA004B9AB4 /* InterestingModules.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FE5DC3229EDD7CA004B9AB4 /* InterestingModules.swift */; };
+ 2FE5DC3A29EDD7CA004B9AB4 /* Welcome.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FE5DC3429EDD7CA004B9AB4 /* Welcome.swift */; };
+ 2FE5DC4029EDD7EE004B9AB4 /* FeatureFlags.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FE5DC3E29EDD7ED004B9AB4 /* FeatureFlags.swift */; };
+ 2FE5DC4129EDD7EE004B9AB4 /* StorageKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FE5DC3F29EDD7EE004B9AB4 /* StorageKeys.swift */; };
+ 2FE5DC4529EDD7F2004B9AB4 /* Binding+Negate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FE5DC4229EDD7F2004B9AB4 /* Binding+Negate.swift */; };
+ 2FE5DC4629EDD7F2004B9AB4 /* Bundle+Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FE5DC4329EDD7F2004B9AB4 /* Bundle+Image.swift */; };
+ 2FE5DC4729EDD7F2004B9AB4 /* CodableArray+RawRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FE5DC4429EDD7F2004B9AB4 /* CodableArray+RawRepresentable.swift */; };
+ 2FE5DC4E29EDD7FA004B9AB4 /* ScheduleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FE5DC4829EDD7FA004B9AB4 /* ScheduleView.swift */; };
+ 2FE5DC4F29EDD7FA004B9AB4 /* EventContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FE5DC4929EDD7FA004B9AB4 /* EventContext.swift */; };
+ 2FE5DC5029EDD7FA004B9AB4 /* EventContextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FE5DC4A29EDD7FA004B9AB4 /* EventContextView.swift */; };
+ 2FE5DC5129EDD7FA004B9AB4 /* TemplateApplicationTaskContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FE5DC4B29EDD7FA004B9AB4 /* TemplateApplicationTaskContext.swift */; };
+ 2FE5DC5229EDD7FA004B9AB4 /* TemplateApplicationScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FE5DC4C29EDD7FA004B9AB4 /* TemplateApplicationScheduler.swift */; };
+ 2FE5DC5329EDD7FA004B9AB4 /* Bundle+Questionnaire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FE5DC4D29EDD7FA004B9AB4 /* Bundle+Questionnaire.swift */; };
+ 2FE5DC6429EDD883004B9AB4 /* SpeziAccount in Frameworks */ = {isa = PBXBuildFile; productRef = 2FE5DC6329EDD883004B9AB4 /* SpeziAccount */; };
+ 2FE5DC6729EDD894004B9AB4 /* SpeziContact in Frameworks */ = {isa = PBXBuildFile; productRef = 2FE5DC6629EDD894004B9AB4 /* SpeziContact */; };
+ 2FE5DC7229EDD8D3004B9AB4 /* SpeziHealthKit in Frameworks */ = {isa = PBXBuildFile; productRef = 2FE5DC7129EDD8D3004B9AB4 /* SpeziHealthKit */; };
+ 2FE5DC7529EDD8E6004B9AB4 /* SpeziFirebaseAccount in Frameworks */ = {isa = PBXBuildFile; productRef = 2FE5DC7429EDD8E6004B9AB4 /* SpeziFirebaseAccount */; };
+ 2FE5DC7729EDD8E6004B9AB4 /* SpeziFirebaseConfiguration in Frameworks */ = {isa = PBXBuildFile; productRef = 2FE5DC7629EDD8E6004B9AB4 /* SpeziFirebaseConfiguration */; };
+ 2FE5DC7929EDD8E6004B9AB4 /* SpeziFirestore in Frameworks */ = {isa = PBXBuildFile; productRef = 2FE5DC7829EDD8E6004B9AB4 /* SpeziFirestore */; };
+ 2FE5DC8429EDD934004B9AB4 /* SpeziQuestionnaire in Frameworks */ = {isa = PBXBuildFile; productRef = 2FE5DC8329EDD934004B9AB4 /* SpeziQuestionnaire */; };
+ 2FE5DC8A29EDD972004B9AB4 /* SpeziLocalStorage in Frameworks */ = {isa = PBXBuildFile; productRef = 2FE5DC8929EDD972004B9AB4 /* SpeziLocalStorage */; };
+ 2FE5DC8C29EDD972004B9AB4 /* SpeziSecureStorage in Frameworks */ = {isa = PBXBuildFile; productRef = 2FE5DC8B29EDD972004B9AB4 /* SpeziSecureStorage */; };
+ 2FE5DC8F29EDD980004B9AB4 /* SpeziViews in Frameworks */ = {isa = PBXBuildFile; productRef = 2FE5DC8E29EDD980004B9AB4 /* SpeziViews */; };
+ 2FE5DC9929EDD9D9004B9AB4 /* XCTestExtensions in Frameworks */ = {isa = PBXBuildFile; productRef = 2FE5DC9829EDD9D9004B9AB4 /* XCTestExtensions */; };
+ 2FE5DC9C29EDD9EF004B9AB4 /* XCTHealthKit in Frameworks */ = {isa = PBXBuildFile; productRef = 2FE5DC9B29EDD9EF004B9AB4 /* XCTHealthKit */; };
+ 2FE5DCB129EE6107004B9AB4 /* AccountOnboarding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FE5DCAC29EE6107004B9AB4 /* AccountOnboarding.swift */; };
+ 2FF53D8B2A8725DE00042B76 /* SpeziMockWebService in Frameworks */ = {isa = PBXBuildFile; productRef = 2FF53D8A2A8725DE00042B76 /* SpeziMockWebService */; };
+ 2FF53D8D2A8729D600042B76 /* TemplateApplicationStandard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FF53D8C2A8729D600042B76 /* TemplateApplicationStandard.swift */; };
+ 5661551D2AB8384200209B80 /* SwiftPackageList in Frameworks */ = {isa = PBXBuildFile; productRef = 5661551C2AB8384200209B80 /* SwiftPackageList */; };
+ 566155292AB8447C00209B80 /* Package+LicenseType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 566155282AB8447C00209B80 /* Package+LicenseType.swift */; };
+ 5661552E2AB854C000209B80 /* PackageHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5661552D2AB854C000209B80 /* PackageHelper.swift */; };
+ 5680DD392AB8983D004E6D4A /* PackageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5680DD382AB8983D004E6D4A /* PackageCell.swift */; };
+ 5680DD3E2AB8CD84004E6D4A /* ContributionsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5680DD3D2AB8CD84004E6D4A /* ContributionsTest.swift */; };
+ 56F6F2A02AB441930022FE5A /* ContributionsList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56F6F29F2AB441930022FE5A /* ContributionsList.swift */; };
+ 653A2551283387FE005D4D48 /* TemplateApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 653A2550283387FE005D4D48 /* TemplateApplication.swift */; };
+ 653A255528338800005D4D48 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 653A255428338800005D4D48 /* Assets.xcassets */; };
+ 653A256228338800005D4D48 /* TemplateApplicationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 653A256128338800005D4D48 /* TemplateApplicationTests.swift */; };
+ 653A256C28338800005D4D48 /* SchedulerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 653A256B28338800005D4D48 /* SchedulerTests.swift */; };
+ 9733CFC62A8066DE001B7ABC /* SpeziOnboarding in Frameworks */ = {isa = PBXBuildFile; productRef = 2FE5DC8029EDD91D004B9AB4 /* SpeziOnboarding */; };
+ 9739A0C62AD7B5730084BEA5 /* FirebaseStorage in Frameworks */ = {isa = PBXBuildFile; productRef = 9739A0C52AD7B5730084BEA5 /* FirebaseStorage */; };
+ 97D73D6A2AD860AD00B47FA0 /* SpeziFirebaseStorage in Frameworks */ = {isa = PBXBuildFile; productRef = 97D73D692AD860AD00B47FA0 /* SpeziFirebaseStorage */; };
+ A9720E432ABB68CC00872D23 /* AccountSetupHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9720E422ABB68CC00872D23 /* AccountSetupHeader.swift */; };
+ A9D83F962B083794000D0C78 /* SpeziFirebaseAccountStorage in Frameworks */ = {isa = PBXBuildFile; productRef = A9D83F952B083794000D0C78 /* SpeziFirebaseAccountStorage */; };
+ A9DFE8A92ABE551400428242 /* AccountButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9DFE8A82ABE551400428242 /* AccountButton.swift */; };
+ A9FE7AD02AA39BAB0077B045 /* AccountSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9FE7ACF2AA39BAB0077B045 /* AccountSheet.swift */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 653A255E28338800005D4D48 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 653A2545283387FE005D4D48 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 653A254C283387FE005D4D48;
+ remoteInfo = TemplateApplication;
+ };
+ 653A256828338800005D4D48 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 653A2545283387FE005D4D48 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 653A254C283387FE005D4D48;
+ remoteInfo = TemplateApplication;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+ 27FA298F2A388E9B009CAC45 /* ModalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalView.swift; sourceTree = ""; };
+ 2F1AC9DE2B4E840E00C24973 /* TemplateApplication.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = TemplateApplication.docc; sourceTree = ""; };
+ 2F1B52CD2A4F5CCE003AE151 /* MockUploadTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockUploadTests.swift; sourceTree = ""; };
+ 2F4E237D2989A2FE0013F3D9 /* OnboardingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingTests.swift; sourceTree = ""; };
+ 2F4E23822989D51F0013F3D9 /* TemplateApplicationTestingSetup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateApplicationTestingSetup.swift; sourceTree = ""; };
+ 2F4E23862989DB360013F3D9 /* ContactsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactsTests.swift; sourceTree = ""; };
+ 2F4FC8D629EE69D300BFFE26 /* MockUpload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockUpload.swift; sourceTree = ""; };
+ 2F5E32BC297E05EA003432F8 /* TemplateApplicationDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateApplicationDelegate.swift; sourceTree = ""; };
+ 2F6025CA29BBE70F0045459E /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; };
+ 2F65B44D2A3B8B0600A36932 /* NotificationPermissions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationPermissions.swift; sourceTree = ""; };
+ 2FA0BFEC2ACC977500E0EF83 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; };
+ 2FAEC07F297F583900C11C42 /* TemplateApplication.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = TemplateApplication.entitlements; sourceTree = ""; };
+ 2FC94CD4298B0A1D009C8209 /* TemplateApplication.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = TemplateApplication.xctestplan; sourceTree = ""; };
+ 2FC975A72978F11A00BA99FE /* Home.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Home.swift; sourceTree = ""; };
+ 2FE5DC2529EDD38A004B9AB4 /* Contacts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Contacts.swift; sourceTree = ""; };
+ 2FE5DC2A29EDD78D004B9AB4 /* AppIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = AppIcon.png; sourceTree = ""; };
+ 2FE5DC2C29EDD78E004B9AB4 /* ConsentDocument.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = ConsentDocument.md; sourceTree = ""; };
+ 2FE5DC2F29EDD7CA004B9AB4 /* Consent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Consent.swift; sourceTree = ""; };
+ 2FE5DC3029EDD7CA004B9AB4 /* HealthKitPermissions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HealthKitPermissions.swift; sourceTree = ""; };
+ 2FE5DC3129EDD7CA004B9AB4 /* OnboardingFlow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnboardingFlow.swift; sourceTree = ""; };
+ 2FE5DC3229EDD7CA004B9AB4 /* InterestingModules.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterestingModules.swift; sourceTree = ""; };
+ 2FE5DC3429EDD7CA004B9AB4 /* Welcome.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Welcome.swift; sourceTree = ""; };
+ 2FE5DC3E29EDD7ED004B9AB4 /* FeatureFlags.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeatureFlags.swift; sourceTree = ""; };
+ 2FE5DC3F29EDD7EE004B9AB4 /* StorageKeys.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StorageKeys.swift; sourceTree = ""; };
+ 2FE5DC4229EDD7F2004B9AB4 /* Binding+Negate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Binding+Negate.swift"; sourceTree = ""; };
+ 2FE5DC4329EDD7F2004B9AB4 /* Bundle+Image.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Bundle+Image.swift"; sourceTree = ""; };
+ 2FE5DC4429EDD7F2004B9AB4 /* CodableArray+RawRepresentable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CodableArray+RawRepresentable.swift"; sourceTree = ""; };
+ 2FE5DC4829EDD7FA004B9AB4 /* ScheduleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScheduleView.swift; sourceTree = ""; };
+ 2FE5DC4929EDD7FA004B9AB4 /* EventContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventContext.swift; sourceTree = ""; };
+ 2FE5DC4A29EDD7FA004B9AB4 /* EventContextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventContextView.swift; sourceTree = ""; };
+ 2FE5DC4B29EDD7FA004B9AB4 /* TemplateApplicationTaskContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TemplateApplicationTaskContext.swift; sourceTree = ""; };
+ 2FE5DC4C29EDD7FA004B9AB4 /* TemplateApplicationScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TemplateApplicationScheduler.swift; sourceTree = ""; };
+ 2FE5DC4D29EDD7FA004B9AB4 /* Bundle+Questionnaire.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Bundle+Questionnaire.swift"; sourceTree = ""; };
+ 2FE5DC5529EDD811004B9AB4 /* SocialSupportQuestionnaire.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = SocialSupportQuestionnaire.json; sourceTree = ""; };
+ 2FE5DCAC29EE6107004B9AB4 /* AccountOnboarding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountOnboarding.swift; sourceTree = ""; };
+ 2FF53D8C2A8729D600042B76 /* TemplateApplicationStandard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TemplateApplicationStandard.swift; sourceTree = ""; };
+ 566155282AB8447C00209B80 /* Package+LicenseType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Package+LicenseType.swift"; sourceTree = ""; };
+ 5661552D2AB854C000209B80 /* PackageHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PackageHelper.swift; sourceTree = ""; };
+ 5680DD382AB8983D004E6D4A /* PackageCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PackageCell.swift; sourceTree = ""; };
+ 5680DD3D2AB8CD84004E6D4A /* ContributionsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContributionsTest.swift; sourceTree = ""; };
+ 56F6F29F2AB441930022FE5A /* ContributionsList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContributionsList.swift; sourceTree = ""; };
+ 653A254D283387FE005D4D48 /* TemplateApplication.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TemplateApplication.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 653A2550283387FE005D4D48 /* TemplateApplication.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateApplication.swift; sourceTree = ""; };
+ 653A255428338800005D4D48 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 653A255D28338800005D4D48 /* TemplateApplicationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TemplateApplicationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ 653A256128338800005D4D48 /* TemplateApplicationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateApplicationTests.swift; sourceTree = ""; };
+ 653A256728338800005D4D48 /* TemplateApplicationUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TemplateApplicationUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ 653A256B28338800005D4D48 /* SchedulerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SchedulerTests.swift; sourceTree = ""; };
+ 653A258928339462005D4D48 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ A9720E422ABB68CC00872D23 /* AccountSetupHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountSetupHeader.swift; sourceTree = ""; };
+ A9DFE8A82ABE551400428242 /* AccountButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountButton.swift; sourceTree = ""; };
+ A9FE7ACF2AA39BAB0077B045 /* AccountSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountSheet.swift; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 653A254A283387FE005D4D48 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 9733CFC62A8066DE001B7ABC /* SpeziOnboarding in Frameworks */,
+ 2FE5DC6429EDD883004B9AB4 /* SpeziAccount in Frameworks */,
+ 2FB099AF2A875DF100B20952 /* FirebaseAuth in Frameworks */,
+ 97D73D6A2AD860AD00B47FA0 /* SpeziFirebaseStorage in Frameworks */,
+ 2FE5DC6729EDD894004B9AB4 /* SpeziContact in Frameworks */,
+ 2FE5DC8429EDD934004B9AB4 /* SpeziQuestionnaire in Frameworks */,
+ 2FB099B32A875DF100B20952 /* FirebaseFirestoreSwift in Frameworks */,
+ 5661551D2AB8384200209B80 /* SwiftPackageList in Frameworks */,
+ 2FB099B12A875DF100B20952 /* FirebaseFirestore in Frameworks */,
+ A9D83F962B083794000D0C78 /* SpeziFirebaseAccountStorage in Frameworks */,
+ 2FB099B62A875E2B00B20952 /* HealthKitOnFHIR in Frameworks */,
+ 2FE5DC8A29EDD972004B9AB4 /* SpeziLocalStorage in Frameworks */,
+ 2FE5DC8C29EDD972004B9AB4 /* SpeziSecureStorage in Frameworks */,
+ 2FE5DC7529EDD8E6004B9AB4 /* SpeziFirebaseAccount in Frameworks */,
+ 9739A0C62AD7B5730084BEA5 /* FirebaseStorage in Frameworks */,
+ 2FF53D8B2A8725DE00042B76 /* SpeziMockWebService in Frameworks */,
+ 2FE5DC7229EDD8D3004B9AB4 /* SpeziHealthKit in Frameworks */,
+ 2F49B7762980407C00BCB272 /* Spezi in Frameworks */,
+ 2FE5DC8F29EDD980004B9AB4 /* SpeziViews in Frameworks */,
+ 2F3D4ABC2A4E7C290068FB2F /* SpeziScheduler in Frameworks */,
+ 2FBD738C2A3BD150004228E7 /* SpeziScheduler in Frameworks */,
+ 2FE5DC7929EDD8E6004B9AB4 /* SpeziFirestore in Frameworks */,
+ 2FE5DC7729EDD8E6004B9AB4 /* SpeziFirebaseConfiguration in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 653A255A28338800005D4D48 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 653A256428338800005D4D48 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 2FE5DC9929EDD9D9004B9AB4 /* XCTestExtensions in Frameworks */,
+ 2FE5DC9C29EDD9EF004B9AB4 /* XCTHealthKit in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 2F4FC8D529EE69BE00BFFE26 /* MockUpload */ = {
+ isa = PBXGroup;
+ children = (
+ 2F4FC8D629EE69D300BFFE26 /* MockUpload.swift */,
+ );
+ path = MockUpload;
+ sourceTree = "";
+ };
+ 2FC9759D2978E30800BA99FE /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ 2FAEC07F297F583900C11C42 /* TemplateApplication.entitlements */,
+ 653A258928339462005D4D48 /* Info.plist */,
+ 2F6025CA29BBE70F0045459E /* GoogleService-Info.plist */,
+ 2F1AC9DE2B4E840E00C24973 /* TemplateApplication.docc */,
+ );
+ path = "Supporting Files";
+ sourceTree = "";
+ };
+ 2FE5DC2729EDD38D004B9AB4 /* Contacts */ = {
+ isa = PBXGroup;
+ children = (
+ 2FE5DC2529EDD38A004B9AB4 /* Contacts.swift */,
+ );
+ path = Contacts;
+ sourceTree = "";
+ };
+ 2FE5DC2829EDD398004B9AB4 /* Onboarding */ = {
+ isa = PBXGroup;
+ children = (
+ 2FE5DC3129EDD7CA004B9AB4 /* OnboardingFlow.swift */,
+ 2FE5DC3429EDD7CA004B9AB4 /* Welcome.swift */,
+ 2FE5DC3229EDD7CA004B9AB4 /* InterestingModules.swift */,
+ 2FE5DCAC29EE6107004B9AB4 /* AccountOnboarding.swift */,
+ 2FE5DC2F29EDD7CA004B9AB4 /* Consent.swift */,
+ 2FE5DC3029EDD7CA004B9AB4 /* HealthKitPermissions.swift */,
+ 2F65B44D2A3B8B0600A36932 /* NotificationPermissions.swift */,
+ );
+ path = Onboarding;
+ sourceTree = "";
+ };
+ 2FE5DC2D29EDD792004B9AB4 /* Resources */ = {
+ isa = PBXGroup;
+ children = (
+ 653A255428338800005D4D48 /* Assets.xcassets */,
+ 2FA0BFEC2ACC977500E0EF83 /* Localizable.xcstrings */,
+ 2FE5DC2C29EDD78E004B9AB4 /* ConsentDocument.md */,
+ 2FE5DC2A29EDD78D004B9AB4 /* AppIcon.png */,
+ 2FE5DC5529EDD811004B9AB4 /* SocialSupportQuestionnaire.json */,
+ );
+ path = Resources;
+ sourceTree = "";
+ };
+ 2FE5DC3B29EDD7D0004B9AB4 /* Schedule */ = {
+ isa = PBXGroup;
+ children = (
+ 2FE5DC4D29EDD7FA004B9AB4 /* Bundle+Questionnaire.swift */,
+ 2FE5DC4929EDD7FA004B9AB4 /* EventContext.swift */,
+ 2FE5DC4A29EDD7FA004B9AB4 /* EventContextView.swift */,
+ 2FE5DC4829EDD7FA004B9AB4 /* ScheduleView.swift */,
+ 2FE5DC4C29EDD7FA004B9AB4 /* TemplateApplicationScheduler.swift */,
+ 2FE5DC4B29EDD7FA004B9AB4 /* TemplateApplicationTaskContext.swift */,
+ 27FA298F2A388E9B009CAC45 /* ModalView.swift */,
+ );
+ path = Schedule;
+ sourceTree = "";
+ };
+ 2FE5DC3C29EDD7DA004B9AB4 /* SharedContext */ = {
+ isa = PBXGroup;
+ children = (
+ 2FE5DC3E29EDD7ED004B9AB4 /* FeatureFlags.swift */,
+ 2FE5DC3F29EDD7EE004B9AB4 /* StorageKeys.swift */,
+ );
+ path = SharedContext;
+ sourceTree = "";
+ };
+ 2FE5DC3D29EDD7E4004B9AB4 /* Helper */ = {
+ isa = PBXGroup;
+ children = (
+ 2FE5DC4229EDD7F2004B9AB4 /* Binding+Negate.swift */,
+ 2FE5DC4329EDD7F2004B9AB4 /* Bundle+Image.swift */,
+ 2FE5DC4429EDD7F2004B9AB4 /* CodableArray+RawRepresentable.swift */,
+ );
+ path = Helper;
+ sourceTree = "";
+ };
+ 56F6F29E2AB441640022FE5A /* Contributions */ = {
+ isa = PBXGroup;
+ children = (
+ 56F6F29F2AB441930022FE5A /* ContributionsList.swift */,
+ 5680DD382AB8983D004E6D4A /* PackageCell.swift */,
+ 566155282AB8447C00209B80 /* Package+LicenseType.swift */,
+ 5661552D2AB854C000209B80 /* PackageHelper.swift */,
+ );
+ path = Contributions;
+ sourceTree = "";
+ };
+ 653A2544283387FE005D4D48 = {
+ isa = PBXGroup;
+ children = (
+ 2FC94CD4298B0A1D009C8209 /* TemplateApplication.xctestplan */,
+ 653A254F283387FE005D4D48 /* TemplateApplication */,
+ 653A256028338800005D4D48 /* TemplateApplicationTests */,
+ 653A256A28338800005D4D48 /* TemplateApplicationUITests */,
+ 653A254E283387FE005D4D48 /* Products */,
+ 653A258B283395A7005D4D48 /* Frameworks */,
+ );
+ sourceTree = "";
+ };
+ 653A254E283387FE005D4D48 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 653A254D283387FE005D4D48 /* TemplateApplication.app */,
+ 653A255D28338800005D4D48 /* TemplateApplicationTests.xctest */,
+ 653A256728338800005D4D48 /* TemplateApplicationUITests.xctest */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 653A254F283387FE005D4D48 /* TemplateApplication */ = {
+ isa = PBXGroup;
+ children = (
+ 653A2550283387FE005D4D48 /* TemplateApplication.swift */,
+ 2F5E32BC297E05EA003432F8 /* TemplateApplicationDelegate.swift */,
+ 2FF53D8C2A8729D600042B76 /* TemplateApplicationStandard.swift */,
+ 2F4E23822989D51F0013F3D9 /* TemplateApplicationTestingSetup.swift */,
+ 2FC975A72978F11A00BA99FE /* Home.swift */,
+ A9720E412ABB68B300872D23 /* Account */,
+ 2FE5DC2829EDD398004B9AB4 /* Onboarding */,
+ 2FE5DC3B29EDD7D0004B9AB4 /* Schedule */,
+ 2FE5DC2729EDD38D004B9AB4 /* Contacts */,
+ 56F6F29E2AB441640022FE5A /* Contributions */,
+ 2F4FC8D529EE69BE00BFFE26 /* MockUpload */,
+ 2FE5DC3C29EDD7DA004B9AB4 /* SharedContext */,
+ 2FE5DC3D29EDD7E4004B9AB4 /* Helper */,
+ 2FE5DC2D29EDD792004B9AB4 /* Resources */,
+ 2FC9759D2978E30800BA99FE /* Supporting Files */,
+ );
+ path = TemplateApplication;
+ sourceTree = "";
+ };
+ 653A256028338800005D4D48 /* TemplateApplicationTests */ = {
+ isa = PBXGroup;
+ children = (
+ 653A256128338800005D4D48 /* TemplateApplicationTests.swift */,
+ );
+ path = TemplateApplicationTests;
+ sourceTree = "";
+ };
+ 653A256A28338800005D4D48 /* TemplateApplicationUITests */ = {
+ isa = PBXGroup;
+ children = (
+ 2F4E237D2989A2FE0013F3D9 /* OnboardingTests.swift */,
+ 653A256B28338800005D4D48 /* SchedulerTests.swift */,
+ 2F4E23862989DB360013F3D9 /* ContactsTests.swift */,
+ 2F1B52CD2A4F5CCE003AE151 /* MockUploadTests.swift */,
+ 5680DD3D2AB8CD84004E6D4A /* ContributionsTest.swift */,
+ );
+ path = TemplateApplicationUITests;
+ sourceTree = "";
+ };
+ 653A258B283395A7005D4D48 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+ A9720E412ABB68B300872D23 /* Account */ = {
+ isa = PBXGroup;
+ children = (
+ A9FE7ACF2AA39BAB0077B045 /* AccountSheet.swift */,
+ A9720E422ABB68CC00872D23 /* AccountSetupHeader.swift */,
+ A9DFE8A82ABE551400428242 /* AccountButton.swift */,
+ );
+ path = Account;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 653A254C283387FE005D4D48 /* TemplateApplication */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 653A257128338800005D4D48 /* Build configuration list for PBXNativeTarget "TemplateApplication" */;
+ buildPhases = (
+ 653A2549283387FE005D4D48 /* Sources */,
+ 653A254A283387FE005D4D48 /* Frameworks */,
+ 653A254B283387FE005D4D48 /* Resources */,
+ 2F5B528D29BD237B002020B7 /* ShellScript */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 566155222AB83CF200209B80 /* PBXTargetDependency */,
+ );
+ name = TemplateApplication;
+ packageProductDependencies = (
+ 2F49B7752980407B00BCB272 /* Spezi */,
+ 2FE5DC6329EDD883004B9AB4 /* SpeziAccount */,
+ 2FE5DC6629EDD894004B9AB4 /* SpeziContact */,
+ 2FE5DC7129EDD8D3004B9AB4 /* SpeziHealthKit */,
+ 2FE5DC7429EDD8E6004B9AB4 /* SpeziFirebaseAccount */,
+ 2FE5DC7629EDD8E6004B9AB4 /* SpeziFirebaseConfiguration */,
+ 2FE5DC7829EDD8E6004B9AB4 /* SpeziFirestore */,
+ 2FE5DC8329EDD934004B9AB4 /* SpeziQuestionnaire */,
+ 2FE5DC8929EDD972004B9AB4 /* SpeziLocalStorage */,
+ 2FE5DC8B29EDD972004B9AB4 /* SpeziSecureStorage */,
+ 2FE5DC8E29EDD980004B9AB4 /* SpeziViews */,
+ 2FBD738B2A3BD150004228E7 /* SpeziScheduler */,
+ 2F3D4ABB2A4E7C290068FB2F /* SpeziScheduler */,
+ 2FE5DC8029EDD91D004B9AB4 /* SpeziOnboarding */,
+ 2FF53D8A2A8725DE00042B76 /* SpeziMockWebService */,
+ 2FB099AE2A875DF100B20952 /* FirebaseAuth */,
+ 2FB099B02A875DF100B20952 /* FirebaseFirestore */,
+ 2FB099B22A875DF100B20952 /* FirebaseFirestoreSwift */,
+ 2FB099B52A875E2B00B20952 /* HealthKitOnFHIR */,
+ 5661551C2AB8384200209B80 /* SwiftPackageList */,
+ 9739A0C52AD7B5730084BEA5 /* FirebaseStorage */,
+ 97D73D692AD860AD00B47FA0 /* SpeziFirebaseStorage */,
+ A9D83F952B083794000D0C78 /* SpeziFirebaseAccountStorage */,
+ );
+ productName = TemplateApplication;
+ productReference = 653A254D283387FE005D4D48 /* TemplateApplication.app */;
+ productType = "com.apple.product-type.application";
+ };
+ 653A255C28338800005D4D48 /* TemplateApplicationTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 653A257428338800005D4D48 /* Build configuration list for PBXNativeTarget "TemplateApplicationTests" */;
+ buildPhases = (
+ 653A255928338800005D4D48 /* Sources */,
+ 653A255A28338800005D4D48 /* Frameworks */,
+ 653A255B28338800005D4D48 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 653A255F28338800005D4D48 /* PBXTargetDependency */,
+ );
+ name = TemplateApplicationTests;
+ productName = TemplateApplicationTests;
+ productReference = 653A255D28338800005D4D48 /* TemplateApplicationTests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
+ 653A256628338800005D4D48 /* TemplateApplicationUITests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 653A257728338800005D4D48 /* Build configuration list for PBXNativeTarget "TemplateApplicationUITests" */;
+ buildPhases = (
+ 653A256328338800005D4D48 /* Sources */,
+ 653A256428338800005D4D48 /* Frameworks */,
+ 653A256528338800005D4D48 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 653A256928338800005D4D48 /* PBXTargetDependency */,
+ );
+ name = TemplateApplicationUITests;
+ packageProductDependencies = (
+ 2FE5DC9829EDD9D9004B9AB4 /* XCTestExtensions */,
+ 2FE5DC9B29EDD9EF004B9AB4 /* XCTHealthKit */,
+ );
+ productName = TemplateApplicationUITests;
+ productReference = 653A256728338800005D4D48 /* TemplateApplicationUITests.xctest */;
+ productType = "com.apple.product-type.bundle.ui-testing";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 653A2545283387FE005D4D48 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ BuildIndependentTargetsInParallel = 1;
+ LastSwiftUpdateCheck = 1340;
+ LastUpgradeCheck = 1520;
+ TargetAttributes = {
+ 653A254C283387FE005D4D48 = {
+ CreatedOnToolsVersion = 13.4;
+ };
+ 653A255C28338800005D4D48 = {
+ CreatedOnToolsVersion = 13.4;
+ TestTargetID = 653A254C283387FE005D4D48;
+ };
+ 653A256628338800005D4D48 = {
+ CreatedOnToolsVersion = 13.4;
+ TestTargetID = 653A254C283387FE005D4D48;
+ };
+ };
+ };
+ buildConfigurationList = 653A2548283387FE005D4D48 /* Build configuration list for PBXProject "TemplateApplication" */;
+ compatibilityVersion = "Xcode 13.0";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 653A2544283387FE005D4D48;
+ packageReferences = (
+ 2F49B7742980407B00BCB272 /* XCRemoteSwiftPackageReference "Spezi" */,
+ 2FE5DC6229EDD883004B9AB4 /* XCRemoteSwiftPackageReference "SpeziAccount" */,
+ 2FE5DC6529EDD894004B9AB4 /* XCRemoteSwiftPackageReference "SpeziContact" */,
+ 2FE5DC7029EDD8D3004B9AB4 /* XCRemoteSwiftPackageReference "SpeziHealthKit" */,
+ 2FE5DC7329EDD8E6004B9AB4 /* XCRemoteSwiftPackageReference "SpeziFirebase" */,
+ 2FE5DC8229EDD934004B9AB4 /* XCRemoteSwiftPackageReference "SpeziQuestionnaire" */,
+ 2FE5DC8829EDD972004B9AB4 /* XCRemoteSwiftPackageReference "SpeziStorage" */,
+ 2FE5DC8D29EDD980004B9AB4 /* XCRemoteSwiftPackageReference "SpeziViews" */,
+ 2FE5DC9029EDD9C3004B9AB4 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */,
+ 2FE5DC9729EDD9D9004B9AB4 /* XCRemoteSwiftPackageReference "XCTestExtensions" */,
+ 2FE5DC9A29EDD9EF004B9AB4 /* XCRemoteSwiftPackageReference "XCTHealthKit" */,
+ 2F3D4ABA2A4E7C290068FB2F /* XCRemoteSwiftPackageReference "SpeziScheduler" */,
+ 97F466E62A76BBEE005DC9B4 /* XCRemoteSwiftPackageReference "SpeziOnboarding" */,
+ 2FE750CA2A87240100723EAE /* XCRemoteSwiftPackageReference "SpeziMockWebService" */,
+ 2FB099B42A875E2B00B20952 /* XCRemoteSwiftPackageReference "HealthKitOnFHIR" */,
+ 5661551B2AB8384200209B80 /* XCRemoteSwiftPackageReference "swift-package-list" */,
+ );
+ productRefGroup = 653A254E283387FE005D4D48 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 653A254C283387FE005D4D48 /* TemplateApplication */,
+ 653A255C28338800005D4D48 /* TemplateApplicationTests */,
+ 653A256628338800005D4D48 /* TemplateApplicationUITests */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 653A254B283387FE005D4D48 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 2FC3439229EE634B002D773C /* ConsentDocument.md in Resources */,
+ 2FC3439129EE6349002D773C /* AppIcon.png in Resources */,
+ 653A255528338800005D4D48 /* Assets.xcassets in Resources */,
+ 2FC3439029EE6346002D773C /* SocialSupportQuestionnaire.json in Resources */,
+ 2FA0BFED2ACC977500E0EF83 /* Localizable.xcstrings in Resources */,
+ 2F6025CB29BBE70F0045459E /* GoogleService-Info.plist in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 653A255B28338800005D4D48 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 653A256528338800005D4D48 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 2F5B528D29BD237B002020B7 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ );
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "if [ \"${CONFIGURATION}\" = \"Debug\" ]; then\n export PATH=\"$PATH:/opt/homebrew/bin\"\n if which swiftlint > /dev/null; then\n swiftlint\n else\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\n fi\nfi\n";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 653A2549283387FE005D4D48 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 2FE5DC4129EDD7EE004B9AB4 /* StorageKeys.swift in Sources */,
+ 2FE5DCB129EE6107004B9AB4 /* AccountOnboarding.swift in Sources */,
+ 2F4FC8D729EE69D300BFFE26 /* MockUpload.swift in Sources */,
+ 2FE5DC3A29EDD7CA004B9AB4 /* Welcome.swift in Sources */,
+ 2FE5DC3829EDD7CA004B9AB4 /* InterestingModules.swift in Sources */,
+ 2FE5DC3529EDD7CA004B9AB4 /* Consent.swift in Sources */,
+ 2FE5DC4529EDD7F2004B9AB4 /* Binding+Negate.swift in Sources */,
+ 2FC975A82978F11A00BA99FE /* Home.swift in Sources */,
+ 2FE5DC4E29EDD7FA004B9AB4 /* ScheduleView.swift in Sources */,
+ A9DFE8A92ABE551400428242 /* AccountButton.swift in Sources */,
+ 2FE5DC3729EDD7CA004B9AB4 /* OnboardingFlow.swift in Sources */,
+ 2F1AC9DF2B4E840E00C24973 /* TemplateApplication.docc in Sources */,
+ 2FF53D8D2A8729D600042B76 /* TemplateApplicationStandard.swift in Sources */,
+ 2FE5DC4729EDD7F2004B9AB4 /* CodableArray+RawRepresentable.swift in Sources */,
+ A9720E432ABB68CC00872D23 /* AccountSetupHeader.swift in Sources */,
+ 2FE5DC4029EDD7EE004B9AB4 /* FeatureFlags.swift in Sources */,
+ 2FE5DC4629EDD7F2004B9AB4 /* Bundle+Image.swift in Sources */,
+ 2FE5DC4F29EDD7FA004B9AB4 /* EventContext.swift in Sources */,
+ 2FE5DC5029EDD7FA004B9AB4 /* EventContextView.swift in Sources */,
+ 2F4E23832989D51F0013F3D9 /* TemplateApplicationTestingSetup.swift in Sources */,
+ 2FE5DC5329EDD7FA004B9AB4 /* Bundle+Questionnaire.swift in Sources */,
+ 2FE5DC5129EDD7FA004B9AB4 /* TemplateApplicationTaskContext.swift in Sources */,
+ 56F6F2A02AB441930022FE5A /* ContributionsList.swift in Sources */,
+ 566155292AB8447C00209B80 /* Package+LicenseType.swift in Sources */,
+ 5680DD392AB8983D004E6D4A /* PackageCell.swift in Sources */,
+ 2F5E32BD297E05EA003432F8 /* TemplateApplicationDelegate.swift in Sources */,
+ 2FE5DC5229EDD7FA004B9AB4 /* TemplateApplicationScheduler.swift in Sources */,
+ A9FE7AD02AA39BAB0077B045 /* AccountSheet.swift in Sources */,
+ 653A2551283387FE005D4D48 /* TemplateApplication.swift in Sources */,
+ 2FE5DC3629EDD7CA004B9AB4 /* HealthKitPermissions.swift in Sources */,
+ 2F65B44E2A3B8B0600A36932 /* NotificationPermissions.swift in Sources */,
+ 5661552E2AB854C000209B80 /* PackageHelper.swift in Sources */,
+ 27FA29902A388E9B009CAC45 /* ModalView.swift in Sources */,
+ 2FE5DC2629EDD38A004B9AB4 /* Contacts.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 653A255928338800005D4D48 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 653A256228338800005D4D48 /* TemplateApplicationTests.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 653A256328338800005D4D48 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 5680DD3E2AB8CD84004E6D4A /* ContributionsTest.swift in Sources */,
+ 2F4E23872989DB360013F3D9 /* ContactsTests.swift in Sources */,
+ 2F4E237E2989A2FE0013F3D9 /* OnboardingTests.swift in Sources */,
+ 2F1B52CE2A4F5CCE003AE151 /* MockUploadTests.swift in Sources */,
+ 653A256C28338800005D4D48 /* SchedulerTests.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 566155222AB83CF200209B80 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ productRef = 566155212AB83CF200209B80 /* SwiftPackageListJSONPlugin */;
+ };
+ 653A255F28338800005D4D48 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 653A254C283387FE005D4D48 /* TemplateApplication */;
+ targetProxy = 653A255E28338800005D4D48 /* PBXContainerItemProxy */;
+ };
+ 653A256928338800005D4D48 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 653A254C283387FE005D4D48 /* TemplateApplication */;
+ targetProxy = 653A256828338800005D4D48 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+ 2FEE10302998C89C000822E1 /* Test */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
+ 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;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ 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.0;
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = TEST;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Test;
+ };
+ 2FEE10312998C89C000822E1 /* Test */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_ENTITLEMENTS = "TemplateApplication/Supporting Files/TemplateApplication.entitlements";
+ CODE_SIGN_IDENTITY = "Apple Development";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_ASSET_PATHS = "";
+ DEVELOPMENT_TEAM = "";
+ ENABLE_PREVIEWS = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = "TemplateApplication/Supporting Files/Info.plist";
+ INFOPLIST_KEY_NSCameraUsageDescription = "This message should never appear. Please adjust this when you start using camera information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect.";
+ INFOPLIST_KEY_NSHealthShareUsageDescription = "The Spezi Template Application uses the step count to demonstrate Spezi's integration with HealthKit.";
+ INFOPLIST_KEY_NSHealthUpdateUsageDescription = "The Spezi Template Application uses the step count to demonstrate Spezi's integration with HealthKit.";
+ INFOPLIST_KEY_NSLocationAlwaysAndWhenInUseUsageDescription = "This message should never appear. Please adjust this when you start using location information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect.";
+ INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "This message should never appear. Please adjust this when you start using location information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect.";
+ INFOPLIST_KEY_NSMicrophoneUsageDescription = "This message should never appear. Please adjust this when you start using microphone information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect.";
+ INFOPLIST_KEY_NSMotionUsageDescription = "This message should never appear. Please adjust this when you start using motion information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect.";
+ INFOPLIST_KEY_NSSpeechRecognitionUsageDescription = "This message should never appear. Please adjust this when you start using speecg information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect.";
+ INFOPLIST_KEY_UILaunchScreen_Generation = YES;
+ INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
+ INFOPLIST_KEY_UITemplateApplicationlicationSceneManifest_Generation = YES;
+ INFOPLIST_KEY_UITemplateApplicationlicationSupportsIndirectInputEvents = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = edu.stanford.spezi.templateapplication;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
+ SUPPORTS_MACCATALYST = NO;
+ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
+ "SWIFT_ELicenseRef-TemplateApplication_LOC_STRINGS" = YES;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = 1;
+ };
+ name = Test;
+ };
+ 2FEE10322998C89C000822E1 /* Test */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = 637867499T;
+ GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = edu.stanford.spezi.templateapplication.tests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ "SWIFT_ELicenseRef-TemplateApplication_LOC_STRINGS" = NO;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TemplateApplication.app/TemplateApplication";
+ };
+ name = Test;
+ };
+ 2FEE10332998C89C000822E1 /* Test */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = 637867499T;
+ GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = edu.stanford.spezi.templateapplicationuitests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ "SWIFT_ELicenseRef-TemplateApplication_LOC_STRINGS" = NO;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ TEST_TARGET_NAME = TemplateApplication;
+ };
+ name = Test;
+ };
+ 653A256F28338800005D4D48 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
+ 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;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ 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.0;
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Debug;
+ };
+ 653A257028338800005D4D48 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
+ 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;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ 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.0;
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ SDKROOT = iphoneos;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 653A257228338800005D4D48 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_ENTITLEMENTS = "TemplateApplication/Supporting Files/TemplateApplication.entitlements";
+ CODE_SIGN_IDENTITY = "Apple Development";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_ASSET_PATHS = "";
+ DEVELOPMENT_TEAM = "";
+ ENABLE_PREVIEWS = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = "TemplateApplication/Supporting Files/Info.plist";
+ INFOPLIST_KEY_NSCameraUsageDescription = "This message should never appear. Please adjust this when you start using camera information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect.";
+ INFOPLIST_KEY_NSHealthShareUsageDescription = "The Spezi Template Application uses the step count to demonstrate Spezi's integration with HealthKit.";
+ INFOPLIST_KEY_NSHealthUpdateUsageDescription = "The Spezi Template Application uses the step count to demonstrate Spezi's integration with HealthKit.";
+ INFOPLIST_KEY_NSLocationAlwaysAndWhenInUseUsageDescription = "This message should never appear. Please adjust this when you start using location information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect.";
+ INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "This message should never appear. Please adjust this when you start using location information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect.";
+ INFOPLIST_KEY_NSMicrophoneUsageDescription = "This message should never appear. Please adjust this when you start using microphone information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect.";
+ INFOPLIST_KEY_NSMotionUsageDescription = "This message should never appear. Please adjust this when you start using motion information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect.";
+ INFOPLIST_KEY_NSSpeechRecognitionUsageDescription = "This message should never appear. Please adjust this when you start using speecg information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect.";
+ INFOPLIST_KEY_UILaunchScreen_Generation = YES;
+ INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
+ INFOPLIST_KEY_UITemplateApplicationlicationSceneManifest_Generation = YES;
+ INFOPLIST_KEY_UITemplateApplicationlicationSupportsIndirectInputEvents = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = edu.stanford.spezi.templateapplication;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
+ SUPPORTS_MACCATALYST = NO;
+ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
+ "SWIFT_ELicenseRef-TemplateApplication_LOC_STRINGS" = YES;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = 1;
+ };
+ name = Debug;
+ };
+ 653A257328338800005D4D48 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_ENTITLEMENTS = "TemplateApplication/Supporting Files/TemplateApplication.entitlements";
+ CODE_SIGN_IDENTITY = "Apple Development";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
+ CODE_SIGN_STYLE = Manual;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_ASSET_PATHS = "";
+ DEVELOPMENT_TEAM = "";
+ "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 637867499T;
+ ENABLE_PREVIEWS = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = "TemplateApplication/Supporting Files/Info.plist";
+ INFOPLIST_KEY_NSCameraUsageDescription = "This message should never appear. Please adjust this when you start using camera information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect.";
+ INFOPLIST_KEY_NSHealthShareUsageDescription = "The Spezi Template Application uses the step count to demonstrate Spezi's integration with HealthKit.";
+ INFOPLIST_KEY_NSHealthUpdateUsageDescription = "The Spezi Template Application uses the step count to demonstrate Spezi's integration with HealthKit.";
+ INFOPLIST_KEY_NSLocationAlwaysAndWhenInUseUsageDescription = "This message should never appear. Please adjust this when you start using location information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect.";
+ INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "This message should never appear. Please adjust this when you start using location information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect.";
+ INFOPLIST_KEY_NSMicrophoneUsageDescription = "This message should never appear. Please adjust this when you start using microphone information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect.";
+ INFOPLIST_KEY_NSMotionUsageDescription = "This message should never appear. Please adjust this when you start using motion information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect.";
+ INFOPLIST_KEY_NSSpeechRecognitionUsageDescription = "This message should never appear. Please adjust this when you start using speecg information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect.";
+ INFOPLIST_KEY_UILaunchScreen_Generation = YES;
+ INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
+ INFOPLIST_KEY_UITemplateApplicationlicationSceneManifest_Generation = YES;
+ INFOPLIST_KEY_UITemplateApplicationlicationSupportsIndirectInputEvents = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = edu.stanford.spezi.templateapplication;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Spezi Template Application";
+ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
+ SUPPORTS_MACCATALYST = NO;
+ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
+ "SWIFT_ELicenseRef-TemplateApplication_LOC_STRINGS" = YES;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = 1;
+ };
+ name = Release;
+ };
+ 653A257528338800005D4D48 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = 637867499T;
+ GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = edu.stanford.spezi.templateapplication.tests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ "SWIFT_ELicenseRef-TemplateApplication_LOC_STRINGS" = NO;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TemplateApplication.app/TemplateApplication";
+ };
+ name = Debug;
+ };
+ 653A257628338800005D4D48 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = 637867499T;
+ GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = edu.stanford.spezi.templateapplication.tests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ "SWIFT_ELicenseRef-TemplateApplication_LOC_STRINGS" = NO;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TemplateApplication.app/TemplateApplication";
+ };
+ name = Release;
+ };
+ 653A257828338800005D4D48 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = 637867499T;
+ GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = edu.stanford.spezi.templateapplicationuitests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ "SWIFT_ELicenseRef-TemplateApplication_LOC_STRINGS" = NO;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ TEST_TARGET_NAME = TemplateApplication;
+ };
+ name = Debug;
+ };
+ 653A257928338800005D4D48 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = 637867499T;
+ GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = edu.stanford.spezi.templateapplicationuitests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ "SWIFT_ELicenseRef-TemplateApplication_LOC_STRINGS" = NO;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ TEST_TARGET_NAME = TemplateApplication;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 653A2548283387FE005D4D48 /* Build configuration list for PBXProject "TemplateApplication" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 653A256F28338800005D4D48 /* Debug */,
+ 2FEE10302998C89C000822E1 /* Test */,
+ 653A257028338800005D4D48 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 653A257128338800005D4D48 /* Build configuration list for PBXNativeTarget "TemplateApplication" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 653A257228338800005D4D48 /* Debug */,
+ 2FEE10312998C89C000822E1 /* Test */,
+ 653A257328338800005D4D48 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 653A257428338800005D4D48 /* Build configuration list for PBXNativeTarget "TemplateApplicationTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 653A257528338800005D4D48 /* Debug */,
+ 2FEE10322998C89C000822E1 /* Test */,
+ 653A257628338800005D4D48 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 653A257728338800005D4D48 /* Build configuration list for PBXNativeTarget "TemplateApplicationUITests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 653A257828338800005D4D48 /* Debug */,
+ 2FEE10332998C89C000822E1 /* Test */,
+ 653A257928338800005D4D48 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+
+/* Begin XCRemoteSwiftPackageReference section */
+ 2F3D4ABA2A4E7C290068FB2F /* XCRemoteSwiftPackageReference "SpeziScheduler" */ = {
+ isa = XCRemoteSwiftPackageReference;
+ repositoryURL = "https://github.com/StanfordSpezi/SpeziScheduler.git";
+ requirement = {
+ kind = upToNextMinorVersion;
+ minimumVersion = 0.8.0;
+ };
+ };
+ 2F49B7742980407B00BCB272 /* XCRemoteSwiftPackageReference "Spezi" */ = {
+ isa = XCRemoteSwiftPackageReference;
+ repositoryURL = "https://github.com/StanfordSpezi/Spezi";
+ requirement = {
+ kind = upToNextMajorVersion;
+ minimumVersion = 1.1.0;
+ };
+ };
+ 2FB099B42A875E2B00B20952 /* XCRemoteSwiftPackageReference "HealthKitOnFHIR" */ = {
+ isa = XCRemoteSwiftPackageReference;
+ repositoryURL = "https://github.com/StanfordBDHG/HealthKitOnFHIR.git";
+ requirement = {
+ kind = upToNextMinorVersion;
+ minimumVersion = 0.2.4;
+ };
+ };
+ 2FE5DC6229EDD883004B9AB4 /* XCRemoteSwiftPackageReference "SpeziAccount" */ = {
+ isa = XCRemoteSwiftPackageReference;
+ repositoryURL = "https://github.com/StanfordSpezi/SpeziAccount.git";
+ requirement = {
+ kind = upToNextMajorVersion;
+ minimumVersion = 1.1.0;
+ };
+ };
+ 2FE5DC6529EDD894004B9AB4 /* XCRemoteSwiftPackageReference "SpeziContact" */ = {
+ isa = XCRemoteSwiftPackageReference;
+ repositoryURL = "https://github.com/StanfordSpezi/SpeziContact.git";
+ requirement = {
+ kind = upToNextMajorVersion;
+ minimumVersion = 1.0.0;
+ };
+ };
+ 2FE5DC7029EDD8D3004B9AB4 /* XCRemoteSwiftPackageReference "SpeziHealthKit" */ = {
+ isa = XCRemoteSwiftPackageReference;
+ repositoryURL = "https://github.com/StanfordSpezi/SpeziHealthKit.git";
+ requirement = {
+ kind = upToNextMinorVersion;
+ minimumVersion = 0.5.0;
+ };
+ };
+ 2FE5DC7329EDD8E6004B9AB4 /* XCRemoteSwiftPackageReference "SpeziFirebase" */ = {
+ isa = XCRemoteSwiftPackageReference;
+ repositoryURL = "https://github.com/StanfordSpezi/SpeziFirebase.git";
+ requirement = {
+ kind = upToNextMajorVersion;
+ minimumVersion = 1.0.0;
+ };
+ };
+ 2FE5DC8229EDD934004B9AB4 /* XCRemoteSwiftPackageReference "SpeziQuestionnaire" */ = {
+ isa = XCRemoteSwiftPackageReference;
+ repositoryURL = "https://github.com/StanfordSpezi/SpeziQuestionnaire.git";
+ requirement = {
+ kind = upToNextMajorVersion;
+ minimumVersion = 1.0.0;
+ };
+ };
+ 2FE5DC8829EDD972004B9AB4 /* XCRemoteSwiftPackageReference "SpeziStorage" */ = {
+ isa = XCRemoteSwiftPackageReference;
+ repositoryURL = "https://github.com/StanfordSpezi/SpeziStorage.git";
+ requirement = {
+ kind = upToNextMajorVersion;
+ minimumVersion = 1.0.0;
+ };
+ };
+ 2FE5DC8D29EDD980004B9AB4 /* XCRemoteSwiftPackageReference "SpeziViews" */ = {
+ isa = XCRemoteSwiftPackageReference;
+ repositoryURL = "https://github.com/StanfordSpezi/SpeziViews.git";
+ requirement = {
+ kind = upToNextMajorVersion;
+ minimumVersion = 1.0.0;
+ };
+ };
+ 2FE5DC9029EDD9C3004B9AB4 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = {
+ isa = XCRemoteSwiftPackageReference;
+ repositoryURL = "https://github.com/firebase/firebase-ios-sdk.git";
+ requirement = {
+ kind = upToNextMajorVersion;
+ minimumVersion = 10.17.0;
+ };
+ };
+ 2FE5DC9729EDD9D9004B9AB4 /* XCRemoteSwiftPackageReference "XCTestExtensions" */ = {
+ isa = XCRemoteSwiftPackageReference;
+ repositoryURL = "https://github.com/StanfordBDHG/XCTestExtensions.git";
+ requirement = {
+ kind = upToNextMinorVersion;
+ minimumVersion = 0.4.7;
+ };
+ };
+ 2FE5DC9A29EDD9EF004B9AB4 /* XCRemoteSwiftPackageReference "XCTHealthKit" */ = {
+ isa = XCRemoteSwiftPackageReference;
+ repositoryURL = "https://github.com/StanfordBDHG/XCTHealthKit.git";
+ requirement = {
+ kind = upToNextMinorVersion;
+ minimumVersion = 0.3.5;
+ };
+ };
+ 2FE750CA2A87240100723EAE /* XCRemoteSwiftPackageReference "SpeziMockWebService" */ = {
+ isa = XCRemoteSwiftPackageReference;
+ repositoryURL = "https://github.com/StanfordSpezi/SpeziMockWebService.git";
+ requirement = {
+ kind = upToNextMajorVersion;
+ minimumVersion = 1.0.0;
+ };
+ };
+ 5661551B2AB8384200209B80 /* XCRemoteSwiftPackageReference "swift-package-list" */ = {
+ isa = XCRemoteSwiftPackageReference;
+ repositoryURL = "https://github.com/FelixHerrmann/swift-package-list";
+ requirement = {
+ kind = upToNextMajorVersion;
+ minimumVersion = 3.0.10;
+ };
+ };
+ 97F466E62A76BBEE005DC9B4 /* XCRemoteSwiftPackageReference "SpeziOnboarding" */ = {
+ isa = XCRemoteSwiftPackageReference;
+ repositoryURL = "https://github.com/StanfordSpezi/SpeziOnboarding";
+ requirement = {
+ kind = upToNextMajorVersion;
+ minimumVersion = 1.0.0;
+ };
+ };
+/* End XCRemoteSwiftPackageReference section */
+
+/* Begin XCSwiftPackageProductDependency section */
+ 2F3D4ABB2A4E7C290068FB2F /* SpeziScheduler */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 2F3D4ABA2A4E7C290068FB2F /* XCRemoteSwiftPackageReference "SpeziScheduler" */;
+ productName = SpeziScheduler;
+ };
+ 2F49B7752980407B00BCB272 /* Spezi */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 2F49B7742980407B00BCB272 /* XCRemoteSwiftPackageReference "Spezi" */;
+ productName = Spezi;
+ };
+ 2FB099AE2A875DF100B20952 /* FirebaseAuth */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 2FE5DC9029EDD9C3004B9AB4 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;
+ productName = FirebaseAuth;
+ };
+ 2FB099B02A875DF100B20952 /* FirebaseFirestore */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 2FE5DC9029EDD9C3004B9AB4 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;
+ productName = FirebaseFirestore;
+ };
+ 2FB099B22A875DF100B20952 /* FirebaseFirestoreSwift */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 2FE5DC9029EDD9C3004B9AB4 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;
+ productName = FirebaseFirestoreSwift;
+ };
+ 2FB099B52A875E2B00B20952 /* HealthKitOnFHIR */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 2FB099B42A875E2B00B20952 /* XCRemoteSwiftPackageReference "HealthKitOnFHIR" */;
+ productName = HealthKitOnFHIR;
+ };
+ 2FBD738B2A3BD150004228E7 /* SpeziScheduler */ = {
+ isa = XCSwiftPackageProductDependency;
+ productName = SpeziScheduler;
+ };
+ 2FE5DC6329EDD883004B9AB4 /* SpeziAccount */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 2FE5DC6229EDD883004B9AB4 /* XCRemoteSwiftPackageReference "SpeziAccount" */;
+ productName = SpeziAccount;
+ };
+ 2FE5DC6629EDD894004B9AB4 /* SpeziContact */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 2FE5DC6529EDD894004B9AB4 /* XCRemoteSwiftPackageReference "SpeziContact" */;
+ productName = SpeziContact;
+ };
+ 2FE5DC7129EDD8D3004B9AB4 /* SpeziHealthKit */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 2FE5DC7029EDD8D3004B9AB4 /* XCRemoteSwiftPackageReference "SpeziHealthKit" */;
+ productName = SpeziHealthKit;
+ };
+ 2FE5DC7429EDD8E6004B9AB4 /* SpeziFirebaseAccount */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 2FE5DC7329EDD8E6004B9AB4 /* XCRemoteSwiftPackageReference "SpeziFirebase" */;
+ productName = SpeziFirebaseAccount;
+ };
+ 2FE5DC7629EDD8E6004B9AB4 /* SpeziFirebaseConfiguration */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 2FE5DC7329EDD8E6004B9AB4 /* XCRemoteSwiftPackageReference "SpeziFirebase" */;
+ productName = SpeziFirebaseConfiguration;
+ };
+ 2FE5DC7829EDD8E6004B9AB4 /* SpeziFirestore */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 2FE5DC7329EDD8E6004B9AB4 /* XCRemoteSwiftPackageReference "SpeziFirebase" */;
+ productName = SpeziFirestore;
+ };
+ 2FE5DC8029EDD91D004B9AB4 /* SpeziOnboarding */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 97F466E62A76BBEE005DC9B4 /* XCRemoteSwiftPackageReference "SpeziOnboarding" */;
+ productName = SpeziOnboarding;
+ };
+ 2FE5DC8329EDD934004B9AB4 /* SpeziQuestionnaire */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 2FE5DC8229EDD934004B9AB4 /* XCRemoteSwiftPackageReference "SpeziQuestionnaire" */;
+ productName = SpeziQuestionnaire;
+ };
+ 2FE5DC8929EDD972004B9AB4 /* SpeziLocalStorage */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 2FE5DC8829EDD972004B9AB4 /* XCRemoteSwiftPackageReference "SpeziStorage" */;
+ productName = SpeziLocalStorage;
+ };
+ 2FE5DC8B29EDD972004B9AB4 /* SpeziSecureStorage */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 2FE5DC8829EDD972004B9AB4 /* XCRemoteSwiftPackageReference "SpeziStorage" */;
+ productName = SpeziSecureStorage;
+ };
+ 2FE5DC8E29EDD980004B9AB4 /* SpeziViews */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 2FE5DC8D29EDD980004B9AB4 /* XCRemoteSwiftPackageReference "SpeziViews" */;
+ productName = SpeziViews;
+ };
+ 2FE5DC9829EDD9D9004B9AB4 /* XCTestExtensions */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 2FE5DC9729EDD9D9004B9AB4 /* XCRemoteSwiftPackageReference "XCTestExtensions" */;
+ productName = XCTestExtensions;
+ };
+ 2FE5DC9B29EDD9EF004B9AB4 /* XCTHealthKit */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 2FE5DC9A29EDD9EF004B9AB4 /* XCRemoteSwiftPackageReference "XCTHealthKit" */;
+ productName = XCTHealthKit;
+ };
+ 2FF53D8A2A8725DE00042B76 /* SpeziMockWebService */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 2FE750CA2A87240100723EAE /* XCRemoteSwiftPackageReference "SpeziMockWebService" */;
+ productName = SpeziMockWebService;
+ };
+ 5661551C2AB8384200209B80 /* SwiftPackageList */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 5661551B2AB8384200209B80 /* XCRemoteSwiftPackageReference "swift-package-list" */;
+ productName = SwiftPackageList;
+ };
+ 566155212AB83CF200209B80 /* SwiftPackageListJSONPlugin */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 5661551B2AB8384200209B80 /* XCRemoteSwiftPackageReference "swift-package-list" */;
+ productName = "plugin:SwiftPackageListJSONPlugin";
+ };
+ 9739A0C52AD7B5730084BEA5 /* FirebaseStorage */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 2FE5DC9029EDD9C3004B9AB4 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;
+ productName = FirebaseStorage;
+ };
+ 97D73D692AD860AD00B47FA0 /* SpeziFirebaseStorage */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 2FE5DC7329EDD8E6004B9AB4 /* XCRemoteSwiftPackageReference "SpeziFirebase" */;
+ productName = SpeziFirebaseStorage;
+ };
+ A9D83F952B083794000D0C78 /* SpeziFirebaseAccountStorage */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 2FE5DC7329EDD8E6004B9AB4 /* XCRemoteSwiftPackageReference "SpeziFirebase" */;
+ productName = SpeziFirebaseAccountStorage;
+ };
+/* End XCSwiftPackageProductDependency section */
+ };
+ rootObject = 653A2545283387FE005D4D48 /* Project object */;
+}
diff --git a/TemplateApplication.xcodeproj/project.pbxproj.license b/TemplateApplication.xcodeproj/project.pbxproj.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication.xcodeproj/project.pbxproj.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/TemplateApplication.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..919434a
--- /dev/null
+++ b/TemplateApplication.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/TemplateApplication.xcodeproj/project.xcworkspace/contents.xcworkspacedata.license b/TemplateApplication.xcodeproj/project.xcworkspace/contents.xcworkspacedata.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication.xcodeproj/project.xcworkspace/contents.xcworkspacedata.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/TemplateApplication.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/TemplateApplication.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/TemplateApplication.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist.license b/TemplateApplication.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/TemplateApplication.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
new file mode 100644
index 0000000..c6a59ae
--- /dev/null
+++ b/TemplateApplication.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -0,0 +1,320 @@
+{
+ "pins" : [
+ {
+ "identity" : "abseil-cpp-binary",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/google/abseil-cpp-binary.git",
+ "state" : {
+ "revision" : "bfc0b6f81adc06ce5121eb23f628473638d67c5c",
+ "version" : "1.2022062300.0"
+ }
+ },
+ {
+ "identity" : "app-check",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/google/app-check.git",
+ "state" : {
+ "revision" : "5746b2d35c91c50581590ed97abe4c06b5037274",
+ "version" : "10.18.0"
+ }
+ },
+ {
+ "identity" : "fhirmodels",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/FHIRModels",
+ "state" : {
+ "revision" : "861afd5816a98d38f86220eab2f812d76cad84a0",
+ "version" : "0.5.0"
+ }
+ },
+ {
+ "identity" : "firebase-ios-sdk",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/firebase/firebase-ios-sdk.git",
+ "state" : {
+ "revision" : "c60c958e707c50a9cf8bcb7cfd7d51c566d726c5",
+ "version" : "10.19.1"
+ }
+ },
+ {
+ "identity" : "googleappmeasurement",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/google/GoogleAppMeasurement.git",
+ "state" : {
+ "revision" : "6b332152355c372ace9966d8ee76ed191f97025e",
+ "version" : "10.17.0"
+ }
+ },
+ {
+ "identity" : "googledatatransport",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/google/GoogleDataTransport.git",
+ "state" : {
+ "revision" : "a732a4b47f59e4f725a2ea10f0c77e93a7131117",
+ "version" : "9.3.0"
+ }
+ },
+ {
+ "identity" : "googleutilities",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/google/GoogleUtilities.git",
+ "state" : {
+ "revision" : "bc27fad73504f3d4af235de451f02ee22586ebd3",
+ "version" : "7.12.1"
+ }
+ },
+ {
+ "identity" : "grpc-binary",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/google/grpc-binary.git",
+ "state" : {
+ "revision" : "a673bc2937fbe886dd1f99c401b01b6d977a9c98",
+ "version" : "1.49.1"
+ }
+ },
+ {
+ "identity" : "gtm-session-fetcher",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/google/gtm-session-fetcher.git",
+ "state" : {
+ "revision" : "115f75e43851774934d695449a4836123c3246e1",
+ "version" : "3.2.0"
+ }
+ },
+ {
+ "identity" : "healthkitonfhir",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/StanfordBDHG/HealthKitOnFHIR.git",
+ "state" : {
+ "revision" : "825e96007d83ed83f81ee49eb3ebab29d7b7ba2f",
+ "version" : "0.2.5"
+ }
+ },
+ {
+ "identity" : "interop-ios-for-google-sdks",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/google/interop-ios-for-google-sdks.git",
+ "state" : {
+ "revision" : "2d12673670417654f08f5f90fdd62926dc3a2648",
+ "version" : "100.0.0"
+ }
+ },
+ {
+ "identity" : "leveldb",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/firebase/leveldb.git",
+ "state" : {
+ "revision" : "9d108e9112aa1d65ce508facf804674546116d9c",
+ "version" : "1.22.3"
+ }
+ },
+ {
+ "identity" : "nanopb",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/firebase/nanopb.git",
+ "state" : {
+ "revision" : "819d0a2173aff699fb8c364b6fb906f7cdb1a692",
+ "version" : "2.30909.0"
+ }
+ },
+ {
+ "identity" : "promises",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/google/promises.git",
+ "state" : {
+ "revision" : "e70e889c0196c76d22759eb50d6a0270ca9f1d9e",
+ "version" : "2.3.1"
+ }
+ },
+ {
+ "identity" : "researchkit",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/StanfordBDHG/ResearchKit",
+ "state" : {
+ "revision" : "cf79a15c7d8c436f98937fe93e72e880dd2f73e4",
+ "version" : "2.2.20"
+ }
+ },
+ {
+ "identity" : "researchkitonfhir",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/StanfordBDHG/ResearchKitOnFHIR",
+ "state" : {
+ "revision" : "7dc09f7acd7fb19673594e0fdd4d72d0869ee006",
+ "version" : "1.0.0"
+ }
+ },
+ {
+ "identity" : "spezi",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/StanfordSpezi/Spezi",
+ "state" : {
+ "revision" : "c4bf0e99de40acfdd2baf0fa02769f06a4c3f0eb",
+ "version" : "1.1.0"
+ }
+ },
+ {
+ "identity" : "speziaccount",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/StanfordSpezi/SpeziAccount.git",
+ "state" : {
+ "revision" : "714f01ae1e67bf9c1c0e7c07624380f9bea772b7",
+ "version" : "1.1.0"
+ }
+ },
+ {
+ "identity" : "spezicontact",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/StanfordSpezi/SpeziContact.git",
+ "state" : {
+ "revision" : "494b776f8c98d771e4a609a1fb706097dba4c030",
+ "version" : "1.0.0"
+ }
+ },
+ {
+ "identity" : "spezifirebase",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/StanfordSpezi/SpeziFirebase.git",
+ "state" : {
+ "revision" : "ca1edf678ec59e76c9869ee3448e6e165d9c2789",
+ "version" : "1.0.0"
+ }
+ },
+ {
+ "identity" : "spezifoundation",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/StanfordSpezi/SpeziFoundation.git",
+ "state" : {
+ "revision" : "683c66f922a4cfe0882c4a86a43854f613b48541",
+ "version" : "1.0.0"
+ }
+ },
+ {
+ "identity" : "spezihealthkit",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/StanfordSpezi/SpeziHealthKit.git",
+ "state" : {
+ "revision" : "d882734a4ed31fce1bffd7b9977e2669080f21de",
+ "version" : "0.5.0"
+ }
+ },
+ {
+ "identity" : "spezimockwebservice",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/StanfordSpezi/SpeziMockWebService.git",
+ "state" : {
+ "revision" : "b18067d3499e630bbd995ef05a296ef8fdd42528",
+ "version" : "1.0.0"
+ }
+ },
+ {
+ "identity" : "spezionboarding",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/StanfordSpezi/SpeziOnboarding",
+ "state" : {
+ "revision" : "ae7b18a18453557cd95c7adeb8f75846f48c343c",
+ "version" : "1.0.0"
+ }
+ },
+ {
+ "identity" : "speziquestionnaire",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/StanfordSpezi/SpeziQuestionnaire.git",
+ "state" : {
+ "revision" : "930a4099db1aca9db0b6ed4e77687141c4780052",
+ "version" : "1.0.0"
+ }
+ },
+ {
+ "identity" : "spezischeduler",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/StanfordSpezi/SpeziScheduler.git",
+ "state" : {
+ "revision" : "adf793cb47dc199f8ae88f5c719f4d3ba06a4c4e",
+ "version" : "0.8.0"
+ }
+ },
+ {
+ "identity" : "spezistorage",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/StanfordSpezi/SpeziStorage.git",
+ "state" : {
+ "revision" : "eaed2220375c35400aa69d1f96a8d32b7e66b1c7",
+ "version" : "1.0.0"
+ }
+ },
+ {
+ "identity" : "speziviews",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/StanfordSpezi/SpeziViews.git",
+ "state" : {
+ "revision" : "0137e69d156bf4001a8d6bf5661c9a37b2bbd0aa",
+ "version" : "1.0.0"
+ }
+ },
+ {
+ "identity" : "swift-argument-parser",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-argument-parser",
+ "state" : {
+ "revision" : "c8ed701b513cf5177118a175d85fbbbcd707ab41",
+ "version" : "1.3.0"
+ }
+ },
+ {
+ "identity" : "swift-collections",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-collections.git",
+ "state" : {
+ "revision" : "d029d9d39c87bed85b1c50adee7c41795261a192",
+ "version" : "1.0.6"
+ }
+ },
+ {
+ "identity" : "swift-package-list",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/FelixHerrmann/swift-package-list",
+ "state" : {
+ "revision" : "412180a72b9a1f8262213c16459e3533b0385ea5",
+ "version" : "3.1.0"
+ }
+ },
+ {
+ "identity" : "swift-protobuf",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-protobuf.git",
+ "state" : {
+ "revision" : "65e8f29b2d63c4e38e736b25c27b83e012159be8",
+ "version" : "1.25.2"
+ }
+ },
+ {
+ "identity" : "xctestextensions",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/StanfordBDHG/XCTestExtensions.git",
+ "state" : {
+ "revision" : "388a6d6a5be48eff5d98a2c45e0b50f30ed21dc3",
+ "version" : "0.4.7"
+ }
+ },
+ {
+ "identity" : "xcthealthkit",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/StanfordBDHG/XCTHealthKit.git",
+ "state" : {
+ "revision" : "6e9344a2d632b801d94fe3bbd1d891817e032103",
+ "version" : "0.3.5"
+ }
+ },
+ {
+ "identity" : "xctruntimeassertions",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/StanfordBDHG/XCTRuntimeAssertions",
+ "state" : {
+ "revision" : "bb2a287c2544aa846e53670d1ece35e5949567be",
+ "version" : "1.0.0"
+ }
+ }
+ ],
+ "version" : 2
+}
diff --git a/TemplateApplication.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved.license b/TemplateApplication.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication.xcodeproj/xcshareddata/xcschemes/TemplateApplication.xcscheme b/TemplateApplication.xcodeproj/xcshareddata/xcschemes/TemplateApplication.xcscheme
new file mode 100644
index 0000000..f5e6644
--- /dev/null
+++ b/TemplateApplication.xcodeproj/xcshareddata/xcschemes/TemplateApplication.xcscheme
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/TemplateApplication.xcodeproj/xcshareddata/xcschemes/TemplateApplication.xcscheme.license b/TemplateApplication.xcodeproj/xcshareddata/xcschemes/TemplateApplication.xcscheme.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication.xcodeproj/xcshareddata/xcschemes/TemplateApplication.xcscheme.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication.xctestplan b/TemplateApplication.xctestplan
new file mode 100644
index 0000000..5d65ec1
--- /dev/null
+++ b/TemplateApplication.xctestplan
@@ -0,0 +1,35 @@
+{
+ "configurations" : [
+ {
+ "id" : "6C9DABEF-5835-4523-A115-B83B0C6E3BBC",
+ "name" : "Default",
+ "options" : {
+
+ }
+ }
+ ],
+ "defaultOptions" : {
+ "targetForVariableExpansion" : {
+ "containerPath" : "container:TemplateApplication.xcodeproj",
+ "identifier" : "653A254C283387FE005D4D48",
+ "name" : "TemplateApplication"
+ }
+ },
+ "testTargets" : [
+ {
+ "target" : {
+ "containerPath" : "container:TemplateApplication.xcodeproj",
+ "identifier" : "653A255C28338800005D4D48",
+ "name" : "TemplateApplicationTests"
+ }
+ },
+ {
+ "target" : {
+ "containerPath" : "container:TemplateApplication.xcodeproj",
+ "identifier" : "653A256628338800005D4D48",
+ "name" : "TemplateApplicationUITests"
+ }
+ }
+ ],
+ "version" : 1
+}
diff --git a/TemplateApplication.xctestplan.license b/TemplateApplication.xctestplan.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication.xctestplan.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Account/AccountButton.swift b/TemplateApplication/Account/AccountButton.swift
new file mode 100644
index 0000000..ca53d00
--- /dev/null
+++ b/TemplateApplication/Account/AccountButton.swift
@@ -0,0 +1,38 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import SwiftUI
+
+
+struct AccountButton: View {
+ static let shouldDisplay = !FeatureFlags.disableFirebase || ProcessInfo.processInfo.isPreviewSimulator
+
+ @Binding private var isPresented: Bool
+
+
+ var body: some View {
+ Button(action: {
+ isPresented = true
+ }) {
+ Image(systemName: "person.crop.circle")
+ }
+ .accessibilityLabel("ACCOUNT_TITLE")
+ }
+
+
+ init(isPresented: Binding) {
+ self._isPresented = isPresented
+ }
+}
+
+
+#if DEBUG
+#Preview(traits: .sizeThatFitsLayout) {
+ AccountButton(isPresented: .constant(false))
+}
+#endif
diff --git a/TemplateApplication/Account/AccountSetupHeader.swift b/TemplateApplication/Account/AccountSetupHeader.swift
new file mode 100644
index 0000000..b89cf1c
--- /dev/null
+++ b/TemplateApplication/Account/AccountSetupHeader.swift
@@ -0,0 +1,43 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import SpeziAccount
+import SwiftUI
+
+
+struct AccountSetupHeader: View {
+ @Environment(Account.self) private var account
+ @Environment(\._accountSetupState) private var setupState
+
+
+ var body: some View {
+ VStack {
+ Text("ACCOUNT_TITLE")
+ .font(.largeTitle)
+ .bold()
+ .padding(.bottom)
+ .padding(.top, 30)
+ Text("ACCOUNT_SUBTITLE")
+ .padding(.bottom, 8)
+ if account.signedIn, case .generic = setupState {
+ Text("ACCOUNT_SIGNED_IN_DESCRIPTION")
+ } else {
+ Text("ACCOUNT_SETUP_DESCRIPTION")
+ }
+ }
+ .multilineTextAlignment(.center)
+ }
+}
+
+
+#if DEBUG
+#Preview {
+ AccountSetupHeader()
+ .environment(Account())
+}
+#endif
diff --git a/TemplateApplication/Account/AccountSheet.swift b/TemplateApplication/Account/AccountSheet.swift
new file mode 100644
index 0000000..f5bfaf1
--- /dev/null
+++ b/TemplateApplication/Account/AccountSheet.swift
@@ -0,0 +1,91 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import SpeziAccount
+import SwiftUI
+
+
+struct AccountSheet: View {
+ @Environment(\.dismiss) var dismiss
+
+ @Environment(Account.self) private var account
+ @Environment(\.accountRequired) var accountRequired
+
+ @State var isInSetup = false
+ @State var overviewIsEditing = false
+
+
+ var body: some View {
+ NavigationStack {
+ ZStack {
+ if account.signedIn && !isInSetup {
+ AccountOverview(isEditing: $overviewIsEditing) {
+ NavigationLink {
+ ContributionsList()
+ } label: {
+ Text("LICENSE_INFO_TITLE")
+ }
+ }
+ .onDisappear {
+ overviewIsEditing = false
+ }
+ .toolbar {
+ if !overviewIsEditing {
+ closeButton
+ }
+ }
+ } else {
+ AccountSetup { _ in
+ dismiss() // we just signed in, dismiss the account setup sheet
+ } header: {
+ AccountSetupHeader()
+ }
+ .onAppear {
+ isInSetup = true
+ }
+ .toolbar {
+ if !accountRequired {
+ closeButton
+ }
+ }
+ }
+ }
+ }
+ }
+
+ var closeButton: some ToolbarContent {
+ ToolbarItem(placement: .cancellationAction) {
+ Button("CLOSE") {
+ dismiss()
+ }
+ }
+ }
+}
+
+
+#if DEBUG
+#Preview("AccountSheet") {
+ let details = AccountDetails.Builder()
+ .set(\.userId, value: "lelandstanford@stanford.edu")
+ .set(\.name, value: PersonNameComponents(givenName: "Leland", familyName: "Stanford"))
+
+ return AccountSheet()
+ .previewWith {
+ AccountConfiguration(building: details, active: MockUserIdPasswordAccountService())
+ }
+}
+
+#Preview("AccountSheet SignIn") {
+ AccountSheet()
+ .previewWith {
+ AccountConfiguration {
+ MockUserIdPasswordAccountService()
+ }
+ }
+}
+#endif
diff --git a/TemplateApplication/Contacts/Contacts.swift b/TemplateApplication/Contacts/Contacts.swift
new file mode 100644
index 0000000..e9b5a75
--- /dev/null
+++ b/TemplateApplication/Contacts/Contacts.swift
@@ -0,0 +1,78 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import Foundation
+import SpeziContact
+import SwiftUI
+
+
+/// Displays the contacts for the Spezi Template Application.
+struct Contacts: View {
+ let contacts = [
+ Contact(
+ name: PersonNameComponents(
+ givenName: "Leland",
+ familyName: "Stanford"
+ ),
+ image: Image(systemName: "figure.wave.circle"), // swiftlint:disable:this accessibility_label_for_image
+ title: "University Founder",
+ description: String(localized: "LELAND_STANFORD_BIO"),
+ organization: "Stanford University",
+ address: {
+ let address = CNMutablePostalAddress()
+ address.country = "USA"
+ address.state = "CA"
+ address.postalCode = "94305"
+ address.city = "Stanford"
+ address.street = "450 Serra Mall"
+ return address
+ }(),
+ contactOptions: [
+ .call("+1 (650) 723-2300"),
+ .text("+1 (650) 723-2300"),
+ .email(addresses: ["contact@stanford.edu"]),
+ ContactOption(
+ image: Image(systemName: "safari.fill"), // swiftlint:disable:this accessibility_label_for_image
+ title: "Website",
+ action: {
+ if let url = URL(string: "https://stanford.edu") {
+ UIApplication.shared.open(url)
+ }
+ }
+ )
+ ]
+ )
+ ]
+
+ @Binding var presentingAccount: Bool
+
+
+ var body: some View {
+ NavigationStack {
+ ContactsList(contacts: contacts)
+ .navigationTitle(String(localized: "CONTACTS_NAVIGATION_TITLE"))
+ .toolbar {
+ if AccountButton.shouldDisplay {
+ AccountButton(isPresented: $presentingAccount)
+ }
+ }
+ }
+ }
+
+
+ init(presentingAccount: Binding) {
+ self._presentingAccount = presentingAccount
+ }
+}
+
+
+#if DEBUG
+#Preview {
+ Contacts(presentingAccount: .constant(false))
+}
+#endif
diff --git a/TemplateApplication/Contributions/ContributionsList.swift b/TemplateApplication/Contributions/ContributionsList.swift
new file mode 100644
index 0000000..86f967c
--- /dev/null
+++ b/TemplateApplication/Contributions/ContributionsList.swift
@@ -0,0 +1,53 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import SwiftPackageList
+import SwiftUI
+
+
+struct ContributionsList: View {
+ var packages: [Package] = PackageHelper.getPackageList()
+
+ var body: some View {
+ List {
+ Section(footer: Text("PROJECT_LICENSE_DESCRIPTION")) {
+ Text("CONTRIBUTIONS_LIST_DESCRIPTION")
+ }
+ Section(
+ header: Text("CONTRIBUTIONS_LIST_HEADER"),
+ footer: Text("CONTRIBUTIONS_LIST_FOOTER")
+ ) {
+ ForEach(packages.sorted(by: { $0.name < $1.name }), id: \.name) { package in
+ PackageCell(package: package)
+ }
+ }
+ }
+ .navigationTitle("LICENSE_INFO_TITLE")
+ .navigationBarTitleDisplayMode(.inline)
+ }
+}
+
+
+#if DEBUG
+#Preview {
+ let mockPackages = [
+ Package(
+ name: "MockPackage",
+ version: "1.0",
+ branch: nil,
+ revision: "0",
+ // We use a force unwrap in the preview as we can not recover from an error here
+ // and the code will never end up in a production environment.
+ // swiftlint:disable:next force_unwrapping
+ repositoryURL: URL(string: "github.com")!,
+ license: "MIT License"
+ )
+ ]
+ return ContributionsList(packages: mockPackages)
+}
+#endif
diff --git a/TemplateApplication/Contributions/Package+LicenseType.swift b/TemplateApplication/Contributions/Package+LicenseType.swift
new file mode 100644
index 0000000..7301c0a
--- /dev/null
+++ b/TemplateApplication/Contributions/Package+LicenseType.swift
@@ -0,0 +1,104 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import Foundation
+import SwiftPackageList
+
+
+// This section of code is based on the SwiftPackageList package:
+// - Original code: https://github.com/FelixHerrmann/swift-package-list/issues/43
+enum LicenseType {
+ case mit
+ case apachev2
+ case gplv2
+ case gplv3
+ case bsd2
+ case bsd3
+ case bsd4
+ case zlib
+
+ /// SPDX-License-Identifier for the UI
+ var spdxIdentifier: String {
+ switch self {
+ case .mit: return "MIT"
+ case .apachev2: return "Apache-2.0"
+ case .gplv2: return "GPL-2.0"
+ case .gplv3: return "GPL-3.0"
+ case .bsd2: return "BSD-2-Clause"
+ case .bsd3: return "BSD-3-Clause"
+ case .bsd4: return "BSD-4-Clause"
+ case .zlib: return "Zlib"
+ }
+ }
+
+ /// Initializer that scans the license document for common licenses and versions
+ init?(license: String) {
+ let license = license
+ .replacingOccurrences(of: "\\s+|\\n", with: " ", options: .regularExpression)
+
+ if license.contains(mitText) {
+ self = .mit
+ } else if license.contains(apacheText) && license.contains("Version 2.0") {
+ self = .apachev2
+ } else if license.contains(gnuText) && license.contains("Version 2") {
+ self = .gplv2
+ } else if license.contains(gnuText) && license.contains("Version 3") {
+ self = .gplv3
+ } else if license.contains(bsdFourClauseText) {
+ self = .bsd4
+ } else if license.range(of: bsdThreeClausePattern, options: .regularExpression) != nil {
+ self = .bsd3
+ } else if license.contains(bsdTwoClauseText) {
+ self = .bsd2
+ } else if license.range(of: zlibPattern, options: .regularExpression) != nil {
+ self = .zlib
+ } else {
+ return nil
+ }
+ }
+}
+
+
+// Constants representing typical text and regular expression patterns often found in license files.
+// They are used for matching and identifying different types of licenses within text documents.
+private let mitText = "MIT License"
+private let apacheText = "Apache License"
+private let gnuText = "GNU GENERAL PUBLIC LICENSE"
+private let bsdTwoClauseText =
+ """
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met
+ """
+private let bsdThreeClausePattern =
+ """
+ Neither the name of (.+) nor the names of (.+) may be used to endorse or promote products derived from this software \
+ without specific prior written permission
+ """
+private let bsdFourClauseText =
+ """
+ All advertising materials mentioning features or use of this software must display the following acknowledgement: \
+ this product includes software developed by
+ """
+private let zlibPattern =
+ """
+ The origin of this software must not be misrepresented; you must not claim that you wrote the original software. \
+ If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.(.*) \
+ Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.(.*) \
+ This notice may not be removed or altered from any source distribution.
+ """
+
+
+extension Package {
+ /// Generates the `LicenseType` from a license document of `String`
+ func getLicenseType(license: String?) -> LicenseType? {
+ if let license = license {
+ let licenseType = LicenseType(license: license)
+ return licenseType
+ }
+ return nil
+ }
+}
diff --git a/TemplateApplication/Contributions/PackageCell.swift b/TemplateApplication/Contributions/PackageCell.swift
new file mode 100644
index 0000000..d64780a
--- /dev/null
+++ b/TemplateApplication/Contributions/PackageCell.swift
@@ -0,0 +1,73 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import SwiftPackageList
+import SwiftUI
+
+
+struct PackageCell: View {
+ let package: Package
+
+ var body: some View {
+ HStack {
+ VStack(alignment: .leading, spacing: 4) {
+ Text(package.name).font(.headline)
+ HStack {
+ Text(getPackageDetails(package: package))
+ .font(.caption)
+ if let licenseType = package.getLicenseType(license: package.license) {
+ Text(licenseType.spdxIdentifier)
+ .font(.caption)
+ .fontWeight(.semibold)
+ .padding(2)
+ .background(Color(.systemGray5))
+ .cornerRadius(4)
+ }
+ }
+ }
+ Spacer()
+ Button(action: {
+ UIApplication.shared.open(package.repositoryURL)
+ }) {
+ Image(systemName: "safari.fill")
+ .imageScale(.large)
+ }.buttonStyle(PlainButtonStyle())
+ .foregroundColor(.blue)
+ .accessibilityLabel(Text("Repository Link"))
+ }
+ }
+
+ func getPackageDetails(package: Package) -> String {
+ if let branch = package.branch {
+ return "Branch: \(branch)"
+ } else if let version = package.version {
+ return "Version: \(version)"
+ } else {
+ return "Revision: \(package.revision)"
+ }
+ }
+}
+
+
+#if DEBUG
+#Preview(traits: .sizeThatFitsLayout) {
+ let mockPackage = Package(
+ name: "MockPackage",
+ version: "1.0",
+ branch: nil,
+ revision: "0",
+ // We use a force unwrap in the preview as we can not recover from an error here
+ // and the code will never end up in a production environment.
+ // swiftlint:disable:next force_unwrapping
+ repositoryURL: URL(string: "github.com")!,
+ license: "MIT License"
+ )
+
+ return PackageCell(package: mockPackage)
+}
+#endif
diff --git a/TemplateApplication/Contributions/PackageHelper.swift b/TemplateApplication/Contributions/PackageHelper.swift
new file mode 100644
index 0000000..11e6e7d
--- /dev/null
+++ b/TemplateApplication/Contributions/PackageHelper.swift
@@ -0,0 +1,26 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import Foundation
+import SwiftPackageList
+
+
+enum PackageHelper {
+ /// Helper function that calls the corresponding API of `SwiftPackageList`to fetch the list of packages
+ static func getPackageList() -> [Package] {
+ do {
+ let packages = try packageList()
+ return packages
+ } catch PackageListError.noPackageList {
+ print("There is no package-list file")
+ } catch {
+ print(error)
+ }
+ return []
+ }
+}
diff --git a/TemplateApplication/Helper/Binding+Negate.swift b/TemplateApplication/Helper/Binding+Negate.swift
new file mode 100644
index 0000000..2c2d49d
--- /dev/null
+++ b/TemplateApplication/Helper/Binding+Negate.swift
@@ -0,0 +1,20 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import SwiftUI
+
+
+extension Binding where Value == Bool {
+ /// Negates a `Binding`.
+ prefix static func ! (value: Binding) -> Binding {
+ Binding(
+ get: { !value.wrappedValue },
+ set: { value.wrappedValue = !$0 }
+ )
+ }
+}
diff --git a/TemplateApplication/Helper/Bundle+Image.swift b/TemplateApplication/Helper/Bundle+Image.swift
new file mode 100644
index 0000000..96ae842
--- /dev/null
+++ b/TemplateApplication/Helper/Bundle+Image.swift
@@ -0,0 +1,30 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import SwiftUI
+
+
+extension Foundation.Bundle {
+ /// Loads an image from the `Bundle` instance.
+ /// - Parameters:
+ /// - name: The name of the image.
+ /// - fileExtension: The file extension of the image.
+ /// - Returns: Returns the `UIImage` loaded from the `Bundle` instance.
+ func image(withName name: String, fileExtension: String) -> UIImage {
+ guard let resourceURL = self.url(forResource: name, withExtension: fileExtension) else {
+ fatalError("Could not find the file \"\(name).\(fileExtension)\" in the bundle.")
+ }
+
+ guard let resourceData = try? Data(contentsOf: resourceURL),
+ let image = UIImage(data: resourceData) else {
+ fatalError("Decode the image named \"\(name).\(fileExtension)\"")
+ }
+
+ return image
+ }
+}
diff --git a/TemplateApplication/Helper/CodableArray+RawRepresentable.swift b/TemplateApplication/Helper/CodableArray+RawRepresentable.swift
new file mode 100644
index 0000000..913af05
--- /dev/null
+++ b/TemplateApplication/Helper/CodableArray+RawRepresentable.swift
@@ -0,0 +1,28 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import Foundation
+
+
+extension Array: RawRepresentable where Element: Codable {
+ public var rawValue: String {
+ guard let data = try? JSONEncoder().encode(self),
+ let rawValue = String(data: data, encoding: .utf8) else {
+ return "[]"
+ }
+ return rawValue
+ }
+
+ public init?(rawValue: String) {
+ guard let data = rawValue.data(using: .utf8),
+ let result = try? JSONDecoder().decode([Element].self, from: data) else {
+ return nil
+ }
+ self = result
+ }
+}
diff --git a/TemplateApplication/Home.swift b/TemplateApplication/Home.swift
new file mode 100644
index 0000000..00004b2
--- /dev/null
+++ b/TemplateApplication/Home.swift
@@ -0,0 +1,86 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import SpeziAccount
+import SpeziMockWebService
+import SwiftUI
+
+
+struct HomeView: View {
+ enum Tabs: String {
+ case schedule
+ case contact
+ case mockUpload
+ }
+
+ static var accountEnabled: Bool {
+ !FeatureFlags.disableFirebase && !FeatureFlags.skipOnboarding
+ }
+
+
+ @AppStorage(StorageKeys.homeTabSelection) private var selectedTab = Tabs.schedule
+ @State private var presentingAccount = false
+
+
+ var body: some View {
+ TabView(selection: $selectedTab) {
+ ScheduleView(presentingAccount: $presentingAccount)
+ .tag(Tabs.schedule)
+ .tabItem {
+ Label("SCHEDULE_TAB_TITLE", systemImage: "list.clipboard")
+ }
+ Contacts(presentingAccount: $presentingAccount)
+ .tag(Tabs.contact)
+ .tabItem {
+ Label("CONTACTS_TAB_TITLE", systemImage: "person.fill")
+ }
+ if FeatureFlags.disableFirebase {
+ MockUpload(presentingAccount: $presentingAccount)
+ .tag(Tabs.mockUpload)
+ .tabItem {
+ Label("MOCK_WEB_SERVICE_TAB_TITLE", systemImage: "server.rack")
+ }
+ }
+ }
+ .sheet(isPresented: $presentingAccount) {
+ AccountSheet()
+ }
+ .accountRequired(Self.accountEnabled) {
+ AccountSheet()
+ }
+ .verifyRequiredAccountDetails(Self.accountEnabled)
+ }
+}
+
+
+#if DEBUG
+#Preview {
+ let details = AccountDetails.Builder()
+ .set(\.userId, value: "lelandstanford@stanford.edu")
+ .set(\.name, value: PersonNameComponents(givenName: "Leland", familyName: "Stanford"))
+
+ return HomeView()
+ .previewWith(standard: TemplateApplicationStandard()) {
+ TemplateApplicationScheduler()
+ MockWebService()
+ AccountConfiguration(building: details, active: MockUserIdPasswordAccountService())
+ }
+}
+
+#Preview {
+ CommandLine.arguments.append("--disableFirebase") // make sure the MockWebService is displayed
+ return HomeView()
+ .previewWith(standard: TemplateApplicationStandard()) {
+ TemplateApplicationScheduler()
+ MockWebService()
+ AccountConfiguration {
+ MockUserIdPasswordAccountService()
+ }
+ }
+}
+#endif
diff --git a/TemplateApplication/MockUpload/MockUpload.swift b/TemplateApplication/MockUpload/MockUpload.swift
new file mode 100644
index 0000000..7a99959
--- /dev/null
+++ b/TemplateApplication/MockUpload/MockUpload.swift
@@ -0,0 +1,41 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import SpeziMockWebService
+import SwiftUI
+
+
+struct MockUpload: View {
+ @Binding var presentingAccount: Bool
+
+ var body: some View {
+ NavigationStack {
+ RequestList()
+ .toolbar {
+ if AccountButton.shouldDisplay {
+ AccountButton(isPresented: $presentingAccount)
+ }
+ }
+ }
+ }
+
+
+ init(presentingAccount: Binding) {
+ self._presentingAccount = presentingAccount
+ }
+}
+
+
+#if DEBUG
+#Preview {
+ MockUpload(presentingAccount: .constant(false))
+ .previewWith {
+ MockWebService()
+ }
+}
+#endif
diff --git a/TemplateApplication/Onboarding/AccountOnboarding.swift b/TemplateApplication/Onboarding/AccountOnboarding.swift
new file mode 100644
index 0000000..cbf1074
--- /dev/null
+++ b/TemplateApplication/Onboarding/AccountOnboarding.swift
@@ -0,0 +1,64 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import SpeziAccount
+import SpeziOnboarding
+import SwiftUI
+
+
+struct AccountOnboarding: View {
+ @Environment(Account.self) private var account
+ @Environment(OnboardingNavigationPath.self) private var onboardingNavigationPath
+
+
+ var body: some View {
+ AccountSetup { _ in
+ Task {
+ // Placing the nextStep() call inside this task will ensure that the sheet dismiss animation is
+ // played till the end before we navigate to the next step.
+ onboardingNavigationPath.nextStep()
+ }
+ } header: {
+ AccountSetupHeader()
+ } continue: {
+ OnboardingActionsView(
+ "ACCOUNT_NEXT",
+ action: {
+ onboardingNavigationPath.nextStep()
+ }
+ )
+ }
+ }
+}
+
+
+#if DEBUG
+#Preview("Account Onboarding SignIn") {
+ OnboardingStack {
+ AccountOnboarding()
+ }
+ .previewWith {
+ AccountConfiguration {
+ MockUserIdPasswordAccountService()
+ }
+ }
+}
+
+#Preview("Account Onboarding") {
+ let details = AccountDetails.Builder()
+ .set(\.userId, value: "lelandstanford@stanford.edu")
+ .set(\.name, value: PersonNameComponents(givenName: "Leland", familyName: "Stanford"))
+
+ return OnboardingStack {
+ AccountOnboarding()
+ }
+ .previewWith {
+ AccountConfiguration(building: details, active: MockUserIdPasswordAccountService())
+ }
+}
+#endif
diff --git a/TemplateApplication/Onboarding/Consent.swift b/TemplateApplication/Onboarding/Consent.swift
new file mode 100644
index 0000000..cb97700
--- /dev/null
+++ b/TemplateApplication/Onboarding/Consent.swift
@@ -0,0 +1,49 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import SpeziOnboarding
+import SwiftUI
+
+
+/// - Note: The `OnboardingConsentView` exports the signed consent form as PDF to the Spezi `Standard`, necessitating the conformance of the `Standard` to the `OnboardingConstraint`.
+struct Consent: View {
+ @Environment(OnboardingNavigationPath.self) private var onboardingNavigationPath
+
+
+ private var consentDocument: Data {
+ guard let path = Bundle.main.url(forResource: "ConsentDocument", withExtension: "md"),
+ let data = try? Data(contentsOf: path) else {
+ return Data(String(localized: "CONSENT_LOADING_ERROR").utf8)
+ }
+ return data
+ }
+
+
+ var body: some View {
+ OnboardingConsentView(
+ markdown: {
+ consentDocument
+ },
+ action: {
+ onboardingNavigationPath.nextStep()
+ }
+ )
+ }
+}
+
+
+#if DEBUG
+#Preview {
+ OnboardingStack {
+ Consent()
+ }
+ .previewWith(standard: TemplateApplicationStandard()) {
+ OnboardingDataSource()
+ }
+}
+#endif
diff --git a/TemplateApplication/Onboarding/HealthKitPermissions.swift b/TemplateApplication/Onboarding/HealthKitPermissions.swift
new file mode 100644
index 0000000..036bbde
--- /dev/null
+++ b/TemplateApplication/Onboarding/HealthKitPermissions.swift
@@ -0,0 +1,77 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import SpeziHealthKit
+import SpeziOnboarding
+import SwiftUI
+
+
+struct HealthKitPermissions: View {
+ @Environment(HealthKit.self) private var healthKitDataSource
+ @Environment(OnboardingNavigationPath.self) private var onboardingNavigationPath
+
+ @State private var healthKitProcessing = false
+
+
+ var body: some View {
+ OnboardingView(
+ contentView: {
+ VStack {
+ OnboardingTitleView(
+ title: "HEALTHKIT_PERMISSIONS_TITLE",
+ subtitle: "HEALTHKIT_PERMISSIONS_SUBTITLE"
+ )
+ Spacer()
+ Image(systemName: "heart.text.square.fill")
+ .font(.system(size: 150))
+ .foregroundColor(.accentColor)
+ .accessibilityHidden(true)
+ Text("HEALTHKIT_PERMISSIONS_DESCRIPTION")
+ .multilineTextAlignment(.center)
+ .padding(.vertical, 16)
+ Spacer()
+ }
+ }, actionView: {
+ OnboardingActionsView(
+ "HEALTHKIT_PERMISSIONS_BUTTON",
+ action: {
+ do {
+ healthKitProcessing = true
+ // HealthKit is not available in the preview simulator.
+ if ProcessInfo.processInfo.isPreviewSimulator {
+ try await _Concurrency.Task.sleep(for: .seconds(5))
+ } else {
+ try await healthKitDataSource.askForAuthorization()
+ }
+ } catch {
+ print("Could not request HealthKit permissions.")
+ }
+ healthKitProcessing = false
+
+ onboardingNavigationPath.nextStep()
+ }
+ )
+ }
+ )
+ .navigationBarBackButtonHidden(healthKitProcessing)
+ // Small fix as otherwise "Login" or "Sign up" is still shown in the nav bar
+ .navigationTitle(Text(verbatim: ""))
+ }
+}
+
+
+#if DEBUG
+#Preview {
+ OnboardingStack {
+ HealthKitPermissions()
+ }
+ .previewWith(standard: TemplateApplicationStandard()) {
+ HealthKit()
+ }
+}
+#endif
diff --git a/TemplateApplication/Onboarding/InterestingModules.swift b/TemplateApplication/Onboarding/InterestingModules.swift
new file mode 100644
index 0000000..77f655d
--- /dev/null
+++ b/TemplateApplication/Onboarding/InterestingModules.swift
@@ -0,0 +1,54 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import SpeziOnboarding
+import SwiftUI
+
+
+struct InterestingModules: View {
+ @Environment(OnboardingNavigationPath.self) private var onboardingNavigationPath
+
+
+ var body: some View {
+ SequentialOnboardingView(
+ title: "INTERESTING_MODULES_TITLE",
+ subtitle: "INTERESTING_MODULES_SUBTITLE",
+ content: [
+ SequentialOnboardingView.Content(
+ title: "INTERESTING_MODULES_AREA1_TITLE",
+ description: "INTERESTING_MODULES_AREA1_DESCRIPTION"
+ ),
+ SequentialOnboardingView.Content(
+ title: "INTERESTING_MODULES_AREA2_TITLE",
+ description: "INTERESTING_MODULES_AREA2_DESCRIPTION"
+ ),
+ SequentialOnboardingView.Content(
+ title: "INTERESTING_MODULES_AREA3_TITLE",
+ description: "INTERESTING_MODULES_AREA3_DESCRIPTION"
+ ),
+ SequentialOnboardingView.Content(
+ title: "INTERESTING_MODULES_AREA4_TITLE",
+ description: "INTERESTING_MODULES_AREA4_DESCRIPTION"
+ )
+ ],
+ actionText: "INTERESTING_MODULES_BUTTON",
+ action: {
+ onboardingNavigationPath.nextStep()
+ }
+ )
+ }
+}
+
+
+#if DEBUG
+#Preview {
+ OnboardingStack {
+ InterestingModules()
+ }
+}
+#endif
diff --git a/TemplateApplication/Onboarding/NotificationPermissions.swift b/TemplateApplication/Onboarding/NotificationPermissions.swift
new file mode 100644
index 0000000..d4af500
--- /dev/null
+++ b/TemplateApplication/Onboarding/NotificationPermissions.swift
@@ -0,0 +1,77 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import SpeziOnboarding
+import SpeziScheduler
+import SwiftUI
+
+
+struct NotificationPermissions: View {
+ @Environment(TemplateApplicationScheduler.self) private var scheduler
+ @Environment(OnboardingNavigationPath.self) private var onboardingNavigationPath
+
+ @State private var notificationProcessing = false
+
+
+ var body: some View {
+ OnboardingView(
+ contentView: {
+ VStack {
+ OnboardingTitleView(
+ title: "NOTIFICATION_PERMISSIONS_TITLE",
+ subtitle: "NOTIFICATION_PERMISSIONS_SUBTITLE"
+ )
+ Spacer()
+ Image(systemName: "bell.square.fill")
+ .font(.system(size: 150))
+ .foregroundColor(.accentColor)
+ .accessibilityHidden(true)
+ Text("NOTIFICATION_PERMISSIONS_DESCRIPTION")
+ .multilineTextAlignment(.center)
+ .padding(.vertical, 16)
+ Spacer()
+ }
+ }, actionView: {
+ OnboardingActionsView(
+ "NOTIFICATION_PERMISSIONS_BUTTON",
+ action: {
+ do {
+ notificationProcessing = true
+ // Notification Authorization is not available in the preview simulator.
+ if ProcessInfo.processInfo.isPreviewSimulator {
+ try await _Concurrency.Task.sleep(for: .seconds(5))
+ } else {
+ try await scheduler.requestLocalNotificationAuthorization()
+ }
+ } catch {
+ print("Could not request notification permissions.")
+ }
+ notificationProcessing = false
+
+ onboardingNavigationPath.nextStep()
+ }
+ )
+ }
+ )
+ .navigationBarBackButtonHidden(notificationProcessing)
+ // Small fix as otherwise "Login" or "Sign up" is still shown in the nav bar
+ .navigationTitle(Text(verbatim: ""))
+ }
+}
+
+
+#if DEBUG
+#Preview {
+ OnboardingStack {
+ NotificationPermissions()
+ }
+ .previewWith {
+ TemplateApplicationScheduler()
+ }
+}
+#endif
diff --git a/TemplateApplication/Onboarding/OnboardingFlow.swift b/TemplateApplication/Onboarding/OnboardingFlow.swift
new file mode 100644
index 0000000..a6ee1f9
--- /dev/null
+++ b/TemplateApplication/Onboarding/OnboardingFlow.swift
@@ -0,0 +1,79 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import SpeziAccount
+import SpeziFirebaseAccount
+import SpeziHealthKit
+import SpeziOnboarding
+import SwiftUI
+
+
+/// Displays an multi-step onboarding flow for the Spezi Template Application.
+struct OnboardingFlow: View {
+ @Environment(HealthKit.self) private var healthKitDataSource
+ @Environment(TemplateApplicationScheduler.self) private var scheduler
+
+ @AppStorage(StorageKeys.onboardingFlowComplete) private var completedOnboardingFlow = false
+
+ @State private var localNotificationAuthorization = false
+
+
+ private var healthKitAuthorization: Bool {
+ // As HealthKit not available in preview simulator
+ if ProcessInfo.processInfo.isPreviewSimulator {
+ return false
+ }
+
+ return healthKitDataSource.authorized
+ }
+
+
+ var body: some View {
+ OnboardingStack(onboardingFlowComplete: $completedOnboardingFlow) {
+ Welcome()
+ InterestingModules()
+
+ if !FeatureFlags.disableFirebase {
+ AccountOnboarding()
+ }
+
+ #if !(targetEnvironment(simulator) && (arch(i386) || arch(x86_64)))
+ Consent()
+ #endif
+
+ if HKHealthStore.isHealthDataAvailable() && !healthKitAuthorization {
+ HealthKitPermissions()
+ }
+
+ if !localNotificationAuthorization {
+ NotificationPermissions()
+ }
+ }
+ .task {
+ localNotificationAuthorization = await scheduler.localNotificationAuthorization
+ }
+ .interactiveDismissDisabled(!completedOnboardingFlow)
+ }
+}
+
+
+#if DEBUG
+#Preview {
+ OnboardingFlow()
+ .environment(Account(MockUserIdPasswordAccountService()))
+ .previewWith(standard: TemplateApplicationStandard()) {
+ OnboardingDataSource()
+ HealthKit()
+ AccountConfiguration {
+ MockUserIdPasswordAccountService()
+ }
+
+ TemplateApplicationScheduler()
+ }
+}
+#endif
diff --git a/TemplateApplication/Onboarding/Welcome.swift b/TemplateApplication/Onboarding/Welcome.swift
new file mode 100644
index 0000000..00620be
--- /dev/null
+++ b/TemplateApplication/Onboarding/Welcome.swift
@@ -0,0 +1,63 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import SpeziOnboarding
+import SwiftUI
+
+
+struct Welcome: View {
+ @Environment(OnboardingNavigationPath.self) private var onboardingNavigationPath
+
+
+ var body: some View {
+ OnboardingView(
+ title: "WELCOME_TITLE",
+ subtitle: "WELCOME_SUBTITLE",
+ areas: [
+ OnboardingInformationView.Content(
+ icon: {
+ Image(systemName: "apps.iphone")
+ .accessibilityHidden(true)
+ },
+ title: "WELCOME_AREA1_TITLE",
+ description: "WELCOME_AREA1_DESCRIPTION"
+ ),
+ OnboardingInformationView.Content(
+ icon: {
+ Image(systemName: "shippingbox.fill")
+ .accessibilityHidden(true)
+ },
+ title: "WELCOME_AREA2_TITLE",
+ description: "WELCOME_AREA2_DESCRIPTION"
+ ),
+ OnboardingInformationView.Content(
+ icon: {
+ Image(systemName: "list.bullet.clipboard.fill")
+ .accessibilityHidden(true)
+ },
+ title: "WELCOME_AREA3_TITLE",
+ description: "WELCOME_AREA3_DESCRIPTION"
+ )
+ ],
+ actionText: "WELCOME_BUTTON",
+ action: {
+ onboardingNavigationPath.nextStep()
+ }
+ )
+ .padding(.top, 24)
+ }
+}
+
+
+#if DEBUG
+#Preview {
+ OnboardingStack {
+ Welcome()
+ }
+}
+#endif
diff --git a/TemplateApplication/Resources/AppIcon.png b/TemplateApplication/Resources/AppIcon.png
new file mode 100644
index 0000000..7414dd9
Binary files /dev/null and b/TemplateApplication/Resources/AppIcon.png differ
diff --git a/TemplateApplication/Resources/AppIcon.png.license b/TemplateApplication/Resources/AppIcon.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Resources/AppIcon.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Resources/Assets.xcassets/AccentColor.colorset/Contents.json b/TemplateApplication/Resources/Assets.xcassets/AccentColor.colorset/Contents.json
new file mode 100644
index 0000000..eb87897
--- /dev/null
+++ b/TemplateApplication/Resources/Assets.xcassets/AccentColor.colorset/Contents.json
@@ -0,0 +1,11 @@
+{
+ "colors" : [
+ {
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/TemplateApplication/Resources/Assets.xcassets/AccentColor.colorset/Contents.json.license b/TemplateApplication/Resources/Assets.xcassets/AccentColor.colorset/Contents.json.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Resources/Assets.xcassets/AccentColor.colorset/Contents.json.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon.png b/TemplateApplication/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon.png
new file mode 100644
index 0000000..7414dd9
Binary files /dev/null and b/TemplateApplication/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon.png differ
diff --git a/TemplateApplication/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon.png.license b/TemplateApplication/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json b/TemplateApplication/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..cefcc87
--- /dev/null
+++ b/TemplateApplication/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,14 @@
+{
+ "images" : [
+ {
+ "filename" : "AppIcon.png",
+ "idiom" : "universal",
+ "platform" : "ios",
+ "size" : "1024x1024"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/TemplateApplication/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json.license b/TemplateApplication/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Resources/Assets.xcassets/Contents.json b/TemplateApplication/Resources/Assets.xcassets/Contents.json
new file mode 100644
index 0000000..73c0059
--- /dev/null
+++ b/TemplateApplication/Resources/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/TemplateApplication/Resources/Assets.xcassets/Contents.json.license b/TemplateApplication/Resources/Assets.xcassets/Contents.json.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Resources/Assets.xcassets/Contents.json.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Resources/ConsentDocument.md b/TemplateApplication/Resources/ConsentDocument.md
new file mode 100644
index 0000000..5f9df6f
--- /dev/null
+++ b/TemplateApplication/Resources/ConsentDocument.md
@@ -0,0 +1 @@
+Spezi can render consent documents in the markdown format: This is a *markdown* **example**.
diff --git a/TemplateApplication/Resources/ConsentDocument.md.license b/TemplateApplication/Resources/ConsentDocument.md.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Resources/ConsentDocument.md.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Resources/Localizable.xcstrings b/TemplateApplication/Resources/Localizable.xcstrings
new file mode 100644
index 0000000..4747562
--- /dev/null
+++ b/TemplateApplication/Resources/Localizable.xcstrings
@@ -0,0 +1,545 @@
+{
+ "sourceLanguage" : "en",
+ "strings" : {
+ "ACCOUNT_NEXT" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Next"
+ }
+ }
+ }
+ },
+ "ACCOUNT_SETUP_DESCRIPTION" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "You may login to your existing account. Or create a new one if you don't have one already."
+ }
+ }
+ }
+ },
+ "ACCOUNT_SIGNED_IN_DESCRIPTION" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "You are already logged in with the account shown below. Continue or change your account by logging out."
+ }
+ }
+ }
+ },
+ "ACCOUNT_SUBTITLE" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "The Template Application demonstrates the usage of the Firebase Account Module."
+ }
+ }
+ }
+ },
+ "ACCOUNT_TITLE" : {
+ "comment" : "MARK: Account",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Your Account"
+ }
+ }
+ }
+ },
+ "CLOSE" : {
+ "comment" : "MARK: General",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Close"
+ }
+ }
+ }
+ },
+ "COMPLETED_TASK_LABEL %@" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Completed Task: %@"
+ }
+ }
+ }
+ },
+ "CONSENT_LOADING_ERROR" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Please include a markdown based document named \"ConsentDocument\" in your module Bundle."
+ }
+ }
+ }
+ },
+ "CONTACTS_NAVIGATION_TITLE" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Contacts"
+ }
+ }
+ }
+ },
+ "CONTACTS_TAB_TITLE" : {
+ "comment" : "MARK: - Contacts",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Contacts"
+ }
+ }
+ }
+ },
+ "CONTRIBUTIONS_LIST_DESCRIPTION" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "The following list contains all Swift Package dependencies of the SpeziTemplateApplication."
+ }
+ }
+ }
+ },
+ "CONTRIBUTIONS_LIST_FOOTER" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Please refer to the individual repository links for packages without license labels."
+ }
+ }
+ }
+ },
+ "CONTRIBUTIONS_LIST_HEADER" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Packages"
+ }
+ }
+ }
+ },
+ "HEALTHKIT_PERMISSIONS_BUTTON" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Grant Access"
+ }
+ }
+ }
+ },
+ "HEALTHKIT_PERMISSIONS_DESCRIPTION" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "This onboarding step allows you to customize the onboarding flow to explain how the application uses the HealhtKit data and allows a user to cusomize the selection."
+ }
+ }
+ }
+ },
+ "HEALTHKIT_PERMISSIONS_SUBTITLE" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Spezi can access data from HealthKit using the HealthKitDataSource module."
+ }
+ }
+ }
+ },
+ "HEALTHKIT_PERMISSIONS_TITLE" : {
+ "comment" : "MARK: HealthKit",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "HealthKit Access"
+ }
+ }
+ }
+ },
+ "INTERESTING_MODULES_AREA1_DESCRIPTION" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "The onboarding module allows you to build an onboarding flow like this one."
+ }
+ }
+ }
+ },
+ "INTERESTING_MODULES_AREA1_TITLE" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Onboarding"
+ }
+ }
+ }
+ },
+ "INTERESTING_MODULES_AREA2_DESCRIPTION" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "The application uses HL7 FHIR to provide a common standard to encode data gathered by the application."
+ }
+ }
+ }
+ },
+ "INTERESTING_MODULES_AREA2_TITLE" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "HL7 FHIR"
+ }
+ }
+ }
+ },
+ "INTERESTING_MODULES_AREA3_DESCRIPTION" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "The contact module allows you to display contact information in your application."
+ }
+ }
+ }
+ },
+ "INTERESTING_MODULES_AREA3_TITLE" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Contact"
+ }
+ }
+ }
+ },
+ "INTERESTING_MODULES_AREA4_DESCRIPTION" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "The HealthKit data source module allows you to fetch data from HealthKit and e.g. transform it to FHIR resources."
+ }
+ }
+ }
+ },
+ "INTERESTING_MODULES_AREA4_TITLE" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "HealthKit Data Source"
+ }
+ }
+ }
+ },
+ "INTERESTING_MODULES_BUTTON" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Next"
+ }
+ }
+ }
+ },
+ "INTERESTING_MODULES_SUBTITLE" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Here are a few Spezi modules that are interesting to know about ..."
+ }
+ }
+ }
+ },
+ "INTERESTING_MODULES_TITLE" : {
+ "comment" : "MARK: Interesting Modules",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Interesting Modules"
+ }
+ }
+ }
+ },
+ "LELAND_STANFORD_BIO" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Amasa Leland Stanford (March 9, 1824 – June 21, 1893) was an American industrialist and politician. [...] He and his wife Jane were also the founders of Stanford University, which they named after their late son.\n[https://en.wikipedia.org/wiki/Leland_Stanford]"
+ }
+ }
+ }
+ },
+ "LICENSE_INFO_TITLE" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "License Information"
+ }
+ }
+ }
+ },
+ "MOCK_WEB_SERVICE_TAB_TITLE" : {
+ "comment" : "MARK: - Mock Upload Data Storage Provider",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Mock Web Service"
+ }
+ }
+ }
+ },
+ "NOTIFICATION_PERMISSIONS_BUTTON" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Allow Notifications"
+ }
+ }
+ }
+ },
+ "NOTIFICATION_PERMISSIONS_DESCRIPTION" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "The Spezi Scheduler module enables to send out local notifications when a new event of a task is scheduled."
+ }
+ }
+ }
+ },
+ "NOTIFICATION_PERMISSIONS_SUBTITLE" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Spezi Scheduler Notifications."
+ }
+ }
+ }
+ },
+ "NOTIFICATION_PERMISSIONS_TITLE" : {
+ "comment" : "MARK: Notifications",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Notifications"
+ }
+ }
+ }
+ },
+ "PROJECT_LICENSE_DESCRIPTION" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "This project is licensed under the MIT License."
+ }
+ }
+ }
+ },
+ "Repository Link" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Repository Link"
+ }
+ }
+ }
+ },
+ "SCHEDULE_LIST_TITLE" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Schedule"
+ }
+ }
+ }
+ },
+ "SCHEDULE_TAB_TITLE" : {
+ "comment" : "MARK: - Schedule",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Schedule"
+ }
+ }
+ }
+ },
+ "TASK_CONTEXT_ACTION_QUESTIONNAIRE" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Start Questionnaire"
+ }
+ }
+ }
+ },
+ "TASK_CONTEXT_ACTION_TEST" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Start Test"
+ }
+ }
+ }
+ },
+ "TASK_LABEL %@" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Task: %@"
+ }
+ }
+ }
+ },
+ "TASK_SOCIAL_SUPPORT_QUESTIONNAIRE_DESCRIPTION" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Please fill out the Social Support Questionnaire every day."
+ }
+ }
+ }
+ },
+ "TASK_SOCIAL_SUPPORT_QUESTIONNAIRE_TITLE" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Social Support Questionnaire"
+ }
+ }
+ }
+ },
+ "WELCOME_AREA1_DESCRIPTION" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "The Spezi Framework builds the foundation of this application."
+ }
+ }
+ }
+ },
+ "WELCOME_AREA1_TITLE" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "The Spezi Framework"
+ }
+ }
+ }
+ },
+ "WELCOME_AREA2_DESCRIPTION" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Spezi uses the Swift Package Manager to import it as a dependency."
+ }
+ }
+ }
+ },
+ "WELCOME_AREA2_TITLE" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Swift Package Manager"
+ }
+ }
+ }
+ },
+ "WELCOME_AREA3_DESCRIPTION" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Spezi offers several modules including HealthKit integration, questionnaires, and more ..."
+ }
+ }
+ }
+ },
+ "WELCOME_AREA3_TITLE" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Spezi Modules"
+ }
+ }
+ }
+ },
+ "WELCOME_BUTTON" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Learn More"
+ }
+ }
+ }
+ },
+ "WELCOME_SUBTITLE" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "This application demonstrates several Spezi features & modules."
+ }
+ }
+ }
+ },
+ "WELCOME_TITLE" : {
+ "comment" : "MARK: Welcome",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Spezi\nTemplate Application"
+ }
+ }
+ }
+ }
+ },
+ "version" : "1.0"
+}
\ No newline at end of file
diff --git a/TemplateApplication/Resources/Localizable.xcstrings.license b/TemplateApplication/Resources/Localizable.xcstrings.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Resources/Localizable.xcstrings.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Resources/SocialSupportQuestionnaire.json b/TemplateApplication/Resources/SocialSupportQuestionnaire.json
new file mode 100644
index 0000000..d3c584d
--- /dev/null
+++ b/TemplateApplication/Resources/SocialSupportQuestionnaire.json
@@ -0,0 +1,387 @@
+{
+ "resourceType": "Questionnaire",
+ "language": "en-US",
+ "id": "socialsupport",
+ "name": "SocialSupport",
+ "title": "Social Support",
+ "description": "This survey measures tangible social support plus a couple of demographic questions.",
+ "version": "1",
+ "status": "draft",
+ "publisher": "RAND Corp",
+ "meta": {
+ "profile": [
+ "http://spezi.stanford.edu/fhir/StructureDefinition/sdf-Questionnaire"
+ ],
+ "tag": [
+ {
+ "system": "urn:ietf:bcp:47",
+ "code": "en-US",
+ "display": "English"
+ }
+ ]
+ },
+ "useContext": [
+ {
+ "code": {
+ "system": "http://hl7.org/fhir/ValueSet/usage-context-type",
+ "code": "focus",
+ "display": "Clinical Focus"
+ },
+ "valueCodeableConcept": {
+ "coding": [
+ {
+ "system": "urn:oid:2.16.578.1.12.4.1.1.8655",
+ "display": "Social Support"
+ }
+ ]
+ }
+ }
+ ],
+ "contact": [
+ {
+ "name": "https://www.rand.org/health-care/surveys_tools/mos/social-support/survey-instrument.html"
+ }
+ ],
+ "subjectType": [
+ "Patient"
+ ],
+ "purpose": "The RAND Medical Outcomes Social Support survey is a 4-item questionnaire that measures social support.",
+ "copyright": "RAND Corp surveys are open-source and free to use.",
+ "date": "2023-01-23T00:00:00-08:00",
+ "url": "http://spezi.stanford.edu/fhir/questionnaire/32f43c8e-93e9-4c70-97a0-e716f8030073",
+ "item": [
+ {
+ "linkId": "dcea2683-9815-4505-b240-e75b502b29ef",
+ "type": "choice",
+ "text": "How often do you need someone to help you if you were confined to bed?",
+ "required": false,
+ "answerOption": [
+ {
+ "valueCoding": {
+ "id": "3d6fe1b8-c64b-497c-8583-db7ddda9e94e",
+ "code": "1",
+ "system": "urn:uuid:e9ecdd47-2e8b-49b3-8780-9d0769a246aa",
+ "display": "None of the time"
+ }
+ },
+ {
+ "valueCoding": {
+ "id": "b4081e9d-d0f1-4aea-9a15-eac4a15d1d10",
+ "code": "2",
+ "system": "urn:uuid:e9ecdd47-2e8b-49b3-8780-9d0769a246aa",
+ "display": "A little of the time"
+ }
+ },
+ {
+ "valueCoding": {
+ "id": "e32f7952-e280-48d7-9746-c13dbb26638f",
+ "code": "3",
+ "system": "urn:uuid:e9ecdd47-2e8b-49b3-8780-9d0769a246aa",
+ "display": "Some of the time"
+ }
+ },
+ {
+ "valueCoding": {
+ "id": "d2f6172d-9402-4cb3-870a-584a7be3a5d7",
+ "code": "4",
+ "system": "urn:uuid:e9ecdd47-2e8b-49b3-8780-9d0769a246aa",
+ "display": "Most of the time"
+ }
+ },
+ {
+ "valueCoding": {
+ "id": "ec48001e-f03e-4a14-8a7a-9fcf34fa81d2",
+ "code": "5",
+ "system": "urn:uuid:e9ecdd47-2e8b-49b3-8780-9d0769a246aa",
+ "display": "All of the time"
+ }
+ }
+ ]
+ },
+ {
+ "linkId": "ce09d701-7b93-4150-defb-51825e05ade9",
+ "type": "choice",
+ "text": "How often do you need someone to take you to the doctor if you needed it?",
+ "required": false,
+ "answerOption": [
+ {
+ "valueCoding": {
+ "id": "3d6fe1b8-c64b-497c-8583-db7ddda9e94e",
+ "code": "1",
+ "system": "urn:uuid:e9ecdd47-2e8b-49b3-8780-9d0769a246aa",
+ "display": "None of the time"
+ }
+ },
+ {
+ "valueCoding": {
+ "id": "b4081e9d-d0f1-4aea-9a15-eac4a15d1d10",
+ "code": "2",
+ "system": "urn:uuid:e9ecdd47-2e8b-49b3-8780-9d0769a246aa",
+ "display": "A little of the time"
+ }
+ },
+ {
+ "valueCoding": {
+ "id": "e32f7952-e280-48d7-9746-c13dbb26638f",
+ "code": "3",
+ "system": "urn:uuid:e9ecdd47-2e8b-49b3-8780-9d0769a246aa",
+ "display": "Some of the time"
+ }
+ },
+ {
+ "valueCoding": {
+ "id": "d2f6172d-9402-4cb3-870a-584a7be3a5d7",
+ "code": "4",
+ "system": "urn:uuid:e9ecdd47-2e8b-49b3-8780-9d0769a246aa",
+ "display": "Most of the time"
+ }
+ },
+ {
+ "valueCoding": {
+ "id": "ec48001e-f03e-4a14-8a7a-9fcf34fa81d2",
+ "code": "5",
+ "system": "urn:uuid:e9ecdd47-2e8b-49b3-8780-9d0769a246aa",
+ "display": "All of the time"
+ }
+ }
+ ]
+ },
+ {
+ "linkId": "58e97564-5f4d-4d4b-86d5-6429cbbc7a8e",
+ "type": "choice",
+ "text": "How often do you need someone to prepare your meals if you were unable to do it yourself?",
+ "required": false,
+ "answerOption": [
+ {
+ "valueCoding": {
+ "id": "3d6fe1b8-c64b-497c-8583-db7ddda9e94e",
+ "code": "1",
+ "system": "urn:uuid:e9ecdd47-2e8b-49b3-8780-9d0769a246aa",
+ "display": "None of the time"
+ }
+ },
+ {
+ "valueCoding": {
+ "id": "b4081e9d-d0f1-4aea-9a15-eac4a15d1d10",
+ "code": "2",
+ "system": "urn:uuid:e9ecdd47-2e8b-49b3-8780-9d0769a246aa",
+ "display": "A little of the time"
+ }
+ },
+ {
+ "valueCoding": {
+ "id": "e32f7952-e280-48d7-9746-c13dbb26638f",
+ "code": "3",
+ "system": "urn:uuid:e9ecdd47-2e8b-49b3-8780-9d0769a246aa",
+ "display": "Some of the time"
+ }
+ },
+ {
+ "valueCoding": {
+ "id": "d2f6172d-9402-4cb3-870a-584a7be3a5d7",
+ "code": "4",
+ "system": "urn:uuid:e9ecdd47-2e8b-49b3-8780-9d0769a246aa",
+ "display": "Most of the time"
+ }
+ },
+ {
+ "valueCoding": {
+ "id": "ec48001e-f03e-4a14-8a7a-9fcf34fa81d2",
+ "code": "5",
+ "system": "urn:uuid:e9ecdd47-2e8b-49b3-8780-9d0769a246aa",
+ "display": "All of the time"
+ }
+ }
+ ]
+ },
+ {
+ "linkId": "ad161c49-e8a6-4d31-90e8-02b2887a765f",
+ "type": "choice",
+ "text": "How often do you need someone to help with daily chores if you were sick",
+ "required": false,
+ "answerOption": [
+ {
+ "valueCoding": {
+ "id": "3d6fe1b8-c64b-497c-8583-db7ddda9e94e",
+ "code": "1",
+ "system": "urn:uuid:e9ecdd47-2e8b-49b3-8780-9d0769a246aa",
+ "display": "None of the time"
+ }
+ },
+ {
+ "valueCoding": {
+ "id": "b4081e9d-d0f1-4aea-9a15-eac4a15d1d10",
+ "code": "2",
+ "system": "urn:uuid:e9ecdd47-2e8b-49b3-8780-9d0769a246aa",
+ "display": "A little of the time"
+ }
+ },
+ {
+ "valueCoding": {
+ "id": "e32f7952-e280-48d7-9746-c13dbb26638f",
+ "code": "3",
+ "system": "urn:uuid:e9ecdd47-2e8b-49b3-8780-9d0769a246aa",
+ "display": "Some of the time"
+ }
+ },
+ {
+ "valueCoding": {
+ "id": "d2f6172d-9402-4cb3-870a-584a7be3a5d7",
+ "code": "4",
+ "system": "urn:uuid:e9ecdd47-2e8b-49b3-8780-9d0769a246aa",
+ "display": "Most of the time"
+ }
+ },
+ {
+ "valueCoding": {
+ "id": "ec48001e-f03e-4a14-8a7a-9fcf34fa81d2",
+ "code": "5",
+ "system": "urn:uuid:e9ecdd47-2e8b-49b3-8780-9d0769a246aa",
+ "display": "All of the time"
+ }
+ }
+ ]
+ },
+ {
+ "linkId": "ba518851-2843-4bbd-c0f7-5b5692d542e0",
+ "type": "integer",
+ "text": "What is your age?",
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/minValue",
+ "valueInteger": 18
+ },
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/maxValue",
+ "valueInteger": 120
+ },
+ {
+ "url": "http://biodesign.stanford.edu/fhir/StructureDefinition/validationtext",
+ "valueString": "Please enter a valid age."
+ }
+ ],
+ "required": false
+ },
+ {
+ "linkId": "695525f3-3e89-4455-8e25-878171c596da",
+ "type": "choice",
+ "text": "What is your preferred contact method?",
+ "required": false,
+ "answerOption": [
+ {
+ "valueCoding": {
+ "id": "b7a3d7a5-52b9-49b1-8b59-7a3885483f1c",
+ "code": "phone-call",
+ "system": "urn:uuid:736ac230-812a-4f4a-edec-5156910fb6ec",
+ "display": "Phone call"
+ }
+ },
+ {
+ "valueCoding": {
+ "id": "3d42dde0-8e60-4832-bd46-bd06de28cbf2",
+ "code": "text-message",
+ "system": "urn:uuid:736ac230-812a-4f4a-edec-5156910fb6ec",
+ "display": "Text message"
+ }
+ },
+ {
+ "valueCoding": {
+ "id": "e672cfc6-118f-4a2d-aafd-02722ff876b9",
+ "code": "e-mail",
+ "system": "urn:uuid:736ac230-812a-4f4a-edec-5156910fb6ec",
+ "display": "E-mail"
+ }
+ }
+ ]
+ },
+ {
+ "linkId": "c3bea33d-4c50-4f4a-8ae4-1a52be326b19",
+ "type": "string",
+ "text": "What is your phone number? Ex. (555) 555-5555",
+ "required": false,
+ "enableWhen": [
+ {
+ "question": "695525f3-3e89-4455-8e25-878171c596da",
+ "operator": "=",
+ "answerCoding": {
+ "system": "urn:uuid:736ac230-812a-4f4a-edec-5156910fb6ec",
+ "code": "phone-call"
+ }
+ }
+ ],
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/regex",
+ "valueString": "^(\\([0-9]{3}\\) |[0-9]{3}-)[0-9]{3}-[0-9]{4}$"
+ },
+ {
+ "url": "http://biodesign.stanford.edu/fhir/StructureDefinition/validationtext",
+ "valueString": "Please enter a valid phone number."
+ }
+ ]
+ },
+ {
+ "linkId": "8e906a39-5fd0-42a8-f42c-bd96d719dd13",
+ "type": "string",
+ "text": "What is your text number? Ex. (555) 555-5555",
+ "required": false,
+ "enableWhen": [
+ {
+ "question": "695525f3-3e89-4455-8e25-878171c596da",
+ "operator": "=",
+ "answerCoding": {
+ "system": "urn:uuid:736ac230-812a-4f4a-edec-5156910fb6ec",
+ "code": "text-message"
+ }
+ }
+ ],
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/regex",
+ "valueString": "^(\\([0-9]{3}\\) |[0-9]{3}-)[0-9]{3}-[0-9]{4}$"
+ },
+ {
+ "url": "http://biodesign.stanford.edu/fhir/StructureDefinition/validationtext",
+ "valueString": "Please enter a valid phone number."
+ }
+ ]
+ },
+ {
+ "linkId": "86290b0a-017e-4193-8707-dc0c2146f0eb",
+ "type": "string",
+ "text": "What is your e-mail?",
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/regex",
+ "valueString": ".*@.+"
+ },
+ {
+ "url": "http://biodesign.stanford.edu/fhir/StructureDefinition/validationtext",
+ "valueString": "Please enter a valid email"
+ },
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/minLength",
+ "valueInteger": 1
+ }
+ ],
+ "required": false,
+ "maxLength": 50,
+ "enableWhen": [
+ {
+ "question": "695525f3-3e89-4455-8e25-878171c596da",
+ "operator": "=",
+ "answerCoding": {
+ "system": "urn:uuid:736ac230-812a-4f4a-edec-5156910fb6ec",
+ "code": "e-mail"
+ }
+ }
+ ]
+ },
+ {
+ "linkId": "305f5381-2d8b-4b98-bc04-5a39bee2f7ec",
+ "type": "display",
+ "text": "Thank you for taking the survey!",
+ "required": false
+ }
+ ]
+}
diff --git a/TemplateApplication/Resources/SocialSupportQuestionnaire.json.license b/TemplateApplication/Resources/SocialSupportQuestionnaire.json.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Resources/SocialSupportQuestionnaire.json.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Schedule/Bundle+Questionnaire.swift b/TemplateApplication/Schedule/Bundle+Questionnaire.swift
new file mode 100644
index 0000000..bc94d92
--- /dev/null
+++ b/TemplateApplication/Schedule/Bundle+Questionnaire.swift
@@ -0,0 +1,26 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import Foundation
+import SpeziQuestionnaire
+
+
+extension Foundation.Bundle {
+ func questionnaire(withName name: String) -> Questionnaire {
+ guard let resourceURL = self.url(forResource: name, withExtension: "json") else {
+ fatalError("Could not find the questionnaire \"\(name).json\" in the bundle.")
+ }
+
+ do {
+ let resourceData = try Data(contentsOf: resourceURL)
+ return try JSONDecoder().decode(Questionnaire.self, from: resourceData)
+ } catch {
+ fatalError("Could not decode the FHIR questionnaire named \"\(name).json\": \(error)")
+ }
+ }
+}
diff --git a/TemplateApplication/Schedule/EventContext.swift b/TemplateApplication/Schedule/EventContext.swift
new file mode 100644
index 0000000..475bac0
--- /dev/null
+++ b/TemplateApplication/Schedule/EventContext.swift
@@ -0,0 +1,25 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import SpeziScheduler
+
+
+struct EventContext: Comparable, Identifiable {
+ let event: Event
+ let task: Task
+
+
+ var id: Event.ID {
+ event.id
+ }
+
+
+ static func < (lhs: EventContext, rhs: EventContext) -> Bool {
+ lhs.event.scheduledAt < rhs.event.scheduledAt
+ }
+}
diff --git a/TemplateApplication/Schedule/EventContextView.swift b/TemplateApplication/Schedule/EventContextView.swift
new file mode 100644
index 0000000..efcbc96
--- /dev/null
+++ b/TemplateApplication/Schedule/EventContextView.swift
@@ -0,0 +1,81 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import SpeziScheduler
+import SwiftUI
+
+
+struct EventContextView: View {
+ let eventContext: EventContext
+
+
+ var body: some View {
+ HStack {
+ VStack(alignment: .leading) {
+ HStack {
+ if eventContext.event.complete {
+ Image(systemName: "checkmark.circle.fill")
+ .foregroundColor(.accentColor)
+ .font(.system(size: 30))
+ .accessibilityHidden(true)
+ }
+ VStack(alignment: .leading, spacing: 8) {
+ Text(verbatim: eventContext.task.title)
+ .font(.headline)
+ .accessibilityLabel(
+ eventContext.event.complete
+ ? "COMPLETED_TASK_LABEL \(eventContext.task.title)"
+ : "TASK_LABEL \(eventContext.task.title)"
+ )
+ Text(verbatim: format(eventDate: eventContext.event.scheduledAt))
+ .font(.subheadline)
+ }
+ }
+ Divider()
+ Text(eventContext.task.description)
+ .font(.callout)
+ if !eventContext.event.complete {
+ Text(eventContext.task.context.actionType)
+ .frame(maxWidth: .infinity, minHeight: 50)
+ .foregroundColor(.white)
+ .background(Color.accentColor)
+ .clipShape(RoundedRectangle(cornerRadius: 8))
+ .padding(.top, 8)
+ }
+ }
+ }
+ .disabled(eventContext.event.complete)
+ .contentShape(Rectangle())
+ }
+
+
+ private func format(eventDate: Date) -> String {
+ let dateFormatter = DateFormatter()
+ dateFormatter.dateStyle = .none
+ dateFormatter.timeStyle = .short
+ return dateFormatter.string(from: eventDate)
+ }
+}
+
+
+#if DEBUG
+#Preview(traits: .sizeThatFitsLayout) {
+ let task = TemplateApplicationScheduler.socialSupportTask
+
+ return EventContextView(
+ eventContext: EventContext(
+ // We use a force unwrap in the preview as we can not recover from an error here
+ // and the code will never end up in a production environment.
+ // swiftlint:disable:next force_unwrapping
+ event: task.events(from: .now.addingTimeInterval(-60 * 60 * 24)).first!,
+ task: task
+ )
+ )
+ .padding()
+}
+#endif
diff --git a/TemplateApplication/Schedule/ModalView.swift b/TemplateApplication/Schedule/ModalView.swift
new file mode 100644
index 0000000..ba68331
--- /dev/null
+++ b/TemplateApplication/Schedule/ModalView.swift
@@ -0,0 +1,47 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import SpeziViews
+import SwiftUI
+
+
+struct ModalView: View {
+ @Environment(\.dismiss) private var dismiss
+
+ let text: String
+ let buttonText: String
+ let onClose: () async -> Void
+
+
+ var body: some View {
+ VStack {
+ Spacer()
+ Text(text)
+ .padding()
+ Spacer()
+ AsyncButton {
+ self.dismiss()
+ await self.onClose()
+ } label: {
+ Text(buttonText)
+ .frame(maxWidth: .infinity, minHeight: 38)
+ }
+ .padding()
+ .buttonStyle(.borderedProminent)
+ }
+ }
+}
+
+
+#if DEBUG
+#Preview {
+ ModalView(text: "Preview Modal", buttonText: "Close") {
+ print("Preview Modal closed.")
+ }
+}
+#endif
diff --git a/TemplateApplication/Schedule/ScheduleView.swift b/TemplateApplication/Schedule/ScheduleView.swift
new file mode 100644
index 0000000..d79f822
--- /dev/null
+++ b/TemplateApplication/Schedule/ScheduleView.swift
@@ -0,0 +1,131 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import SpeziAccount
+import SpeziQuestionnaire
+import SpeziScheduler
+import SwiftUI
+
+
+struct ScheduleView: View {
+ @Environment(TemplateApplicationStandard.self) private var standard
+ @Environment(TemplateApplicationScheduler.self) private var scheduler
+ @State private var eventContextsByDate: [Date: [EventContext]] = [:]
+ @State private var presentedContext: EventContext?
+
+
+ @Binding private var presentingAccount: Bool
+
+
+ private var startOfDays: [Date] {
+ Array(eventContextsByDate.keys)
+ }
+
+
+ var body: some View {
+ NavigationStack {
+ List(startOfDays, id: \.timeIntervalSinceNow) { startOfDay in
+ Section(format(startOfDay: startOfDay)) {
+ ForEach(eventContextsByDate[startOfDay] ?? [], id: \.event) { eventContext in
+ EventContextView(eventContext: eventContext)
+ .onTapGesture {
+ if !eventContext.event.complete {
+ presentedContext = eventContext
+ }
+ }
+ }
+ }
+ }
+ .onChange(of: scheduler) {
+ calculateEventContextsByDate()
+ }
+ .task {
+ calculateEventContextsByDate()
+ }
+ .sheet(item: $presentedContext) { presentedContext in
+ destination(withContext: presentedContext)
+ }
+ .toolbar {
+ if AccountButton.shouldDisplay {
+ AccountButton(isPresented: $presentingAccount)
+ }
+ }
+ .navigationTitle("SCHEDULE_LIST_TITLE")
+ }
+ }
+
+
+ init(presentingAccount: Binding) {
+ self._presentingAccount = presentingAccount
+ }
+
+
+ private func destination(withContext eventContext: EventContext) -> some View {
+ @ViewBuilder var destination: some View {
+ switch eventContext.task.context {
+ case let .questionnaire(questionnaire):
+ QuestionnaireView(questionnaire: questionnaire) { result in
+ presentedContext = nil
+
+ guard case let .completed(response) = result else {
+ return // user cancelled the task
+ }
+
+ eventContext.event.complete(true)
+ await standard.add(response: response)
+ }
+ case let .test(string):
+ ModalView(text: string, buttonText: String(localized: "CLOSE")) {
+ await eventContext.event.complete(true)
+ }
+ }
+ }
+ return destination
+ }
+
+
+ private func format(startOfDay: Date) -> String {
+ let dateFormatter = DateFormatter()
+ dateFormatter.dateStyle = .long
+ dateFormatter.timeStyle = .none
+ return dateFormatter.string(from: startOfDay)
+ }
+
+ private func calculateEventContextsByDate() {
+ let eventContexts = scheduler.tasks.flatMap { task in
+ task
+ .events(
+ from: Calendar.current.startOfDay(for: .now),
+ to: .numberOfEventsOrEndDate(100, .now)
+ )
+ .map { event in
+ EventContext(event: event, task: task)
+ }
+ }
+ .sorted()
+
+ let newEventContextsByDate = Dictionary(grouping: eventContexts) { eventContext in
+ Calendar.current.startOfDay(for: eventContext.event.scheduledAt)
+ }
+
+ eventContextsByDate = newEventContextsByDate
+ }
+}
+
+
+#if DEBUG
+#Preview("ScheduleView") {
+ ScheduleView(presentingAccount: .constant(false))
+ .previewWith(standard: TemplateApplicationStandard()) {
+ TemplateApplicationScheduler()
+ AccountConfiguration {
+ MockUserIdPasswordAccountService()
+ }
+ }
+}
+#endif
diff --git a/TemplateApplication/Schedule/TemplateApplicationScheduler.swift b/TemplateApplication/Schedule/TemplateApplicationScheduler.swift
new file mode 100644
index 0000000..aa6626f
--- /dev/null
+++ b/TemplateApplication/Schedule/TemplateApplicationScheduler.swift
@@ -0,0 +1,49 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import Foundation
+import SpeziScheduler
+
+
+/// A `Scheduler` using the ``TemplateApplicationTaskContext`` to schedule and manage tasks and events in the
+/// Spezi Template Application.
+typealias TemplateApplicationScheduler = Scheduler
+
+
+extension TemplateApplicationScheduler {
+ static var socialSupportTask: SpeziScheduler.Task {
+ let dateComponents: DateComponents
+ if FeatureFlags.testSchedule {
+ // Adds a task at the current time for UI testing if the `--testSchedule` feature flag is set
+ dateComponents = DateComponents(
+ hour: Calendar.current.component(.hour, from: .now),
+ minute: Calendar.current.component(.minute, from: .now)
+ )
+ } else {
+ // For the normal app usage, we schedule the task for every day at 8:00 AM
+ dateComponents = DateComponents(hour: 8, minute: 0)
+ }
+
+ return Task(
+ title: String(localized: "TASK_SOCIAL_SUPPORT_QUESTIONNAIRE_TITLE"),
+ description: String(localized: "TASK_SOCIAL_SUPPORT_QUESTIONNAIRE_DESCRIPTION"),
+ schedule: Schedule(
+ start: Calendar.current.startOfDay(for: Date()),
+ repetition: .matching(dateComponents),
+ end: .numberOfEvents(365)
+ ),
+ notifications: true,
+ context: TemplateApplicationTaskContext.questionnaire(Bundle.main.questionnaire(withName: "SocialSupportQuestionnaire"))
+ )
+ }
+
+ /// Creates a default instance of the ``TemplateApplicationScheduler`` by scheduling the tasks listed below.
+ convenience init() {
+ self.init(tasks: [Self.socialSupportTask])
+ }
+}
diff --git a/TemplateApplication/Schedule/TemplateApplicationTaskContext.swift b/TemplateApplication/Schedule/TemplateApplicationTaskContext.swift
new file mode 100644
index 0000000..58c2356
--- /dev/null
+++ b/TemplateApplication/Schedule/TemplateApplicationTaskContext.swift
@@ -0,0 +1,40 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import Foundation
+import ModelsR4
+
+
+/// The context attached to each task in the Spezi Template Application.
+///
+/// We currently only support `Questionnaire`s, more cases can be added in the future.
+enum TemplateApplicationTaskContext: Codable, Identifiable {
+ /// The task should display a `Questionnaire`.
+ case questionnaire(Questionnaire)
+ /// The task is used for UI testing.
+ case test(String)
+
+
+ var id: FHIRPrimitive? {
+ switch self {
+ case let .questionnaire(questionnaire):
+ return questionnaire.id
+ case .test:
+ return FHIRPrimitive(FHIRString(UUID().uuidString))
+ }
+ }
+
+ var actionType: LocalizedStringResource {
+ switch self {
+ case .questionnaire:
+ return LocalizedStringResource("TASK_CONTEXT_ACTION_QUESTIONNAIRE")
+ case .test:
+ return LocalizedStringResource("TASK_CONTEXT_ACTION_TEST")
+ }
+ }
+}
diff --git a/TemplateApplication/SharedContext/FeatureFlags.swift b/TemplateApplication/SharedContext/FeatureFlags.swift
new file mode 100644
index 0000000..29e4e56
--- /dev/null
+++ b/TemplateApplication/SharedContext/FeatureFlags.swift
@@ -0,0 +1,26 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+/// A collection of feature flags for the Template Application.
+enum FeatureFlags {
+ /// Skips the onboarding flow to enable easier development of features in the application and to allow UI tests to skip the onboarding flow.
+ static let skipOnboarding = CommandLine.arguments.contains("--skipOnboarding")
+ /// Always show the onboarding when the application is launched. Makes it easy to modify and test the onboarding flow without the need to manually remove the application or reset the simulator.
+ static let showOnboarding = CommandLine.arguments.contains("--showOnboarding")
+ /// Disables the Firebase interactions, including the login/sign-up step and the Firebase Firestore upload.
+ static let disableFirebase = CommandLine.arguments.contains("--disableFirebase")
+ #if targetEnvironment(simulator)
+ /// Defines if the application should connect to the local firebase emulator. Always set to true when using the iOS simulator.
+ static let useFirebaseEmulator = true
+ #else
+ /// Defines if the application should connect to the local firebase emulator. Always set to true when using the iOS simulator.
+ static let useFirebaseEmulator = CommandLine.arguments.contains("--useFirebaseEmulator")
+ #endif
+ /// Adds a test task to the schedule at the current time
+ static let testSchedule = CommandLine.arguments.contains("--testSchedule")
+}
diff --git a/TemplateApplication/SharedContext/StorageKeys.swift b/TemplateApplication/SharedContext/StorageKeys.swift
new file mode 100644
index 0000000..3136734
--- /dev/null
+++ b/TemplateApplication/SharedContext/StorageKeys.swift
@@ -0,0 +1,21 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+/// Constants shared across the Spezi Teamplate Application to access storage information including the `AppStorage` and `SceneStorage`
+enum StorageKeys {
+ // MARK: - Onboarding
+ /// A `Bool` flag indicating of the onboarding was completed.
+ static let onboardingFlowComplete = "onboardingFlow.complete"
+ /// A `Step` flag indicating the current step in the onboarding process.
+ static let onboardingFlowStep = "onboardingFlow.step"
+
+
+ // MARK: - Home
+ /// The currently selected home tab.
+ static let homeTabSelection = "home.tabselection"
+}
diff --git a/TemplateApplication/Supporting Files/GoogleService-Info.plist b/TemplateApplication/Supporting Files/GoogleService-Info.plist
new file mode 100644
index 0000000..f7699ba
--- /dev/null
+++ b/TemplateApplication/Supporting Files/GoogleService-Info.plist
@@ -0,0 +1,34 @@
+
+
+
+
+ CLIENT_ID
+ CLIENT_ID
+ REVERSED_CLIENT_ID
+ REVERSED_CLIENT_ID
+ API_KEY
+ API_KEY
+ GCM_SENDER_ID
+ GCM_SENDER_ID
+ PLIST_VERSION
+ 1
+ BUNDLE_ID
+ edu.stanford.spezi.templateapplication
+ PROJECT_ID
+ stanfordspezitemplateapp
+ STORAGE_BUCKET
+ STORAGE_BUCKET
+ IS_ADS_ENABLED
+
+ IS_ANALYTICS_ENABLED
+
+ IS_APPINVITE_ENABLED
+
+ IS_GCM_ENABLED
+
+ IS_SIGNIN_ENABLED
+
+ GOOGLE_APP_ID
+ 1:123456789012:ios:1234567890123456789012
+
+
diff --git a/TemplateApplication/Supporting Files/GoogleService-Info.plist.license b/TemplateApplication/Supporting Files/GoogleService-Info.plist.license
new file mode 100644
index 0000000..9f5551c
--- /dev/null
+++ b/TemplateApplication/Supporting Files/GoogleService-Info.plist.license
@@ -0,0 +1,5 @@
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2022 Stanford University and the project authors (see CONTRIBUTORS.md)
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/Info.plist b/TemplateApplication/Supporting Files/Info.plist
new file mode 100644
index 0000000..5dc3321
--- /dev/null
+++ b/TemplateApplication/Supporting Files/Info.plist
@@ -0,0 +1,17 @@
+
+
+
+
+ ITSAppUsesNonExemptEncryption
+
+ UIApplicationSceneManifest
+
+ UIApplicationSupportsMultipleScenes
+
+ UISceneConfigurations
+
+
+ CFBundleAllowMixedLocalizations
+
+
+
diff --git a/TemplateApplication/Supporting Files/Info.plist.license b/TemplateApplication/Supporting Files/Info.plist.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/Info.plist.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Create.md b/TemplateApplication/Supporting Files/TemplateApplication.docc/Create.md
new file mode 100644
index 0000000..a0b4da9
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Create.md
@@ -0,0 +1,55 @@
+# Create Your Spezi-based Application
+
+
+
+How to create your own Spezi-based application based on the Spezi Template Application.
+
+
+## 1. Create Your Own Repository
+
+You can create your own Spezi-based application by creating a new GitHub repo and [using the Stanford Spezi Template Application as a template](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-repository-from-a-template).
+
+> Tip: Spezi is completely independent of the Spezi Template Application or any other setup. You can always import one or more Spezi modules in any Swift and SwiftUI-based application.
+
+
+## 2. Change The Name and Key Information
+
+The Spezi Template application repository provides a convenient script to rename all aspects of the Spezi Template Application to your application name, update the bundle identifier, and remove unused documentation and files that are not needed for your own project.
+
+The shell script can be called as follows:
+```bash
+$ sh Scripts/create.sh --name --bundleIdentifier [--provisioningProfile ]
+```
+
+Argument | Description
+--- | ---
+`--name` | Name of the application. (required)
+`--bundleIdentifier` | The iOS bundle identifier of the application. (required)
+`--provisioningProfile` | The name of the iOS provisioning profile to build the application. (optional, defaults to the value of --name).
+`--help` | Display help and exit.
+
+The following example shows renaming the application to "My Spezi App":
+
+```bash
+$ sh Scripts/create.sh --name "My Spezi App" --bundleIdentifier "edu.stanford.spezi.myapp"
+```
+
+## 3. Setup the Continous Integration and Delivery Setup
+
+Continuous integration (CI) and continuous delivery (CD) are essential to automatically test and deploy your application at any time.
+Each Spezi Template Application-based Spezi app already has the necessary infrastructure in place; the Spezi Template Application includes continuous integration (CI) and continuous delivery (CD) setup:
+- Automatically build and test the application on every pull request before deploying it. Suppose your organization doesn't have a self-hosted macOS runner modeled after the setup in the [StanfordBDHG ContinuousIntegration](https://github.com/StanfordBDHG/ContinousIntegration) setup. In that case, you will need to remove the `runsonlabels` arguments in the `build-and-test.yml` file to ensure that the build runs on the default macOS runners provided by GitHub.
+- An automated setup to deploy the application to TestFlight every time there is a new commit on the repository's main branch. You will need to provide the provisioning profile and other GitHub secrets to make them available to the GitHub Action.
+- Ensure a coherent code style by checking the conformance to the SwiftLint rules defined in `.swiftlint.yml` on every pull request and commit.
+- Ensure conformance to the [REUSE Specification]() to property license the application and all related code.
+- Deploy documentation of the application to GitHub pages with every commit to the main branch.
+
+Please refer to the [Stanford Biodesign Digital Health Template Application](https://github.com/StanfordBDHG/TemplateApplication) and the [ContinuousDelivery Example by Paul Schmiedmayer](https://github.com/PSchmiedmayer/ContinousDelivery) for more background about the CI and CD setup for the Spezi Template Application.
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Modify.md b/TemplateApplication/Supporting Files/TemplateApplication.docc/Modify.md
new file mode 100644
index 0000000..a485ca3
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Modify.md
@@ -0,0 +1,105 @@
+# Start Development of Your Spezi-based Application
+
+
+
+Overview of the different parts of the Spezi Template Application-based Spezi app and how to modify them to your needs.
+
+> Important: Please first follow the instructions on how to install all the necessary software to build, run, and modify the application () and how to create your own Spezi-based application based on the Spezi Template Application ().
+
+
+## Onboarding Flow
+
+The onboarding contains different steps.
+It uses the [**Spezi Onboarding** module](https://github.com/StanfordSpezi/SpeziOnboarding) to display different onboarding-related views like the information about the application, a consent screen, and a screen to display a HealthKit consent view.
+
+@Row(numberOfColumns: 4) {
+ @Column(size: 1) {
+ @Image(source: "Welcome", alt: "A screen displaying welcome information.") {
+ You can find and modify the welcome messages in the ``Welcome`` view.
+ }
+ }
+ @Column(size: 1) {
+ @Image(source: "InterestingModules", alt: "A screen showing an overview of the modules used in the Spezi Template Application.") {
+ You can find and modify the sequential onboarding information in the ``InterestingModules`` view.
+ }
+ }
+ @Column(size: 1) {
+ @Image(source: "Consent", alt: "A screen displaying the consent view.") {
+ You can find and modify the consent setup and surrounding user interface in the ``Consent`` view.
+ }
+ }
+}
+
+The application also automatically pulls and processes HealthKit data types that are defined in the ``TemplateApplicationDelegate`` using the [**Spezi HealthKit** module](https://github.com/StanfordSpezi/SpeziHealthKit).
+
+@Row(numberOfColumns: 4) {
+ @Column(size: 1) {
+ @Image(source: "HealthKitAccess", alt: "HealthKit Onboarding Flow") {
+ You can find and modify the HealthKit onboarding flow in the ``HealthKitPermissions`` view.
+ }
+ }
+ @Column(size: 1) {
+ @Image(source: "HealthKitSheet", alt: "Permissions screen of the HealthKit framework") {
+ You can define which elements should be pulled from HealthKit in the ``TemplateApplicationDelegate``.
+ }
+ }
+}
+
+## Schedule & Questionnaires
+
+The scheduler part of the application provides the functionality to schedule a recurring task and bind it to an action, e.g., displaying a questionnaire.
+It uses the [**Spezi Scheduler**](https://github.com/StanfordSpezi/SpeziScheduler) and [**Spezi Questionnaire**](https://github.com/StanfordSpezi/SpeziQuestionnaire) modules to schedule the tasks as defined in the `TemplateApplicationScheduler`.
+
+@Row(numberOfColumns: 4) {
+ @Column(size: 1) {
+ @Image(source: "Schedule", alt: "A screen displaying the Scheduler UI.") {
+ The elements that are displayed as part of a schedule are defined in the ``TemplateApplicationTaskContext`` and displayed using the ``EventContextView`` and logic in the ``ScheduleView``.
+ }
+ }
+ @Column(size: 1) {
+ @Image(source: "Notifications", alt: "Onboarding screen showing the Notifications permission screen.") {
+ You can find and modify the scheduled tasks, including local notifications, by changing the configuration and setup in the ``TemplateApplicationScheduler``.
+ }
+ }
+ @Column(size: 1) {
+ @Image(source: "Questionnaire", alt: "A screen showing a questionnaire using ResearchKit.") {
+ The questionnaire content is defined using the FHIR questionnaire information found in the `Resources` folder and defined by the ``TemplateApplicationScheduler``.
+ }
+ }
+ @Column(size: 1) {
+ @Image(source: "ScheduleComplete", alt: "The scheduler screen showing the completed UI") {
+ The [**Spezi Scheduler**](https://github.com/StanfordSpezi/SpeziScheduler) module keeps track of the completion state and due dates of tasks and events.
+ }
+ }
+}
+
+
+## Additional Application
+
+The [**Spezi Contacts** module](https://github.com/StanfordSpezi/SpeziContact) uses the contact-related views provided by Spezi.
+
+@Row(numberOfColumns: 4) {
+ @Column(size: 1) {
+ @Image(source: "Contacts", alt: "A screen displaying the Contact UI.") {
+ You can find and modify the contact information in the ``Contacts`` view.
+ }
+ }
+ @Column(size: 1) {
+ @Image(source: "License", alt: "License information to list all used Swift Packages") {
+ You can investigate the ``ContributionsList`` to learn how the application loads and displays the license information.
+ }
+ }
+ @Column(size: 1) {
+ @Image(source: "Request", alt: "User Interface of the Mock Web Service") {
+ If Firebase is disabled, the Mock Web Service allows you to see the requests that would be sent to a web service.
+ }
+ }
+}
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/Account.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/Account.png
new file mode 100644
index 0000000..a607eeb
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/Account.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/Account.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/Account.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/Account.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/Account~dark.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/Account~dark.png
new file mode 100644
index 0000000..36d9519
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/Account~dark.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/Account~dark.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/Account~dark.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/Account~dark.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/Login.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/Login.png
new file mode 100644
index 0000000..4c240db
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/Login.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/Login.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/Login.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/Login.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/Login~dark.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/Login~dark.png
new file mode 100644
index 0000000..e24cfe1
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/Login~dark.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/Login~dark.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/Login~dark.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/Login~dark.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/NewAccount.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/NewAccount.png
new file mode 100644
index 0000000..5d14995
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/NewAccount.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/NewAccount.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/NewAccount.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/NewAccount.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/NewAccount~dark.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/NewAccount~dark.png
new file mode 100644
index 0000000..7b8b784
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/NewAccount~dark.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/NewAccount~dark.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/NewAccount~dark.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Account/NewAccount~dark.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/Contacts.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/Contacts.png
new file mode 100644
index 0000000..ae7581f
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/Contacts.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/Contacts.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/Contacts.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/Contacts.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/Contacts~dark.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/Contacts~dark.png
new file mode 100644
index 0000000..a1ebd71
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/Contacts~dark.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/Contacts~dark.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/Contacts~dark.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/Contacts~dark.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/License.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/License.png
new file mode 100644
index 0000000..d0b1854
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/License.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/License.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/License.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/License.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/License~dark.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/License~dark.png
new file mode 100644
index 0000000..c1b8f78
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/License~dark.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/License~dark.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/License~dark.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/License~dark.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/Request.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/Request.png
new file mode 100644
index 0000000..e1ec526
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/Request.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/Request.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/Request.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/Request.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/Request~dark.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/Request~dark.png
new file mode 100644
index 0000000..d11e5b0
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/Request~dark.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/Request~dark.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/Request~dark.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Context/Request~dark.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/Consent.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/Consent.png
new file mode 100644
index 0000000..f36de82
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/Consent.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/Consent.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/Consent.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/Consent.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/Consent~dark.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/Consent~dark.png
new file mode 100644
index 0000000..7053c90
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/Consent~dark.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/Consent~dark.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/Consent~dark.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/Consent~dark.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/HealthKitAccess.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/HealthKitAccess.png
new file mode 100644
index 0000000..9649ca2
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/HealthKitAccess.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/HealthKitAccess.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/HealthKitAccess.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/HealthKitAccess.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/HealthKitAccess~dark.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/HealthKitAccess~dark.png
new file mode 100644
index 0000000..52d2f36
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/HealthKitAccess~dark.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/HealthKitAccess~dark.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/HealthKitAccess~dark.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/HealthKitAccess~dark.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/HealthKitSheet.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/HealthKitSheet.png
new file mode 100644
index 0000000..11aa55f
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/HealthKitSheet.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/HealthKitSheet.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/HealthKitSheet.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/HealthKitSheet.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/HealthKitSheet~dark.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/HealthKitSheet~dark.png
new file mode 100644
index 0000000..c0c62f3
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/HealthKitSheet~dark.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/HealthKitSheet~dark.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/HealthKitSheet~dark.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/HealthKitSheet~dark.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/InterestingModules.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/InterestingModules.png
new file mode 100644
index 0000000..88307a4
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/InterestingModules.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/InterestingModules.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/InterestingModules.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/InterestingModules.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/InterestingModules~dark.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/InterestingModules~dark.png
new file mode 100644
index 0000000..c1206ba
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/InterestingModules~dark.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/InterestingModules~dark.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/InterestingModules~dark.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/InterestingModules~dark.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/Welcome.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/Welcome.png
new file mode 100644
index 0000000..25bbe2a
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/Welcome.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/Welcome.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/Welcome.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/Welcome.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/Welcome~dark.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/Welcome~dark.png
new file mode 100644
index 0000000..222a72d
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/Welcome~dark.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/Welcome~dark.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/Welcome~dark.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Onboarding/Welcome~dark.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Notifications.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Notifications.png
new file mode 100644
index 0000000..104e2b6
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Notifications.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Notifications.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Notifications.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Notifications.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Notifications~dark.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Notifications~dark.png
new file mode 100644
index 0000000..5a21b21
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Notifications~dark.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Notifications~dark.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Notifications~dark.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Notifications~dark.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Questionnaire.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Questionnaire.png
new file mode 100644
index 0000000..d0be49d
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Questionnaire.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Questionnaire.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Questionnaire.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Questionnaire.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Questionnaire~dark.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Questionnaire~dark.png
new file mode 100644
index 0000000..db8dfe2
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Questionnaire~dark.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Questionnaire~dark.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Questionnaire~dark.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Questionnaire~dark.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Schedule.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Schedule.png
new file mode 100644
index 0000000..39fa9b9
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Schedule.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Schedule.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Schedule.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Schedule.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/ScheduleComplete.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/ScheduleComplete.png
new file mode 100644
index 0000000..5340701
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/ScheduleComplete.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/ScheduleComplete.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/ScheduleComplete.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/ScheduleComplete.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/ScheduleComplete~dark.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/ScheduleComplete~dark.png
new file mode 100644
index 0000000..4a81661
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/ScheduleComplete~dark.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/ScheduleComplete~dark.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/ScheduleComplete~dark.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/ScheduleComplete~dark.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Schedule~dark.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Schedule~dark.png
new file mode 100644
index 0000000..ba1e669
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Schedule~dark.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Schedule~dark.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Schedule~dark.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Schedule/Schedule~dark.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/FirebaseCLI.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/FirebaseCLI.png
new file mode 100644
index 0000000..2c1a82b
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/FirebaseCLI.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/FirebaseCLI.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/FirebaseCLI.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/FirebaseCLI.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/FirebaseCLI~dark.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/FirebaseCLI~dark.png
new file mode 100644
index 0000000..02c7dcf
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/FirebaseCLI~dark.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/FirebaseCLI~dark.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/FirebaseCLI~dark.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/FirebaseCLI~dark.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/FirebaseWeb.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/FirebaseWeb.png
new file mode 100644
index 0000000..8ed1663
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/FirebaseWeb.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/FirebaseWeb.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/FirebaseWeb.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/FirebaseWeb.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/FirebaseWeb~dark.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/FirebaseWeb~dark.png
new file mode 100644
index 0000000..8ab43dd
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/FirebaseWeb~dark.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/FirebaseWeb~dark.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/FirebaseWeb~dark.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/FirebaseWeb~dark.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Run.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Run.png
new file mode 100644
index 0000000..a1dbb28
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Run.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Run.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Run.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Run.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Run~dark.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Run~dark.png
new file mode 100644
index 0000000..7874d1e
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Run~dark.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Run~dark.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Run~dark.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Run~dark.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Scheme.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Scheme.png
new file mode 100644
index 0000000..36fbc09
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Scheme.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Scheme.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Scheme.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Scheme.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Scheme~dark.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Scheme~dark.png
new file mode 100644
index 0000000..8354e92
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Scheme~dark.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Scheme~dark.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Scheme~dark.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Scheme~dark.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Xcode.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Xcode.png
new file mode 100644
index 0000000..8f881e8
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Xcode.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Xcode.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Xcode.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Xcode.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Xcode~dark.png b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Xcode~dark.png
new file mode 100644
index 0000000..9ccde1f
Binary files /dev/null and b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Xcode~dark.png differ
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Xcode~dark.png.license b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Xcode~dark.png.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Resources/Setup/Xcode~dark.png.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/Setup.md b/TemplateApplication/Supporting Files/TemplateApplication.docc/Setup.md
new file mode 100644
index 0000000..2c70108
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/Setup.md
@@ -0,0 +1,150 @@
+# Build And Run a Spezi Template Application-based Application
+
+
+
+How to install all the nescessary software to build, run, and modify your Spezi Template Application-base Spezi app.
+
+## 1. Install Xcode
+
+Applications for the Apple ecosystem are written in the [Swift programming language](https://swift.org).
+The framework for developing the user interface for mobile applications in Swift is called [SwiftUI](https://developer.apple.com/xcode/swiftui/).
+You will need access to a macOS-based machine to build and run the Swift-based Spezi Template Application.
+
+Please ensure that your Mac meets the following criteria and that you install or update the software on your Mac accordingly.
+
+
+### macOS - Sonoma 14.2 Or Newer
+
+The Mac needs to run macOS Sonoma 14.2 or newer. Please [update to the latest operating system version following the Apple-provided instructions](https://support.apple.com/en-us/HT201541).
+
+You can verify that you run the latest macOS version by clicking on the Apple Logo on the top left of your screen and selecting "About this Mac". You can see the macOS version number in the specs list under your Mac picture.
+
+
+### Xcode - 15.2 Or Newer
+
+Xcode is the integrated development environment (IDE) that is required to build and run Swift-based iOS applications.
+You need to have Xcode 15.2 or later installed.
+[You can install Xcode using the Mac AppStore](https://apps.apple.com/us/app/xcode/id497799835).
+
+Please open Xcode and follow the instructions to finish the installation.
+
+You can verify that you run the latest version of Xcode and everything is installed if you can see the "Welcome to Xcode" screen when you open Xcode, showing 15.2 or newer as the version number.
+
+@Image(source: "Xcode", alt: "Screenshot showing the Welcome to Xcode window.")
+
+You can learn more about Xcode, including [creating an Xcode project for an app](https://developer.apple.com/documentation/xcode/creating-an-xcode-project-for-an-app), information about the IDE interface by following the instructions on [creating your app's interface with SwiftUI](https://developer.apple.com/documentation/xcode/creating-your-app-s-interface-with-swiftui) & [Previewing your app's interface in Xcode](https://developer.apple.com/documentation/xcode/previewing-your-apps-interface-in-xcode).
+
+
+## 2. Install Helper Tools
+
+The Spezi Template Application and applications derived from it provide a pre-configured setup of integrated tools that make the development of applications easier and enforce best practices during the development of your application.
+
+We provide a simple setup script that installs essential tools like [homebrew](https://brew.sh) (macOS package manager), [swiftlint](https://github.com/realm/SwiftLint) (automated checking of your source code for programmatic and stylistic errors), [git LFS](https://git-lfs.com) (Git extension for versioning large files), and [swift-package-list](https://github.com/FelixHerrmann/swift-package-list) (automatically give credit to all dependencies in the application user interface).
+The script also installs the [Google Firebase emulator and command line interface (CLI)](https://firebase.google.com/docs/cli), including java, node, and other dependencies needed to execute the Firebase emulator to allow you to locally test your application's future cloud connection without setting up a cloud firebase project.
+
+You can simply run the script by opening up your macOS [Terminal](https://support.apple.com/guide/terminal/open-or-quit-terminal-apd5265185d-f365-44cb-8b09-71a064a42125/mac) and executing the following command:
+```bash
+$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/StanfordSpezi/SpeziTemplateApplication/HEAD/Scripts/setup.sh)"
+```
+
+> Tip: If you don't feel comfortable running the setup script, you can [inspect the script yourself](https://raw.githubusercontent.com/StanfordSpezi/SpeziTemplateApplication/HEAD/Scripts/setup.sh) and use the commands in the script to install the required software yourself selectively.
+
+
+## 3. Run The Application
+
+[Google Firebase](https://firebase.google.com) is a set of backend cloud computing services and application development platforms provided by Google.
+It hosts databases, services, authentication, and integration for a variety of applications, including mobile applications like the Spezi Template Application.
+Stanford provides its version of a Firebase instance in the form of the [Stanford mHealth platform](https://med.stanford.edu/mhealth.html).
+We use Firebase and the mHealt platform as the default integrated cloud provided in the Spezi Template Application.
+
+> Tip: Spezi itself is independent of any cloud provider or platform! Spezi offers different modules to connect to cloud providers, including [Spezi Firebase](https://github.com/StanfordSpezi/SpeziFirebase), which is the cloud provider demonstrated in the Spezi Template Application.
+
+
+### Alternative A: Get Started without Firebase
+
+You can start using the application without a cloud connection if you enable the `--disableFirebase` feature flag, enabled by default when opening the Xcode project.
+
+The application includes feature flags that can be configured in the [scheme editor in Xcode](https://help.apple.com/xcode/mac/11.4/index.html?localePath=en.lproj#/dev0bee46f46) and selecting your application scheme (default **TemplateApplication**), the **Run** configuration, and to switch to the **Arguments** tab to add, enable, disable, or remove arguments passed on launch.
+
+@Image(source: "Scheme", alt: "Screenshot showing the application scheme Run configuration's launch arguments.")
+
+The login and account setup is skipped in this configuration.
+
+
+### Alternative B: Firebase Setup
+
+The application also provides a [Firebase Firestore](https://firebase.google.com/docs/firestore)-based data upload and [Firebase Authentication](https://firebase.google.com/docs/auth) login & sign-up.
+It is required to have the [Firebase Emulator Suite](https://firebase.google.com/docs/emulator-suite) to be up and running to use these features to build and test the application locally.
+The setup script described above installs the [Firebase Emulator Suite](https://firebase.google.com/docs/emulator-suite).
+
+> Important: You do not have to make any modifications to the Firebase configuration, log into the `Firebase` CLI using your Google account, or create a project in Firebase to run, build, and test the application!
+
+Navigate to the root folder of this setup containing your **.xcodeproj** file ([using `cd` in your terminal](https://tutorials.codebar.io/command-line/introduction/tutorial.html)) and startup the [Firebase Emulator Suite](https://firebase.google.com/docs/emulator-suite) in your [Terminal](https://support.apple.com/guide/terminal/open-or-quit-terminal-apd5265185d-f365-44cb-8b09-71a064a42125/mac) using
+```bash
+$ firebase emulators:start
+```
+
+@Image(source: "FirebaseCLI", alt: "Screenshot showing the terminal and the running Firebase Emulators.")
+
+After the emulators have started up, you can run the application in your simulator to build, test, and run the application and see the results show up in Firebase.
+
+@Image(source: "FirebaseWeb", alt: "Screenshot showing Safari and the Firebase Emulators web interface.")
+
+
+### Run the App
+
+You can build and run the application using [Xcode](https://developer.apple.com/xcode/) by opening up the **.xcodeproj** file in the root of the repository.
+
+You can follow the Apple Documentation on [Building and running an app](https://developer.apple.com/documentation/xcode/building-and-running-an-app) to run the application in the iOS simulator right on your Mac.
+
+@Row(numberOfColumns: 4) {
+ @Column(size: 3) {
+ @Image(source: "Run", alt: "Press the run button in the upper left corner to run the app.") {
+ Press the run button in the upper left corner to run the app.
+ }
+ }
+ @Column {
+ @Image(source: "Welcome", alt: "The Spezi Template Application running in the iOS Simulator.") {
+ The Spezi Template Application running in the iOS Simulator.
+ }
+ }
+}
+
+## 4. Modify The Application
+
+> Tip: You can learn more about changing up the code if the application and customizing your Spezi Template Application-based app in the article.
+
+
+### Firebase Cloud Setup
+
+If you want to connect your project to a development or production Firebase cloud project, you can provide your [`GoogleService-Info.plist`](https://firebase.google.com/docs/ios/setup) in a base 64 representation in the [GitHub secrets](https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions) (`GOOGLE_SERVICE_INFO_PLIST_BASE64`) of your project where it is picked up and loaded in the configured path setup in the [`beta-deployment.yml`] [GitHub Action](https://docs.github.com/en/actions) using the `googleserviceinfoplistpath` parameter that needs to be adapted to your project structure.
+
+You can generate a base 64 representation of a file after you [navigated into the folder](https://en.wikipedia.org/wiki/Cd_(command)#Usage) where you have downloaded your [`GoogleService-Info.plist`](https://firebase.google.com/docs/ios/setup) file to.
+```shell
+base64 -i GoogleService-Info.plist
+```
+
+> Warning: We do **not recommend** to commit your Firebase secrets and configuration file to your project. While one can extract the file from the deployed application, we encourage open-source projects to make it clear to contributors to set up their own Firebase project if they plan to build and deploy a version of an open-source project.
+
+The deployment requires you to store your Google service account JSON credentials in a base 64 representation in the `GOOGLE_APPLICATION_CREDENTIALS_BASE64`. You can learn more about how to generate the JSON in the [Firebase documentation](https://firebase.google.com/docs/app-distribution/authenticate-service-account). The service account must have the minimally required permissions (not the `Firebase App Distribution Admin` role) as documented at https://firebase.google.com/docs/projects/iam/roles-predefined for your deployment needs and setup.
+
+Be sure to update your `.firebaserc` project name and placeholder `GoogleService-Info.plist` project identifier to always reflect the name of your project and all security rules to reflect any changes in your application.
+
+
+### Other Configuration Options
+
+The application also includes the following feature flags that can be configured in the [scheme editor in Xcode](https://help.apple.com/xcode/mac/11.4/index.html?localePath=en.lproj#/dev0bee46f46) and selecting your scheme, the **Run** configuration, and to switch to the **Arguments** tab to add, enable, disable, or remove the following arguments passed on launch:
+- `--skipOnboarding`: Skips the onboarding flow to enable easier development of features in the application and to allow UI tests to skip the onboarding flow.
+- `--showOnboarding`: Always show the onboarding when the application is launched. Makes it easy to modify and test the onboarding flow without the need to manually remove the application or reset the simulator.
+- `--disableFirebase`: Disables the Firebase interactions, including the login/sign-up step and the Firebase Firestore upload.
+- `--useFirebaseEmulator`: Defines if the application should connect to the local Firebase emulator. Always set to true when using the iOS simulator.
+
+> Tip: You can learn how to add, modify, and remove feature flags that are passed to the application when it is started in the [Customizing the build schemes for a project](https://developer.apple.com/documentation/xcode/customizing-the-build-schemes-for-a-project#Specify-launch-arguments-and-environment-variables) tutorial in the [*Specify launch arguments and environment variables* section](https://developer.apple.com/documentation/xcode/customizing-the-build-schemes-for-a-project#Specify-launch-arguments-and-environment-variables).
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.docc/TemplateApplication.md b/TemplateApplication/Supporting Files/TemplateApplication.docc/TemplateApplication.md
new file mode 100644
index 0000000..99ffbad
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.docc/TemplateApplication.md
@@ -0,0 +1,92 @@
+# ``TemplateApplication``
+
+
+
+Template to provide a starting point for Spezi-based applications.
+
+## Overview
+
+The Spezi Template Application demonstrates using the [Spezi](https://github.com/StanfordSpezi/Spezi) ecosystem and builds on top of the [Stanford Biodesign Digital Health Template Application](https://github.com/StanfordBDHG/TemplateApplication).
+
+> Tip: Do you want to try out the Spezi Template Application? You can download it to your iOS device using [TestFlight](https://testflight.apple.com/join/ipEezBY1)!
+
+The following screenshots show a wide variety of features based on Spezi Modules that are part of the Spezi Template Application.
+
+@Row(numberOfColumns: 3) {
+ @Column(size: 1) {
+ @Image(source: "Welcome", alt: "A screen displaying welcome information.") {
+ Welcome View.
+ }
+ }
+ @Column(size: 1) {
+ @Image(source: "InterestingModules", alt: "A screen showing an overview of the modules used in the Spezi Template Application.") {
+ Interesting Modules
+ }
+ }
+ @Column(size: 1) {
+ @Image(source: "Consent", alt: "A screen displaying the consent view.") {
+ Consent Signature.
+ }
+ }
+}
+@Row(numberOfColumns: 3) {
+ @Column(size: 1) {
+ @Image(source: "HealthKitAccess", alt: "HealthKit Onboarding Flow") {
+ HealthKit Access.
+ }
+ }
+ @Column(size: 1) {
+ @Image(source: "HealthKitSheet", alt: "Permissions screen of the HealthKit framework") {
+ Granular HealthKit Share Control.
+ }
+ }
+ @Column(size: 1) {
+ @Image(source: "Schedule", alt: "A screen displaying the Scheduler UI.") {
+ Schedule Tasks.
+ }
+ }
+}
+@Row(numberOfColumns: 3) {
+ @Column(size: 1) {
+ @Image(source: "Notifications", alt: "Onboarding screen showing the Notifications permission screen.") {
+ Trigger Local Notifications.
+ }
+ }
+ @Column(size: 1) {
+ @Image(source: "Questionnaire", alt: "A screen showing a questionnaire using ResearchKit.") {
+ Display Questionnaires.
+ }
+ }
+ @Column(size: 1) {
+ @Image(source: "ScheduleComplete", alt: "The scheduler screen showing the completed UI") {
+ Keep Track of Tasks.
+ }
+ }
+}
+@Row(numberOfColumns: 3) {
+ @Column(size: 1) {
+ @Image(source: "Contacts", alt: "A screen displaying the Contact UI.") {
+ Contact Information.
+ }
+ }
+ @Column(size: 1) {
+ @Image(source: "License", alt: "License information to list all used Swift Packages") {
+ License Information.
+ }
+ }
+ @Column(size: 1) {
+ @Image(source: "Request", alt: "User Interface of the Mock Web Service") {
+ Mock Web Service Requests.
+ }
+ }
+}
+
+> Tip: You can find all the used Spezi Modules in the [Stanford Spezi GitHub Organization](https://github.com/StanfordSpezi).
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.entitlements b/TemplateApplication/Supporting Files/TemplateApplication.entitlements
new file mode 100644
index 0000000..c541ce6
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.entitlements
@@ -0,0 +1,16 @@
+
+
+
+
+ com.apple.developer.applesignin
+
+ Default
+
+ com.apple.developer.healthkit
+
+ com.apple.developer.healthkit.access
+
+ com.apple.developer.healthkit.background-delivery
+
+
+
diff --git a/TemplateApplication/Supporting Files/TemplateApplication.entitlements.license b/TemplateApplication/Supporting Files/TemplateApplication.entitlements.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/TemplateApplication/Supporting Files/TemplateApplication.entitlements.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/TemplateApplication/TemplateApplication.swift b/TemplateApplication/TemplateApplication.swift
new file mode 100644
index 0000000..301a6e7
--- /dev/null
+++ b/TemplateApplication/TemplateApplication.swift
@@ -0,0 +1,36 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import Spezi
+import SpeziFirebaseAccount
+import SwiftUI
+
+
+@main
+struct TemplateApplication: App {
+ @UIApplicationDelegateAdaptor(TemplateApplicationDelegate.self) var appDelegate
+ @AppStorage(StorageKeys.onboardingFlowComplete) var completedOnboardingFlow = false
+
+
+ var body: some Scene {
+ WindowGroup {
+ ZStack {
+ if completedOnboardingFlow {
+ HomeView()
+ } else {
+ EmptyView()
+ }
+ }
+ .sheet(isPresented: !$completedOnboardingFlow) {
+ OnboardingFlow()
+ }
+ .testingSetup()
+ .spezi(appDelegate)
+ }
+ }
+}
diff --git a/TemplateApplication/TemplateApplicationDelegate.swift b/TemplateApplication/TemplateApplicationDelegate.swift
new file mode 100644
index 0000000..0862aa9
--- /dev/null
+++ b/TemplateApplication/TemplateApplicationDelegate.swift
@@ -0,0 +1,84 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import Spezi
+import SpeziAccount
+import SpeziFirebaseAccount
+import SpeziFirebaseStorage
+import SpeziFirestore
+import SpeziHealthKit
+import SpeziMockWebService
+import SpeziOnboarding
+import SpeziScheduler
+import SwiftUI
+
+
+class TemplateApplicationDelegate: SpeziAppDelegate {
+ override var configuration: Configuration {
+ Configuration(standard: TemplateApplicationStandard()) {
+ if !FeatureFlags.disableFirebase {
+ AccountConfiguration(configuration: [
+ .requires(\.userId),
+ .requires(\.name),
+
+ // additional values stored using the `FirestoreAccountStorage` within our Standard implementation
+ .collects(\.genderIdentity),
+ .collects(\.dateOfBirth)
+ ])
+
+ if FeatureFlags.useFirebaseEmulator {
+ FirebaseAccountConfiguration(
+ authenticationMethods: [.emailAndPassword, .signInWithApple],
+ emulatorSettings: (host: "localhost", port: 9099)
+ )
+ } else {
+ FirebaseAccountConfiguration(authenticationMethods: [.emailAndPassword, .signInWithApple])
+ }
+ firestore
+ if FeatureFlags.useFirebaseEmulator {
+ FirebaseStorageConfiguration(emulatorSettings: (host: "localhost", port: 9199))
+ } else {
+ FirebaseStorageConfiguration()
+ }
+ } else {
+ MockWebService()
+ }
+
+ if HKHealthStore.isHealthDataAvailable() {
+ healthKit
+ }
+
+ TemplateApplicationScheduler()
+ OnboardingDataSource()
+ }
+ }
+
+
+ private var firestore: Firestore {
+ let settings = FirestoreSettings()
+ if FeatureFlags.useFirebaseEmulator {
+ settings.host = "localhost:8080"
+ settings.cacheSettings = MemoryCacheSettings()
+ settings.isSSLEnabled = false
+ }
+
+ return Firestore(
+ settings: settings
+ )
+ }
+
+
+ private var healthKit: HealthKit {
+ HealthKit {
+ CollectSample(
+ HKQuantityType(.stepCount),
+ deliverySetting: .anchorQuery(.afterAuthorizationAndApplicationWillLaunch)
+ )
+ }
+ }
+}
diff --git a/TemplateApplication/TemplateApplicationStandard.swift b/TemplateApplication/TemplateApplicationStandard.swift
new file mode 100644
index 0000000..cd6fe37
--- /dev/null
+++ b/TemplateApplication/TemplateApplicationStandard.swift
@@ -0,0 +1,203 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import FirebaseFirestore
+import FirebaseStorage
+import HealthKitOnFHIR
+import OSLog
+import PDFKit
+import Spezi
+import SpeziAccount
+import SpeziFirebaseAccountStorage
+import SpeziFirestore
+import SpeziHealthKit
+import SpeziMockWebService
+import SpeziOnboarding
+import SpeziQuestionnaire
+import SwiftUI
+
+
+actor TemplateApplicationStandard: Standard, EnvironmentAccessible, HealthKitConstraint, OnboardingConstraint, AccountStorageConstraint {
+ enum TemplateApplicationStandardError: Error {
+ case userNotAuthenticatedYet
+ }
+
+ private static var userCollection: CollectionReference {
+ Firestore.firestore().collection("users")
+ }
+
+ @Dependency var mockWebService: MockWebService?
+ @Dependency var accountStorage: FirestoreAccountStorage?
+
+ @AccountReference var account: Account
+
+ private let logger = Logger(subsystem: "TemplateApplication", category: "Standard")
+
+
+ private var userDocumentReference: DocumentReference {
+ get async throws {
+ guard let details = await account.details else {
+ throw TemplateApplicationStandardError.userNotAuthenticatedYet
+ }
+
+ return Self.userCollection.document(details.accountId)
+ }
+ }
+
+ private var userBucketReference: StorageReference {
+ get async throws {
+ guard let details = await account.details else {
+ throw TemplateApplicationStandardError.userNotAuthenticatedYet
+ }
+
+ return Storage.storage().reference().child("users/\(details.accountId)")
+ }
+ }
+
+
+ init() {
+ if !FeatureFlags.disableFirebase {
+ _accountStorage = Dependency(wrappedValue: FirestoreAccountStorage(storeIn: TemplateApplicationStandard.userCollection))
+ }
+ }
+
+
+ func add(sample: HKSample) async {
+ if let mockWebService {
+ let encoder = JSONEncoder()
+ encoder.outputFormatting = [.prettyPrinted, .sortedKeys, .withoutEscapingSlashes]
+ let jsonRepresentation = (try? String(data: encoder.encode(sample.resource), encoding: .utf8)) ?? ""
+ try? await mockWebService.upload(path: "healthkit/\(sample.uuid.uuidString)", body: jsonRepresentation)
+ return
+ }
+
+ do {
+ try await healthKitDocument(id: sample.id).setData(from: sample.resource)
+ } catch {
+ logger.error("Could not store HealthKit sample: \(error)")
+ }
+ }
+
+ func remove(sample: HKDeletedObject) async {
+ if let mockWebService {
+ try? await mockWebService.remove(path: "healthkit/\(sample.uuid.uuidString)")
+ return
+ }
+
+ do {
+ try await healthKitDocument(id: sample.uuid).delete()
+ } catch {
+ logger.error("Could not remove HealthKit sample: \(error)")
+ }
+ }
+
+ func add(response: ModelsR4.QuestionnaireResponse) async {
+ let id = response.identifier?.value?.value?.string ?? UUID().uuidString
+
+ if let mockWebService {
+ let jsonRepresentation = (try? String(data: JSONEncoder().encode(response), encoding: .utf8)) ?? ""
+ try? await mockWebService.upload(path: "questionnaireResponse/\(id)", body: jsonRepresentation)
+ return
+ }
+
+ do {
+ try await userDocumentReference
+ .collection("QuestionnaireResponse") // Add all HealthKit sources in a /QuestionnaireResponse collection.
+ .document(id) // Set the document identifier to the id of the response.
+ .setData(from: response)
+ } catch {
+ logger.error("Could not store questionnaire response: \(error)")
+ }
+ }
+
+
+ private func healthKitDocument(id uuid: UUID) async throws -> DocumentReference {
+ try await userDocumentReference
+ .collection("HealthKit") // Add all HealthKit sources in a /HealthKit collection.
+ .document(uuid.uuidString) // Set the document identifier to the UUID of the document.
+ }
+
+ func deletedAccount() async throws {
+ // delete all user associated data
+ do {
+ try await userDocumentReference.delete()
+ } catch {
+ logger.error("Could not delete user document: \(error)")
+ }
+ }
+
+ /// Stores the given consent form in the user's document directory with a unique timestamped filename.
+ ///
+ /// - Parameter consent: The consent form's data to be stored as a `PDFDocument`.
+ func store(consent: PDFDocument) async {
+ let formatter = DateFormatter()
+ formatter.dateFormat = "yyyy-MM-dd_HHmmss"
+ let dateString = formatter.string(from: Date())
+
+ guard !FeatureFlags.disableFirebase else {
+ guard let basePath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
+ logger.error("Could not create path for writing consent form to user document directory.")
+ return
+ }
+
+ let filePath = basePath.appending(path: "consentForm_\(dateString).pdf")
+ consent.write(to: filePath)
+
+ return
+ }
+
+ do {
+ guard let consentData = consent.dataRepresentation() else {
+ logger.error("Could not store consent form.")
+ return
+ }
+
+ let metadata = StorageMetadata()
+ metadata.contentType = "application/pdf"
+ _ = try await userBucketReference.child("consent/\(dateString).pdf").putDataAsync(consentData, metadata: metadata)
+ } catch {
+ logger.error("Could not store consent form: \(error)")
+ }
+ }
+
+
+ func create(_ identifier: AdditionalRecordId, _ details: SignupDetails) async throws {
+ guard let accountStorage else {
+ preconditionFailure("Account Storage was requested although not enabled in current configuration.")
+ }
+ try await accountStorage.create(identifier, details)
+ }
+
+ func load(_ identifier: AdditionalRecordId, _ keys: [any AccountKey.Type]) async throws -> PartialAccountDetails {
+ guard let accountStorage else {
+ preconditionFailure("Account Storage was requested although not enabled in current configuration.")
+ }
+ return try await accountStorage.load(identifier, keys)
+ }
+
+ func modify(_ identifier: AdditionalRecordId, _ modifications: AccountModifications) async throws {
+ guard let accountStorage else {
+ preconditionFailure("Account Storage was requested although not enabled in current configuration.")
+ }
+ try await accountStorage.modify(identifier, modifications)
+ }
+
+ func clear(_ identifier: AdditionalRecordId) async {
+ guard let accountStorage else {
+ preconditionFailure("Account Storage was requested although not enabled in current configuration.")
+ }
+ await accountStorage.clear(identifier)
+ }
+
+ func delete(_ identifier: AdditionalRecordId) async throws {
+ guard let accountStorage else {
+ preconditionFailure("Account Storage was requested although not enabled in current configuration.")
+ }
+ try await accountStorage.delete(identifier)
+ }
+}
diff --git a/TemplateApplication/TemplateApplicationTestingSetup.swift b/TemplateApplication/TemplateApplicationTestingSetup.swift
new file mode 100644
index 0000000..b0b98ee
--- /dev/null
+++ b/TemplateApplication/TemplateApplicationTestingSetup.swift
@@ -0,0 +1,34 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import SwiftUI
+
+
+private struct TemplateAppTestingSetup: ViewModifier {
+ @AppStorage(StorageKeys.onboardingFlowComplete) var completedOnboardingFlow = false
+
+
+ func body(content: Content) -> some View {
+ content
+ .task {
+ if FeatureFlags.skipOnboarding {
+ completedOnboardingFlow = true
+ }
+ if FeatureFlags.showOnboarding {
+ completedOnboardingFlow = false
+ }
+ }
+ }
+}
+
+
+extension View {
+ func testingSetup() -> some View {
+ self.modifier(TemplateAppTestingSetup())
+ }
+}
diff --git a/TemplateApplicationTests/TemplateApplicationTests.swift b/TemplateApplicationTests/TemplateApplicationTests.swift
new file mode 100644
index 0000000..731c1a9
--- /dev/null
+++ b/TemplateApplicationTests/TemplateApplicationTests.swift
@@ -0,0 +1,17 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+@testable import TemplateApplication
+import XCTest
+
+
+class TemplateApplicationTests: XCTestCase {
+ func testExample() throws {
+ XCTAssertTrue(true)
+ }
+}
diff --git a/TemplateApplicationUITests/ContactsTests.swift b/TemplateApplicationUITests/ContactsTests.swift
new file mode 100644
index 0000000..672ebc3
--- /dev/null
+++ b/TemplateApplicationUITests/ContactsTests.swift
@@ -0,0 +1,39 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import XCTest
+
+
+class ContactsTests: XCTestCase {
+ override func setUpWithError() throws {
+ try super.setUpWithError()
+
+ continueAfterFailure = false
+
+ let app = XCUIApplication()
+ app.launchArguments = ["--skipOnboarding"]
+ app.launch()
+ }
+
+
+ func testContacts() throws {
+ let app = XCUIApplication()
+
+ XCTAssertTrue(app.tabBars["Tab Bar"].buttons["Contacts"].waitForExistence(timeout: 2))
+ app.tabBars["Tab Bar"].buttons["Contacts"].tap()
+
+ XCTAssertTrue(app.staticTexts["Contact: Leland Stanford"].waitForExistence(timeout: 2))
+
+ XCTAssertTrue(app.buttons["Call"].waitForExistence(timeout: 2))
+ XCTAssertTrue(app.buttons["Text"].waitForExistence(timeout: 2))
+ XCTAssertTrue(app.buttons["Email"].waitForExistence(timeout: 2))
+ XCTAssertTrue(app.buttons["Website"].waitForExistence(timeout: 2))
+
+ XCTAssertTrue(app.buttons["Address: 450 Serra Mall\nStanford CA 94305\nUSA"].waitForExistence(timeout: 2))
+ }
+}
diff --git a/TemplateApplicationUITests/ContributionsTest.swift b/TemplateApplicationUITests/ContributionsTest.swift
new file mode 100644
index 0000000..99227a7
--- /dev/null
+++ b/TemplateApplicationUITests/ContributionsTest.swift
@@ -0,0 +1,41 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import XCTest
+
+
+final class ContributionsTest: XCTestCase {
+ override func setUpWithError() throws {
+ try super.setUpWithError()
+
+ try disablePasswordAutofill()
+
+ continueAfterFailure = false
+
+ let app = XCUIApplication()
+ app.launchArguments = ["--showOnboarding"]
+ app.deleteAndLaunch(withSpringboardAppName: "TemplateApplication")
+ }
+
+ func testLicenseInformationPage() throws {
+ let app = XCUIApplication()
+
+ // complete onboarding so user is logged in
+ try app.conductOnboardingIfNeeded(email: "leland@contributions.stanford.edu")
+
+
+ XCTAssertTrue(app.buttons["Your Account"].waitForExistence(timeout: 6.0))
+ app.buttons["Your Account"].tap()
+
+ XCTAssertTrue(app.buttons["License Information"].waitForExistence(timeout: 2))
+ app.buttons["License Information"].tap()
+ // Test if the sheet opens by checking if the title of the sheet is present
+ XCTAssertTrue(app.staticTexts["This project is licensed under the MIT License."].waitForExistence(timeout: 2))
+ XCTAssertTrue(app.buttons["Repository Link"].waitForExistence(timeout: 2))
+ }
+}
diff --git a/TemplateApplicationUITests/MockUploadTests.swift b/TemplateApplicationUITests/MockUploadTests.swift
new file mode 100644
index 0000000..298caa0
--- /dev/null
+++ b/TemplateApplicationUITests/MockUploadTests.swift
@@ -0,0 +1,32 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import XCTest
+
+
+class MockUploadTestsTests: XCTestCase {
+ override func setUpWithError() throws {
+ try super.setUpWithError()
+
+ continueAfterFailure = false
+
+ let app = XCUIApplication()
+ app.launchArguments = ["--skipOnboarding", "--disableFirebase"]
+ app.launch()
+ }
+
+
+ func testMockUpload() throws {
+ let app = XCUIApplication()
+
+ XCTAssertTrue(app.tabBars["Tab Bar"].buttons["Mock Web Service"].waitForExistence(timeout: 2))
+ app.tabBars["Tab Bar"].buttons["Mock Web Service"].tap()
+
+ XCTAssertTrue(app.staticTexts["Mock Requests"].waitForExistence(timeout: 2))
+ }
+}
diff --git a/TemplateApplicationUITests/OnboardingTests.swift b/TemplateApplicationUITests/OnboardingTests.swift
new file mode 100644
index 0000000..c3c2873
--- /dev/null
+++ b/TemplateApplicationUITests/OnboardingTests.swift
@@ -0,0 +1,227 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import XCTest
+import XCTestExtensions
+import XCTHealthKit
+
+
+class OnboardingTests: XCTestCase {
+ override func setUpWithError() throws {
+ try super.setUpWithError()
+
+ try disablePasswordAutofill()
+
+ continueAfterFailure = false
+
+ let app = XCUIApplication()
+ app.launchArguments = ["--showOnboarding"]
+ app.deleteAndLaunch(withSpringboardAppName: "TemplateApplication")
+ }
+
+
+ func testOnboardingFlow() throws {
+ let app = XCUIApplication()
+ let email = "leland@onboarding.stanford.edu"
+
+ try app.navigateOnboardingFlow(email: email)
+
+ app.assertOnboardingComplete()
+ try app.assertAccountInformation(email: email)
+ }
+
+ func testOnboardingFlowRepeated() throws {
+ let app = XCUIApplication()
+ app.launchArguments = ["--showOnboarding", "--disableFirebase"]
+ app.terminate()
+ app.launch()
+
+ try app.navigateOnboardingFlow()
+ app.assertOnboardingComplete()
+
+ app.terminate()
+
+ // Second onboarding round shouldn't display HealthKit and Notification authorizations anymore
+ app.activate()
+
+ try app.navigateOnboardingFlow(repeated: true)
+ // Do not show HealthKit and Notification authorization view again
+ app.assertOnboardingComplete()
+ }
+}
+
+
+extension XCUIApplication {
+ func conductOnboardingIfNeeded(email: String = "leland@stanford.edu") throws {
+ let app = XCUIApplication()
+
+ if app.staticTexts["Spezi\nTemplate Application"].waitForExistence(timeout: 5) {
+ try app.navigateOnboardingFlow(email: email)
+ }
+ }
+
+ fileprivate func navigateOnboardingFlow(
+ email: String = "leland@stanford.edu",
+ repeated skippedIfRepeated: Bool = false
+ ) throws {
+ try navigateOnboardingFlowWelcome()
+ try navigateOnboardingFlowInterestingModules()
+ if staticTexts["Your Account"].waitForExistence(timeout: 5) {
+ try navigateOnboardingAccount(email: email)
+ }
+ if staticTexts["Consent"].waitForExistence(timeout: 5) {
+ try navigateOnboardingFlowConsent()
+ }
+ if !skippedIfRepeated {
+ try navigateOnboardingFlowHealthKitAccess()
+ try navigateOnboardingFlowNotification()
+ }
+ }
+
+ private func navigateOnboardingFlowWelcome() throws {
+ XCTAssertTrue(staticTexts["Spezi\nTemplate Application"].waitForExistence(timeout: 5))
+
+ XCTAssertTrue(buttons["Learn More"].waitForExistence(timeout: 2))
+ buttons["Learn More"].tap()
+ }
+
+ private func navigateOnboardingFlowInterestingModules() throws {
+ XCTAssertTrue(staticTexts["Interesting Modules"].waitForExistence(timeout: 5))
+
+ for _ in 1..<4 {
+ XCTAssertTrue(buttons["Next"].waitForExistence(timeout: 2))
+ buttons["Next"].tap()
+ }
+
+ XCTAssertTrue(buttons["Next"].waitForExistence(timeout: 2))
+ buttons["Next"].tap()
+ }
+
+ private func navigateOnboardingAccount(email: String) throws {
+ guard !buttons["Next"].waitForExistence(timeout: 5) else {
+ buttons["Next"].tap()
+ return
+ }
+
+ XCTAssertTrue(buttons["Signup"].waitForExistence(timeout: 2))
+ buttons["Signup"].tap()
+
+ XCTAssertTrue(staticTexts["Create a new Account"].waitForExistence(timeout: 2))
+
+ try collectionViews.textFields["E-Mail Address"].enter(value: email)
+ try collectionViews.secureTextFields["Password"].enter(value: "StanfordRocks")
+ try textFields["enter first name"].enter(value: "Leland")
+ try textFields["enter last name"].enter(value: "Stanford")
+
+ XCTAssertTrue(collectionViews.buttons["Signup"].waitForExistence(timeout: 2))
+ collectionViews.buttons["Signup"].tap()
+
+ sleep(3)
+
+ if staticTexts["HealthKit Access"].waitForExistence(timeout: 5) && navigationBars.buttons["Back"].waitForExistence(timeout: 5) {
+ navigationBars.buttons["Back"].tap()
+
+ XCTAssertTrue(staticTexts["Leland Stanford"].waitForExistence(timeout: 2))
+ XCTAssertTrue(staticTexts[email].waitForExistence(timeout: 2))
+
+ XCTAssertTrue(buttons["Next"].waitForExistence(timeout: 2))
+ buttons["Next"].tap()
+ }
+ }
+
+ private func navigateOnboardingFlowConsent() throws {
+ XCTAssertTrue(staticTexts["Consent"].waitForExistence(timeout: 5))
+
+ XCTAssertTrue(staticTexts["First Name"].waitForExistence(timeout: 2))
+ try textFields["Enter your first name ..."].enter(value: "Leland")
+
+ XCTAssertTrue(staticTexts["Last Name"].waitForExistence(timeout: 2))
+ try textFields["Enter your last name ..."].enter(value: "Stanford")
+
+ XCTAssertTrue(scrollViews["Signature Field"].waitForExistence(timeout: 2))
+ scrollViews["Signature Field"].swipeRight()
+
+ XCTAssertTrue(buttons["I Consent"].waitForExistence(timeout: 2))
+ buttons["I Consent"].tap()
+ }
+
+ private func navigateOnboardingFlowHealthKitAccess() throws {
+ XCTAssertTrue(staticTexts["HealthKit Access"].waitForExistence(timeout: 5))
+
+ XCTAssertTrue(buttons["Grant Access"].waitForExistence(timeout: 2))
+ buttons["Grant Access"].tap()
+
+ try handleHealthKitAuthorization()
+ }
+
+ private func navigateOnboardingFlowNotification() throws {
+ XCTAssertTrue(staticTexts["Notifications"].waitForExistence(timeout: 5))
+
+ XCTAssertTrue(buttons["Allow Notifications"].waitForExistence(timeout: 2))
+ buttons["Allow Notifications"].tap()
+
+ let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
+ let alertAllowButton = springboard.buttons["Allow"]
+ if alertAllowButton.waitForExistence(timeout: 5) {
+ alertAllowButton.tap()
+ }
+ }
+
+ fileprivate func assertOnboardingComplete() {
+ let tabBar = tabBars["Tab Bar"]
+ XCTAssertTrue(tabBar.buttons["Schedule"].waitForExistence(timeout: 2))
+ XCTAssertTrue(tabBar.buttons["Contacts"].waitForExistence(timeout: 2))
+ }
+
+ fileprivate func assertAccountInformation(email: String) throws {
+ XCTAssertTrue(navigationBars.buttons["Your Account"].waitForExistence(timeout: 2))
+ navigationBars.buttons["Your Account"].tap()
+
+ XCTAssertTrue(staticTexts["Account Overview"].waitForExistence(timeout: 5.0))
+ XCTAssertTrue(staticTexts["Leland Stanford"].exists)
+ XCTAssertTrue(staticTexts[email].exists)
+ XCTAssertTrue(staticTexts["Gender Identity, Choose not to answer"].exists)
+
+
+ XCTAssertTrue(navigationBars.buttons["Close"].waitForExistence(timeout: 0.5))
+ navigationBars.buttons["Close"].tap()
+
+ XCTAssertTrue(navigationBars.buttons["Your Account"].waitForExistence(timeout: 2))
+ navigationBars.buttons["Your Account"].tap()
+
+ XCTAssertTrue(navigationBars.buttons["Edit"].waitForExistence(timeout: 2))
+ navigationBars.buttons["Edit"].tap()
+
+ usleep(500_00)
+ XCTAssertFalse(navigationBars.buttons["Close"].exists)
+
+ XCTAssertTrue(buttons["Delete Account"].waitForExistence(timeout: 2))
+ buttons["Delete Account"].tap()
+
+ let alert = "Are you sure you want to delete your account?"
+ XCTAssertTrue(alerts[alert].waitForExistence(timeout: 6.0))
+ alerts[alert].buttons["Delete"].tap()
+
+ XCTAssertTrue(alerts["Authentication Required"].waitForExistence(timeout: 2.0))
+ XCTAssertTrue(alerts["Authentication Required"].secureTextFields["Password"].waitForExistence(timeout: 0.5))
+ typeText("StanfordRocks") // the password field has focus already
+ XCTAssertTrue(alerts["Authentication Required"].buttons["Login"].waitForExistence(timeout: 0.5))
+ alerts["Authentication Required"].buttons["Login"].tap()
+
+ sleep(2)
+
+ // Login
+ try textFields["E-Mail Address"].enter(value: email)
+ try secureTextFields["Password"].enter(value: "StanfordRocks")
+
+ XCTAssertTrue(buttons["Login"].waitForExistence(timeout: 0.5))
+ buttons["Login"].tap()
+
+ XCTAssertTrue(alerts["Invalid Credentials"].waitForExistence(timeout: 2.0))
+ }
+}
diff --git a/TemplateApplicationUITests/SchedulerTests.swift b/TemplateApplicationUITests/SchedulerTests.swift
new file mode 100644
index 0000000..c69d84e
--- /dev/null
+++ b/TemplateApplicationUITests/SchedulerTests.swift
@@ -0,0 +1,36 @@
+//
+// This source file is part of the Stanford Spezi Template Application open-source project
+//
+// SPDX-FileCopyrightText: 2023 Stanford University
+//
+// SPDX-License-Identifier: MIT
+//
+
+import XCTest
+import XCTestExtensions
+
+
+class SchedulerTests: XCTestCase {
+ override func setUpWithError() throws {
+ try super.setUpWithError()
+
+ continueAfterFailure = false
+
+ let app = XCUIApplication()
+ app.launchArguments = ["--skipOnboarding", "--testSchedule"]
+ app.deleteAndLaunch(withSpringboardAppName: "TemplateApplication")
+ }
+
+
+ func testScheduler() throws {
+ let app = XCUIApplication()
+
+ XCTAssertTrue(app.tabBars["Tab Bar"].buttons["Schedule"].waitForExistence(timeout: 2))
+ app.tabBars["Tab Bar"].buttons["Schedule"].tap()
+
+ XCTAssertTrue(app.staticTexts["Start Questionnaire"].waitForExistence(timeout: 2))
+ app.staticTexts["Start Questionnaire"].tap()
+
+ XCTAssertTrue(app.staticTexts["Social Support"].waitForExistence(timeout: 2))
+ }
+}
diff --git a/codecov.yml b/codecov.yml
new file mode 100644
index 0000000..8110181
--- /dev/null
+++ b/codecov.yml
@@ -0,0 +1,40 @@
+#
+# This source file is part of the Stanford Spezi Template Application open-source project
+#
+# SPDX-FileCopyrightText: 2023 Stanford University
+#
+# SPDX-License-Identifier: MIT
+#
+
+codecov:
+ branch: main
+ require_ci_to_pass: true
+comment:
+ behavior: default
+ layout: reach,diff,flags,files,footer
+ require_changes: false
+coverage:
+ precision: 2
+ range:
+ - 70.0
+ - 90.0
+ round: up
+ status:
+ patch:
+ default:
+ target: auto
+ threshold: 5.0
+ project:
+ default:
+ target: auto
+ threshold: 5.0
+ignore:
+- ^TemplateApplicationUITests.*
+- ^TemplateApplicationTests.*
+parsers:
+ gcov:
+ branch_detection:
+ conditional: true
+ loop: true
+ macro: false
+ method: false
diff --git a/fastlane/.gitignore b/fastlane/.gitignore
new file mode 100644
index 0000000..6c81710
--- /dev/null
+++ b/fastlane/.gitignore
@@ -0,0 +1,11 @@
+#
+# This source file is part of the Stanford Spezi Template Application open-source project
+#
+# SPDX-FileCopyrightText: 2023 Stanford University
+#
+# SPDX-License-Identifier: MIT
+#
+
+test_output
+report.xml
+screenshots
\ No newline at end of file
diff --git a/fastlane/Appfile b/fastlane/Appfile
new file mode 100644
index 0000000..214ae7c
--- /dev/null
+++ b/fastlane/Appfile
@@ -0,0 +1,13 @@
+#
+# This source file is part of the Stanford Spezi Template Application open-source project
+#
+# SPDX-FileCopyrightText: 2023 Stanford University
+#
+# SPDX-License-Identifier: MIT
+#
+
+# For more information about the Appfile, see:
+# https://docs.fastlane.tools/advanced/#appfile
+
+app_identifier "edu.stanford.spezi.templateapplication" # The bundle identifier of your app
+apple_id ENV["APPLE_ID"] # Your Apple email address
\ No newline at end of file
diff --git a/fastlane/Fastfile b/fastlane/Fastfile
new file mode 100644
index 0000000..ce180ba
--- /dev/null
+++ b/fastlane/Fastfile
@@ -0,0 +1,89 @@
+#
+# This source file is part of the Stanford Spezi Template Application open-source project
+#
+# SPDX-FileCopyrightText: 2023 Stanford University
+#
+# SPDX-License-Identifier: MIT
+#
+
+default_platform(:ios)
+
+platform :ios do
+ before_all do
+ ENV["FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT"] = "5"
+ ENV["FASTLANE_XCODEBUILD_SETTINGS_RETRIES"] = "6"
+ end
+
+ desc "Build and test"
+ lane :test do
+ run_tests(
+ skip_build: true,
+ derived_data_path: ".derivedData",
+ code_coverage: true,
+ devices: ["iPhone 15 Pro"],
+ force_quit_simulator: true,
+ reset_simulator: true,
+ prelaunch_simulator: false,
+ concurrent_workers: 1,
+ max_concurrent_simulators: 1,
+ result_bundle: true,
+ output_directory: ".",
+ xcargs: "-skipPackagePluginValidation"
+ )
+ end
+
+ desc "CodeQL"
+ lane :codeql do
+ build_app(
+ skip_archive: true,
+ skip_codesigning: true,
+ derived_data_path: ".derivedData",
+ xcargs: "-skipPackagePluginValidation"
+ )
+ end
+
+ desc "Build app"
+ lane :build do
+ build_app(
+ derived_data_path: ".derivedData",
+ xcargs: "-skipPackagePluginValidation",
+ export_options: {
+ provisioningProfiles: {
+ "edu.stanford.spezi.templateapplication" => "Spezi Template Application"
+ }
+ }
+ )
+ end
+
+ desc "Sign in to the App Store Connect API"
+ lane :signin do
+ app_store_connect_api_key(
+ key_id: ENV["APP_STORE_CONNECT_API_KEY_ID"],
+ issuer_id: ENV["APP_STORE_CONNECT_ISSUER_ID"],
+ key_content: ENV["APP_STORE_CONNECT_API_KEY_BASE64"],
+ is_key_content_base64: true
+ )
+ end
+
+ desc "Publish a beta release to internal TestFlight testers"
+ lane :beta do
+ signin
+ increment_build_number(
+ {
+ build_number: latest_testflight_build_number + 1
+ }
+ )
+ build
+ commit = last_git_commit
+ upload_to_testflight(
+ distribute_external: true,
+ groups: [
+ "External Testers"
+ ],
+ submit_beta_review: true,
+ notify_external_testers: true,
+ expire_previous_builds: true,
+ changelog: commit[:message]
+ )
+ end
+end
diff --git a/fastlane/README.md b/fastlane/README.md
new file mode 100644
index 0000000..140582f
--- /dev/null
+++ b/fastlane/README.md
@@ -0,0 +1,64 @@
+fastlane documentation
+----
+
+# Installation
+
+Make sure you have the latest version of the Xcode command line tools installed:
+
+```sh
+xcode-select --install
+```
+
+For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane)
+
+# Available Actions
+
+## iOS
+
+### ios test
+
+```sh
+[bundle exec] fastlane ios test
+```
+
+Build and test
+
+### ios codeql
+
+```sh
+[bundle exec] fastlane ios codeql
+```
+
+CodeQL
+
+### ios build
+
+```sh
+[bundle exec] fastlane ios build
+```
+
+Build app
+
+### ios signin
+
+```sh
+[bundle exec] fastlane ios signin
+```
+
+Sign in to the App Store Connect API
+
+### ios beta
+
+```sh
+[bundle exec] fastlane ios beta
+```
+
+Publish a beta release to internal TestFlight testers
+
+----
+
+This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run.
+
+More information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools).
+
+The documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools).
diff --git a/fastlane/README.md.license b/fastlane/README.md.license
new file mode 100644
index 0000000..8ffe29f
--- /dev/null
+++ b/fastlane/README.md.license
@@ -0,0 +1,6 @@
+
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2023 Stanford University
+
+SPDX-License-Identifier: MIT
diff --git a/firebase.json b/firebase.json
new file mode 100644
index 0000000..0b85064
--- /dev/null
+++ b/firebase.json
@@ -0,0 +1,24 @@
+{
+ "firestore": {
+ "rules": "firestore.rules"
+ },
+ "storage": {
+ "rules": "firebasestorage.rules"
+ },
+ "emulators": {
+ "auth": {
+ "port": 9099
+ },
+ "firestore": {
+ "port": 8080
+ },
+ "ui": {
+ "enabled": true,
+ "port": 4000
+ },
+ "storage": {
+ "port": 9199
+ },
+ "singleProjectMode": true
+ }
+}
\ No newline at end of file
diff --git a/firebase.json.license b/firebase.json.license
new file mode 100644
index 0000000..9f5551c
--- /dev/null
+++ b/firebase.json.license
@@ -0,0 +1,5 @@
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2022 Stanford University and the project authors (see CONTRIBUTORS.md)
+
+SPDX-License-Identifier: MIT
diff --git a/firebasestorage.rules b/firebasestorage.rules
new file mode 100644
index 0000000..18a3bd2
--- /dev/null
+++ b/firebasestorage.rules
@@ -0,0 +1,12 @@
+rules_version = '2';
+service firebase.storage {
+ match /b/{bucket}/o {
+ match /users/{userId}/{allPaths=**} {
+ allow read, write: if request.auth != null && request.auth.uid == userId;
+ }
+
+ match /{allPaths=**} {
+ allow read, write: if false;
+ }
+ }
+}
diff --git a/firebasestorage.rules.license b/firebasestorage.rules.license
new file mode 100644
index 0000000..9f5551c
--- /dev/null
+++ b/firebasestorage.rules.license
@@ -0,0 +1,5 @@
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2022 Stanford University and the project authors (see CONTRIBUTORS.md)
+
+SPDX-License-Identifier: MIT
diff --git a/firestore.rules b/firestore.rules
new file mode 100644
index 0000000..5a233b0
--- /dev/null
+++ b/firestore.rules
@@ -0,0 +1,9 @@
+rules_version = '2';
+service cloud.firestore {
+ match /databases/{database}/documents {
+ // Allow only authenticated content owners access
+ match /users/{userId}/{documents=**} {
+ allow read, write: if request.auth != null && request.auth.uid == userId;
+ }
+ }
+}
diff --git a/firestore.rules.license b/firestore.rules.license
new file mode 100644
index 0000000..9f5551c
--- /dev/null
+++ b/firestore.rules.license
@@ -0,0 +1,5 @@
+This source file is part of the Stanford Spezi Template Application open-source project
+
+SPDX-FileCopyrightText: 2022 Stanford University and the project authors (see CONTRIBUTORS.md)
+
+SPDX-License-Identifier: MIT