├── .github └── workflows │ └── swiftlint-workflow.yml ├── .gitignore ├── .swift-format ├── .swiftlint.yml ├── CODE_OF_CONDUCT.md ├── Configuration ├── Debug.xcconfig ├── General.xcconfig └── Release.xcconfig ├── LICENSE.md ├── README.md ├── Sensor-App-Framework ├── API │ ├── AppUpdates.swift │ ├── CalculationManager.swift │ ├── ExportManager.swift │ ├── LocationManager.swift │ ├── MotionManager.swift │ └── SettingsManager.swift ├── Customization │ └── Extension │ │ ├── Extension+Bundle.swift │ │ ├── Extension+Double.swift │ │ ├── Extension+Logger.swift │ │ ├── Extension+String.swift │ │ └── Extension+URL.swift ├── Localization │ └── Helper │ │ ├── PreviewLocalizationModifier.swift │ │ └── SupportedLanguage.swift └── Model │ ├── AltitudeModel.swift │ ├── GraphDetailModel.swift │ ├── LocationModel.swift │ ├── MapKitSettings.swift │ ├── MotionModel.swift │ ├── SettingsForUserDefaults.swift │ └── UserSettings.swift ├── Sensor-App-WatchApp ├── Layout │ ├── ContentView.swift │ └── Sensor_AppApp.swift ├── Miscellaneous │ ├── ComplicationController.swift │ ├── Info.plist │ ├── NotificationController.swift │ ├── NotificationView.swift │ ├── Preview Content │ │ └── Preview Assets.xcassets │ │ │ └── Contents.json │ ├── PushNotificationPayload.apns │ └── UIIdentifiers.swift └── View │ ├── AccelerationView.swift │ ├── AltitudeView.swift │ ├── AttitudeView.swift │ ├── GravityView.swift │ ├── GyroscopeView.swift │ ├── LocationView.swift │ ├── MagnetometerView.swift │ └── SettingsView.swift ├── Sensor-App.png ├── Sensor-App.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── xcshareddata │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ │ ├── Sensor-App-Framework.xcscheme │ │ ├── Sensor-App-watchOS.xcscheme │ │ └── Sensor-App.xcscheme └── xcuserdata │ └── volkerschmitt.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── Sensor-App ├── Helper │ ├── CustomToolbar.swift │ ├── Customization │ │ ├── CustomModifier │ │ │ ├── ConditionalOverlay.swift │ │ │ └── NavigationBarItemModifier.swift │ │ └── Extension │ │ │ └── Extension_Map.swift │ ├── MotionManagerAccessView.swift │ ├── RefreshRateView.swift │ └── ShareSheet.swift ├── Layout │ ├── ContentView.swift │ ├── DetailColumn.swift │ ├── HomeScreen.swift │ ├── Navigation │ │ ├── AppState.swift │ │ ├── Route.swift │ │ └── Screen.swift │ ├── SensorAppApp.swift │ └── Sidebar.swift ├── Localization │ ├── InfoPlist.xcstrings │ └── Localizable.xcstrings ├── Miscellaneous │ ├── Assets.xcassets │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ ├── App Icons │ │ │ ├── AppIcon-V1.appiconset │ │ │ │ ├── Contents.json │ │ │ │ ├── ios-marketing 1.png │ │ │ │ └── ios-marketing 2.png │ │ │ ├── AppIcon-V2.appiconset │ │ │ │ ├── Contents.json │ │ │ │ ├── ios-marketing 1.png │ │ │ │ └── ios-marketing.png │ │ │ ├── AppIcon-V3.appiconset │ │ │ │ ├── Contents.json │ │ │ │ ├── ios-marketing 1.png │ │ │ │ └── ios-marketing.png │ │ │ └── Contents.json │ │ ├── Contents.json │ │ └── Images │ │ │ ├── AppIcon-V1.imageset │ │ │ ├── Contents.json │ │ │ └── ios-marketing.png │ │ │ ├── AppIcon-V2.imageset │ │ │ ├── Contents.json │ │ │ └── ios-marketing.png │ │ │ ├── AppIcon-V3.imageset │ │ │ ├── Contents.json │ │ │ └── ios-marketing.png │ │ │ └── Contents.json │ ├── Info.plist │ ├── MyPlayground.playground │ │ ├── Contents.swift │ │ ├── Pages │ │ │ ├── Array Transformation.xcplaygroundpage │ │ │ │ └── Contents.swift │ │ │ ├── Array-Search.xcplaygroundpage │ │ │ │ └── Contents.swift │ │ │ ├── Call function of parent View.xcplaygroundpage │ │ │ │ └── Contents.swift │ │ │ ├── Chart Array transformation.xcplaygroundpage │ │ │ │ └── Contents.swift │ │ │ └── Struct-Timestamp.xcplaygroundpage │ │ │ │ └── Contents.swift │ │ ├── contents.xcplayground │ │ └── timeline.xctimeline │ ├── Preview Content │ │ └── Preview Assets.xcassets │ │ │ └── Contents.json │ └── UIIdentifiers.swift ├── Modules │ ├── Acceleration │ │ ├── AccelerationList.swift │ │ ├── AccelerationScreen.swift │ │ └── AccelerationView.swift │ ├── Altitude │ │ ├── AltitudeList.swift │ │ ├── AltitudeScreen.swift │ │ └── AltitudeView.swift │ ├── Attitude │ │ ├── AttitudeList.swift │ │ ├── AttitudeScreen.swift │ │ └── AttitudeView.swift │ ├── Gravity │ │ ├── GravityList.swift │ │ ├── GravityScreen.swift │ │ └── GravityView.swift │ ├── Gyroscope │ │ ├── GyroscopeList.swift │ │ ├── GyroscopeScreen.swift │ │ └── GyroscopeView.swift │ ├── LineGraph │ │ └── LineGraphSubView.swift │ ├── Location │ │ ├── LocationScreen.swift │ │ ├── LocationView.swift │ │ ├── MapKitView.swift │ │ └── MapView.swift │ ├── Magnetometer │ │ ├── MagnetometerList.swift │ │ ├── MagnetometerScreen.swift │ │ └── MagnetometerView.swift │ ├── Notification │ │ ├── NotificationEnvironmentKey.swift │ │ ├── NotificationModifier.swift │ │ └── NotificationView.swift │ ├── ReleaseNotes │ │ ├── ReleaseNotesScreen.swift │ │ └── ReleaseNotesView.swift │ └── Settings │ │ └── SettingsScreen.swift ├── PreviewTraits │ └── NavEmbedded.swift └── ViewModifier │ └── NavigationStackWrapper.swift ├── Tests ├── 0_Test Plans │ ├── iOS_FullTest.xctestplan │ ├── iOS_ScreenshotTest.xctestplan │ ├── iOS_UITest.xctestplan │ ├── iOS_UnitTest.xctestplan │ ├── watchOS_FullTest.xctestplan │ ├── watchOS_ScreenshotTest.xctestplan │ └── watchOS_UITest.xctestplan ├── iOSUITests │ ├── AccelerationViewUITests.swift │ ├── AltitudeViewUITests.swift │ ├── AttitudeViewUITests.swift │ ├── GravityViewUITests.swift │ ├── GyroscopeViewUITests.swift │ ├── Helper │ │ ├── BaseTestCase.swift │ │ └── Extension+XCUIElement.swift │ ├── LocationViewUITests.swift │ ├── MagnetometerViewUITests.swift │ ├── ScreenshotUITests.swift │ └── SettingsViewUITests.swift ├── iOSUnitTests │ ├── API │ │ ├── AppUpdatesTests.swift │ │ ├── CalculationManagerTests.swift │ │ └── SettingsManagerTests.swift │ ├── Customization │ │ └── ExtensionTests.swift │ └── Helper │ │ └── BaseTestCase.swift └── watchOSUITests │ ├── Helper │ └── BaseTestCase.swift │ └── ScreenshotUITests.swift ├── fastlane └── metadata │ ├── copyright.txt │ ├── de-DE │ ├── description.txt │ ├── keywords.txt │ ├── marketing_url.txt │ ├── name.txt │ ├── privacy_url.txt │ ├── promotional_text.txt │ ├── release_notes.txt │ ├── subtitle.txt │ └── support_url.txt │ ├── en-US │ ├── description.txt │ ├── keywords.txt │ ├── marketing_url.txt │ ├── name.txt │ ├── privacy_url.txt │ ├── promotional_text.txt │ ├── release_notes.txt │ ├── subtitle.txt │ └── support_url.txt │ └── review_information │ ├── demo_password.txt │ ├── demo_user.txt │ ├── email.txt │ ├── first_name.txt │ ├── last_name.txt │ ├── notes.txt │ └── phone_number.txt ├── screenshots.sh └── screenshots ├── cs_CZ ├── cs_CZ_0Home-AppleWatch.png ├── cs_CZ_0Home-iPad.png ├── cs_CZ_0Home-iPhone.png ├── cs_CZ_1Location-AppleWatch.png ├── cs_CZ_1Location-iPad.png ├── cs_CZ_1Location-iPhone.png ├── cs_CZ_2Acceleration-AppleWatch.png ├── cs_CZ_2Acceleration-iPad.png ├── cs_CZ_2Acceleration-iPhone.png ├── cs_CZ_3Acceleration_Log-iPad.png ├── cs_CZ_3Acceleration_Log-iPhone.png ├── cs_CZ_4Settings-AppleWatch.png ├── cs_CZ_4Settings-iPad.png └── cs_CZ_4Settings-iPhone.png ├── de_DE ├── de_DE_0Home-AppleWatch.png ├── de_DE_0Home-iPad.png ├── de_DE_0Home-iPhone.png ├── de_DE_1Location-AppleWatch.png ├── de_DE_1Location-iPad.png ├── de_DE_1Location-iPhone.png ├── de_DE_2Acceleration-AppleWatch.png ├── de_DE_2Acceleration-iPad.png ├── de_DE_2Acceleration-iPhone.png ├── de_DE_3Acceleration_Log-iPad.png ├── de_DE_3Acceleration_Log-iPhone.png ├── de_DE_4Settings-AppleWatch.png ├── de_DE_4Settings-iPad.png └── de_DE_4Settings-iPhone.png ├── en_US ├── en_US_0Home-AppleWatch.png ├── en_US_0Home-iPad.png ├── en_US_0Home-iPhone.png ├── en_US_1Location-AppleWatch.png ├── en_US_1Location-iPad.png ├── en_US_1Location-iPhone.png ├── en_US_2Acceleration-AppleWatch.png ├── en_US_2Acceleration-iPad.png ├── en_US_2Acceleration-iPhone.png ├── en_US_3Acceleration_Log-iPad.png ├── en_US_3Acceleration_Log-iPhone.png ├── en_US_4Settings-AppleWatch.png ├── en_US_4Settings-iPad.png └── en_US_4Settings-iPhone.png ├── es_ES ├── es_ES_0Home-AppleWatch.png ├── es_ES_0Home-iPad.png ├── es_ES_0Home-iPhone.png ├── es_ES_1Location-AppleWatch.png ├── es_ES_1Location-iPad.png ├── es_ES_1Location-iPhone.png ├── es_ES_2Acceleration-AppleWatch.png ├── es_ES_2Acceleration-iPad.png ├── es_ES_2Acceleration-iPhone.png ├── es_ES_3Acceleration_Log-iPad.png ├── es_ES_3Acceleration_Log-iPhone.png ├── es_ES_4Settings-AppleWatch.png ├── es_ES_4Settings-iPad.png └── es_ES_4Settings-iPhone.png ├── fr_FR ├── fr_FR_0Home-AppleWatch.png ├── fr_FR_0Home-iPad.png ├── fr_FR_0Home-iPhone.png ├── fr_FR_1Location-AppleWatch.png ├── fr_FR_1Location-iPad.png ├── fr_FR_1Location-iPhone.png ├── fr_FR_2Acceleration-AppleWatch.png ├── fr_FR_2Acceleration-iPad.png ├── fr_FR_2Acceleration-iPhone.png ├── fr_FR_3Acceleration_Log-iPad.png ├── fr_FR_3Acceleration_Log-iPhone.png ├── fr_FR_4Settings-AppleWatch.png ├── fr_FR_4Settings-iPad.png └── fr_FR_4Settings-iPhone.png ├── it_IT ├── it_IT_0Home-AppleWatch.png ├── it_IT_0Home-iPad.png ├── it_IT_0Home-iPhone.png ├── it_IT_1Location-AppleWatch.png ├── it_IT_1Location-iPad.png ├── it_IT_1Location-iPhone.png ├── it_IT_2Acceleration-AppleWatch.png ├── it_IT_2Acceleration-iPad.png ├── it_IT_2Acceleration-iPhone.png ├── it_IT_3Acceleration_Log-iPad.png ├── it_IT_3Acceleration_Log-iPhone.png ├── it_IT_4Settings-AppleWatch.png ├── it_IT_4Settings-iPad.png └── it_IT_4Settings-iPhone.png ├── ja_JP ├── ja_JP_0Home-AppleWatch.png ├── ja_JP_0Home-iPad.png ├── ja_JP_0Home-iPhone.png ├── ja_JP_1Location-AppleWatch.png ├── ja_JP_1Location-iPad.png ├── ja_JP_1Location-iPhone.png ├── ja_JP_2Acceleration-AppleWatch.png ├── ja_JP_2Acceleration-iPad.png ├── ja_JP_2Acceleration-iPhone.png ├── ja_JP_3Acceleration_Log-iPad.png ├── ja_JP_3Acceleration_Log-iPhone.png ├── ja_JP_4Settings-AppleWatch.png ├── ja_JP_4Settings-iPad.png └── ja_JP_4Settings-iPhone.png ├── ko_KR ├── ko_KR_0Home-AppleWatch.png ├── ko_KR_0Home-iPad.png ├── ko_KR_0Home-iPhone.png ├── ko_KR_1Location-AppleWatch.png ├── ko_KR_1Location-iPad.png ├── ko_KR_1Location-iPhone.png ├── ko_KR_2Acceleration-AppleWatch.png ├── ko_KR_2Acceleration-iPad.png ├── ko_KR_2Acceleration-iPhone.png ├── ko_KR_3Acceleration_Log-iPad.png ├── ko_KR_3Acceleration_Log-iPhone.png ├── ko_KR_4Settings-AppleWatch.png ├── ko_KR_4Settings-iPad.png └── ko_KR_4Settings-iPhone.png ├── pt_PT ├── pt_PT_0Home-AppleWatch.png ├── pt_PT_0Home-iPad.png ├── pt_PT_0Home-iPhone.png ├── pt_PT_1Location-AppleWatch.png ├── pt_PT_1Location-iPad.png ├── pt_PT_1Location-iPhone.png ├── pt_PT_2Acceleration-AppleWatch.png ├── pt_PT_2Acceleration-iPad.png ├── pt_PT_2Acceleration-iPhone.png ├── pt_PT_3Acceleration_Log-iPad.png ├── pt_PT_3Acceleration_Log-iPhone.png ├── pt_PT_4Settings-AppleWatch.png ├── pt_PT_4Settings-iPad.png └── pt_PT_4Settings-iPhone.png └── zh_Hans ├── zh_Hans_0Home-Applewatch.png ├── zh_Hans_0Home-iPad.png ├── zh_Hans_0Home-iPhone.png ├── zh_Hans_1Location-Applewatch.png ├── zh_Hans_1Location-iPad.png ├── zh_Hans_1Location-iPhone.png ├── zh_Hans_2Acceleration-Applewatch.png ├── zh_Hans_2Acceleration-iPad.png ├── zh_Hans_2Acceleration-iPhone.png ├── zh_Hans_3Acceleration_Log-iPad.png ├── zh_Hans_3Acceleration_Log-iPhone.png ├── zh_Hans_4Settings-Applewatch.png ├── zh_Hans_4Settings-iPad.png └── zh_Hans_4Settings-iPhone.png /.github/workflows/swiftlint-workflow.yml: -------------------------------------------------------------------------------- 1 | name: Lint Code 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - "*" 8 | pull_request: 9 | branches: 10 | - main 11 | - "*" 12 | paths: 13 | - ".github/workflows/codelint.yml" 14 | - ".swiftlint.yml" 15 | - "**/*.swift" 16 | jobs: 17 | SwiftLint: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v4 21 | - name: Lint code using SwiftLint 22 | uses: norio-nomura/action-swiftlint@3.2.1 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/macos 2 | # Edit at https://www.gitignore.io/?templates=macos 3 | 4 | ### macOS ### 5 | # General 6 | .DS_Store 7 | .AppleDouble 8 | .LSOverride 9 | 10 | # Icon must end with two \r 11 | Icon 12 | 13 | # Thumbnails 14 | ._* 15 | 16 | # Files that might appear in the root of a volume 17 | .DocumentRevisions-V100 18 | .fseventsd 19 | .Spotlight-V100 20 | .TemporaryItems 21 | .Trashes 22 | .VolumeIcon.icns 23 | .com.apple.timemachine.donotpresent 24 | 25 | # Directories potentially created on remote AFP share 26 | .AppleDB 27 | .AppleDesktop 28 | Network Trash Folder 29 | Temporary Items 30 | .apdisk 31 | 32 | # End of https://www.gitignore.io/api/macos 33 | 34 | 35 | 36 | # Xcode 37 | # 38 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 39 | 40 | ## User settings 41 | xcuserdata/ 42 | 43 | 44 | ## Obj-C/Swift specific 45 | *.hmap 46 | 47 | ## App packaging 48 | *.ipa 49 | *.dSYM.zip 50 | *.dSYM 51 | 52 | ## Playgrounds 53 | #timeline.xctimeline 54 | #playground.xcworkspace 55 | 56 | # Swift Package Manager 57 | # 58 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 59 | # Packages/ 60 | # Package.pins 61 | # Package.resolved 62 | # *.xcodeproj 63 | # 64 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 65 | # hence it is not needed unless you have added a package configuration file to your project 66 | # .swiftpm 67 | 68 | .build/ 69 | 70 | # CocoaPods 71 | # 72 | # We recommend against adding the Pods directory to your .gitignore. However 73 | # you should judge for yourself, the pros and cons are mentioned at: 74 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 75 | # 76 | # Pods/ 77 | # 78 | # Add this line if you want to avoid checking in source code from the Xcode workspace 79 | # *.xcworkspace 80 | 81 | # Carthage 82 | # 83 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 84 | # Carthage/Checkouts 85 | 86 | Carthage/Build/ 87 | 88 | # Accio dependency management 89 | Dependencies/ 90 | .accio/ 91 | 92 | # fastlane 93 | # 94 | # It is recommended to not store the screenshots in the git repo. 95 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 96 | # For more information about the recommended setup visit: 97 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 98 | 99 | fastlane/report.xml 100 | fastlane/Preview.html 101 | fastlane/screenshots/**/*.png 102 | fastlane/test_output 103 | 104 | # Code Injection 105 | # 106 | # After new code Injection tools there's a generated folder /iOSInjectionProject 107 | # https://github.com/johnno1962/injectionforxcode 108 | 109 | iOSInjectionProject/ 110 | 111 | # Secrets for Bundle Identifier 112 | Secret.xcconfig 113 | -------------------------------------------------------------------------------- /.swift-format: -------------------------------------------------------------------------------- 1 | { 2 | "fileScopedDeclarationPrivacy" : { 3 | "accessLevel" : "private" 4 | }, 5 | "indentConditionalCompilationBlocks" : true, 6 | "indentSwitchCaseLabels" : true, 7 | "indentation" : { 8 | "spaces" : 4 9 | }, 10 | "lineBreakAroundMultilineExpressionChainComponents" : false, 11 | "lineBreakBeforeControlFlowKeywords" : false, 12 | "lineBreakBeforeEachArgument" : false, 13 | "lineBreakBeforeEachGenericRequirement" : false, 14 | "lineLength" : 120, 15 | "maximumBlankLines" : 1, 16 | "multiElementCollectionTrailingCommas" : false, 17 | "noAssignmentInExpressions" : { 18 | "allowedFunctions" : [ 19 | "XCTAssertNoThrow" 20 | ] 21 | }, 22 | "prioritizeKeepingFunctionOutputTogether" : false, 23 | "respectsExistingLineBreaks" : true, 24 | "rules" : { 25 | "AllPublicDeclarationsHaveDocumentation" : false, 26 | "AlwaysUseLiteralForEmptyCollectionInit" : false, 27 | "AlwaysUseLowerCamelCase" : true, 28 | "AmbiguousTrailingClosureOverload" : true, 29 | "BeginDocumentationCommentWithOneLineSummary" : false, 30 | "DoNotUseSemicolons" : true, 31 | "DontRepeatTypeInStaticProperties" : true, 32 | "FileScopedDeclarationPrivacy" : true, 33 | "FullyIndirectEnum" : true, 34 | "GroupNumericLiterals" : true, 35 | "IdentifiersMustBeASCII" : true, 36 | "NeverForceUnwrap" : false, 37 | "NeverUseForceTry" : false, 38 | "NeverUseImplicitlyUnwrappedOptionals" : false, 39 | "NoAccessLevelOnExtensionDeclaration" : true, 40 | "NoAssignmentInExpressions" : true, 41 | "NoBlockComments" : true, 42 | "NoCasesWithOnlyFallthrough" : true, 43 | "NoEmptyTrailingClosureParentheses" : true, 44 | "NoLabelsInCasePatterns" : true, 45 | "NoLeadingUnderscores" : false, 46 | "NoParensAroundConditions" : true, 47 | "NoPlaygroundLiterals" : true, 48 | "NoVoidReturnOnFunctionSignature" : true, 49 | "OmitExplicitReturns" : false, 50 | "OneCasePerLine" : true, 51 | "OneVariableDeclarationPerLine" : true, 52 | "OnlyOneTrailingClosureArgument" : true, 53 | "OrderedImports" : true, 54 | "ReplaceForEachWithForLoop" : true, 55 | "ReturnVoidInsteadOfEmptyTuple" : true, 56 | "TypeNamesShouldBeCapitalized" : true, 57 | "UseEarlyExits" : false, 58 | "UseExplicitNilCheckInConditions" : true, 59 | "UseLetInEveryBoundCaseVariable" : true, 60 | "UseShorthandTypeNames" : true, 61 | "UseSingleLinePropertyGetter" : true, 62 | "UseSynthesizedInitializer" : true, 63 | "UseTripleSlashForDocumentationComments" : true, 64 | "UseWhereClausesInForLoops" : false, 65 | "ValidateDocumentationComments" : false 66 | }, 67 | "spacesAroundRangeFormationOperators" : false, 68 | "tabWidth" : 8, 69 | "version" : 1 70 | } 71 | -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | identifier_name: 2 | excluded: 3 | - id 4 | - to 5 | - ms 6 | - pa 7 | - i 8 | - j 9 | - k 10 | - x 11 | - y 12 | - z 13 | 14 | disabled_rules: 15 | - multiple_closures_with_trailing_closure 16 | - no_space_in_method_call 17 | - redundant_string_enum_value 18 | - nesting 19 | - switch_case_alignment 20 | 21 | line_length: 22 | warning: 120 23 | ignores_comments: true 24 | ignores_urls: true 25 | ignores_function_declarations: true 26 | ignores_interpolated_strings: true 27 | 28 | opt_in_rules: 29 | - deployment_target 30 | - force_unwrapping 31 | - redundant_type_annotation 32 | - force_try 33 | - operator_usage_whitespace 34 | 35 | deployment_target: 36 | iOSApplicationExtension_deployment_target: "${deployment_target_iOS}" 37 | iOS_deployment_target: "${deployment_target_iOS}" 38 | macOSApplicationExtension_deployment_target: "${deployment_target_macOS}" 39 | macOS_deployment_target: "${deployment_target_macOS}" 40 | tvOSApplicationExtension_deployment_target: "${deployment_target_tvOS}" 41 | tvOS_deployment_target: "${deployment_target_tvOS}" 42 | watchOSApplicationExtension_deployment_target: "${deployment_target_watchOS}" 43 | watchOS_deployment_target: "${deployment_target_watchOS}" 44 | -------------------------------------------------------------------------------- /Configuration/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Debug.xcconfig 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 12.09.2024. 6 | // 7 | 8 | // Configuration settings file format documentation can be found at: 9 | // https://help.apple.com/xcode/#/dev745c5c974 10 | 11 | #include "General.xcconfig" 12 | #include? "Secret.xcconfig" 13 | 14 | // DEBUG-Specific settings 15 | APP_DISPLAY_NAME = Sensor-App 16 | PRIMARY_APP_ICON = AppIcon-V1 17 | -------------------------------------------------------------------------------- /Configuration/General.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // General.xcconfig 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 12.09.2024. 6 | // 7 | 8 | // Configuration settings file format documentation can be found at: 9 | // https://help.apple.com/xcode/#/dev745c5c974 10 | 11 | // Versioning 12 | MY_BUILD_VERSION = 20250601.1 13 | MY_VERSION = 5.2.0 14 | 15 | // Secrets 16 | // 17 | // To build the app with your own secrets, create "Secret.xcconfig" file and define below Variable inside your Secret.xcconfig file. 18 | BASE_BUNDLE_IDENTIFIER = Sensor-App.${DEVELOPMENT_TEAM} 19 | 20 | // Deployment Targets for Xcode 21 | IPHONEOS_DEPLOYMENT_TARGET = 18.0 22 | WATCHOS_DEPLOYMENT_TARGET = 11.0 23 | //TVOS_DEPLOYMENT_TARGET = 18.0 24 | //MACOSX_DEPLOYMENT_TARGET = 15.0 25 | //XROS_DEPLOYMENT_TARGET = 2.0 26 | 27 | // Deployment Targets for Swiftlint 28 | deployment_target_iOS = 18.0 29 | deployment_target_watchOS = 11.0 30 | deployment_target_tvOS = 18.0 31 | deployment_target_macOS = 15.0 32 | -------------------------------------------------------------------------------- /Configuration/Release.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Release.xcconfig 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 12.09.2024. 6 | // 7 | 8 | // Configuration settings file format documentation can be found at: 9 | // https://help.apple.com/xcode/#/dev745c5c974 10 | 11 | #include "General.xcconfig" 12 | #include? "Secret.xcconfig" 13 | 14 | // RELEASE-specific settings 15 | APP_DISPLAY_NAME = Sensor-App 16 | PRIMARY_APP_ICON = AppIcon-V3 17 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Volker 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Sensor-App-Framework/API/AppUpdates.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppUpdates.swift 3 | // Sensor-App-Framework 4 | // 5 | // Created by Volker Schmitt on 11.01.20. 6 | // Copyright © 2020 Volker Schmitt. All rights reserved. 7 | // 8 | 9 | import OSLog 10 | import SwiftUI 11 | 12 | @MainActor 13 | @Observable 14 | public class AppUpdates { 15 | 16 | public init() {} 17 | 18 | public var showReleaseNotes = false 19 | 20 | public let settings = SettingsManager() 21 | 22 | /// Call this function to check if the app version is up to date 23 | public func checkForUpdate() { 24 | let appVersion = UserDefaults.standard.string(forKey: "CurrentAppVersion") 25 | 26 | if appVersion == getCurrentAppVersion() { 27 | Logger.appUpdate.debug("App is up to date! (\(self.getCurrentAppVersion()))") 28 | } else { 29 | Logger.appUpdate.debug("App is not up to date! (\(self.getCurrentAppVersion()))") 30 | if appVersion == nil { 31 | Logger.appUpdate.debug("App is opened the first time") 32 | } else { 33 | showReleaseNotes = settings.fetchUserSettings().showReleaseNotes 34 | Logger.appUpdate.debug("Show Release Notes") 35 | } 36 | updateApp() 37 | } 38 | } 39 | 40 | /// Method to update the app 41 | /// 42 | /// This function will clear old userDefaults and save the Current App Version 43 | /// - Note: Add additional tasks which need to be performed with a new app version 44 | private func updateApp() { 45 | settings.clearUserDefaults() 46 | UserDefaults.standard.setValue(getCurrentAppVersion(), forKey: "CurrentAppVersion") 47 | Logger.appUpdate.debug("App has been updated (\(self.getCurrentAppVersion()))") 48 | } 49 | 50 | /// Call this method to get the current app version number 51 | /// 52 | /// - Returns: App Version 53 | private func getCurrentAppVersion() -> String { 54 | let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] 55 | let version = (appVersion as! String) // swiftlint:disable:this force_cast 56 | 57 | return version 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Sensor-App-Framework/API/ExportManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExportManager.swift 3 | // Sensor-App-Framework 4 | // 5 | // Created by Volker Schmitt on 29.06.20. 6 | // Copyright © 2020 Volker Schmitt. All rights reserved. 7 | // 8 | 9 | import OSLog 10 | import SwiftUI 11 | 12 | public class ExportManager { 13 | 14 | public init() {} 15 | 16 | /// Export File 17 | /// 18 | /// Generate file and open Share Sheet 19 | /// - Parameters: 20 | /// - exportText: String 21 | /// - filename: String 22 | /// - fileExtension: String 23 | /// - Returns: URL? 24 | public func getFile(exportText: String, filename: String, fileExtension: String = ".csv") -> URL { 25 | // swiftlint:disable force_unwrapping 26 | let fileName = "\(filename)\(fileExtension)" 27 | let path = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(fileName) 28 | 29 | let exportText: String = exportText 30 | 31 | do { 32 | try exportText.write(to: path!, atomically: true, encoding: String.Encoding.utf8) 33 | Logger.exportFile.debug("\(path!)") 34 | Logger.exportFile.debug("\(exportText)") 35 | } catch { 36 | Logger.exportFile.error("\(error)") 37 | } 38 | 39 | return path! 40 | // swiftlint:enable force_unwrapping 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sensor-App-Framework/Customization/Extension/Extension+Bundle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extension+Bundle.swift 3 | // Sensor-App-Framework 4 | // 5 | // Created by Volker Schmitt on 11.10.20. 6 | // 7 | 8 | import SwiftUI 9 | 10 | extension Bundle { 11 | func decode( 12 | _ type: T.Type, from file: String, dateDecodingStrategy: JSONDecoder.DateDecodingStrategy = .deferredToDate, 13 | keyDecodingStrategy: JSONDecoder.KeyDecodingStrategy = .useDefaultKeys 14 | ) -> T { 15 | guard let url = self.url(forResource: file, withExtension: nil) else { 16 | fatalError("Failed to locate \(file) in bundle.") 17 | } 18 | 19 | guard let data = try? Data(contentsOf: url) else { 20 | fatalError("Failed to load \(file) from bundle.") 21 | } 22 | 23 | let decoder = JSONDecoder() 24 | decoder.dateDecodingStrategy = dateDecodingStrategy 25 | decoder.keyDecodingStrategy = keyDecodingStrategy 26 | 27 | do { 28 | return try decoder.decode(T.self, from: data) 29 | } catch DecodingError.keyNotFound(let key, let context) { 30 | fatalError( 31 | "Failed to decode \(file) from bundle due to missing key '\(key.stringValue)' not found – \(context.debugDescription)" 32 | ) 33 | } catch DecodingError.typeMismatch(_, let context) { 34 | fatalError("Failed to decode \(file) from bundle due to type mismatch – \(context.debugDescription)") 35 | } catch DecodingError.valueNotFound(let type, let context) { 36 | fatalError( 37 | "Failed to decode \(file) from bundle due to missing \(type) value – \(context.debugDescription)") 38 | } catch DecodingError.dataCorrupted(_) { 39 | fatalError("Failed to decode \(file) from bundle because it appears to be invalid JSON") 40 | } catch { 41 | fatalError("Failed to decode \(file) from bundle: \(error.localizedDescription)") 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Sensor-App-Framework/Customization/Extension/Extension+Double.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extension-Double.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 11.10.19. 6 | // Copyright © 2019 Volker Schmitt. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | 11 | extension Double { 12 | /// Round Doubles 13 | /// 14 | /// Call this function to round Doubles to X decimal digits 15 | /// - Parameter places: Decimal digits 16 | /// - Returns: Rounded Double 17 | public func rounded(toPlaces places: Int) -> Double { 18 | let divisor = pow(10.0, Double(places)) 19 | return (self * divisor).rounded() / divisor 20 | } 21 | 22 | /// Localize Decimalnumber 23 | /// - Parameter maxDecimal: Int 24 | /// - Returns: String 25 | public func localizedDecimal(_ maxDecimal: Int = 20) -> String { 26 | let formatter = NumberFormatter() 27 | formatter.numberStyle = .decimal 28 | formatter.maximumFractionDigits = maxDecimal 29 | let localized = formatter.string(from: NSNumber(value: self)) 30 | 31 | return localized ?? "" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Sensor-App-Framework/Customization/Extension/Extension+Logger.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extension+Logger.swift 3 | // Sensor-App-Framework 4 | // 5 | // Created by Volker Schmitt on 29.08.20. 6 | // 7 | 8 | import Foundation 9 | import OSLog 10 | 11 | extension Logger { 12 | /// Application bundle identifier 13 | private static let subsystem = Bundle.main.bundleIdentifier! // swiftlint:disable:this force_unwrapping 14 | 15 | /// Log ``scenePhase`` events 16 | public static let scenePhase = Logger(subsystem: subsystem, category: "scenePhase") 17 | 18 | /// Log ``viewCycle`` events 19 | public static let viewCycle = Logger(subsystem: subsystem, category: "viewCycle") 20 | 21 | /// Log ``AppUpdates`` events 22 | public static let appUpdate = Logger(subsystem: subsystem, category: "appUpdate") 23 | 24 | /// Log ``coreLocation`` events 25 | public static let coreLocation = Logger(subsystem: subsystem, category: "coreLocation") 26 | 27 | /// Log ``coreMotion`` events 28 | public static let coreMotion = Logger(subsystem: subsystem, category: "coreMotion") 29 | 30 | /// Log ``ExportManager`` events 31 | public static let exportFile = Logger(subsystem: subsystem, category: "exportFile") 32 | 33 | /// Log ``userDefaults`` events 34 | public static let userDefaults = Logger(subsystem: subsystem, category: "userDefaults") 35 | } 36 | -------------------------------------------------------------------------------- /Sensor-App-Framework/Customization/Extension/Extension+String.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extension+String.swift 3 | // Sensor-App-Framework 4 | // 5 | // Created by Volker Schmitt on 11.10.20. 6 | // 7 | 8 | import SwiftUI 9 | 10 | extension String: Swift.Identifiable { 11 | public var id: String { 12 | self 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Sensor-App-Framework/Customization/Extension/Extension+URL.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extension+URL.swift 3 | // Sensor-App-Framework 4 | // 5 | // Created by Volker Schmitt on 10.10.20. 6 | // 7 | 8 | import SwiftUI 9 | 10 | extension Foundation.URL: Swift.Identifiable { 11 | public var id: URL { 12 | self 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Sensor-App-Framework/Localization/Helper/PreviewLocalizationModifier.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PreviewLocalizationModifier.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 11/24/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | /// Wraps as view into a ``PreviewLocalizationModifier`` 11 | public struct PreviewLocalizationModifier: ViewModifier { 12 | 13 | public let locale: String 14 | 15 | public func body(content: Content) -> some View { 16 | content 17 | .environment(\.locale, .init(identifier: locale)) 18 | } 19 | } 20 | 21 | extension View { 22 | /// Wraps as view into a ``PreviewLocalizationModifier`` 23 | /// 24 | /// - Returns: ``View`` 25 | public func previewLocalization(_ language: SupportedLanguage) -> some View { 26 | modifier(PreviewLocalizationModifier(locale: language.rawValue)) 27 | } 28 | 29 | /// Wraps as view into a ``PreviewLocalizationModifier`` 30 | /// 31 | /// - Returns: ``View`` 32 | public func previewLocalization(locale: String) -> some View { 33 | modifier(PreviewLocalizationModifier(locale: locale)) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Sensor-App-Framework/Localization/Helper/SupportedLanguage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SupportedLanguage.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 11/24/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public enum SupportedLanguage: String { 11 | case english = "en_US" 12 | case chinese = "zh_Hans" 13 | case czech = "cs_CZ" 14 | case french = "fr_FR" 15 | case german = "de_DE" 16 | case italien = "it_IT" 17 | case japanese = "ja_JP" 18 | case korean = "ko_KR" 19 | case portuguese = "pt_PT" 20 | case spanish = "es_ES" 21 | } 22 | -------------------------------------------------------------------------------- /Sensor-App-Framework/Model/AltitudeModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AltitudeModel.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 03.09.19. 6 | // Copyright © 2019 Volker Schmitt. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | @MainActor 12 | public struct AltitudeModel: Hashable { 13 | public let counter: Int 14 | public let timestamp: String 15 | public let pressureValue: Double 16 | public let relativeAltitudeValue: Double 17 | } 18 | 19 | extension AltitudeModel { 20 | public func graphValue(for graph: GraphDetail) -> Double { 21 | switch graph { 22 | case .pressureValue: return pressureValue 23 | case .relativeAltitudeValue: return relativeAltitudeValue 24 | default: return 0 25 | } 26 | } 27 | 28 | public var calculatedPressure: Double { 29 | let calculation = CalculationManager() 30 | let pressureSetting = SettingsManager().fetchUserSettings().pressureSetting 31 | 32 | return calculation.calculatePressure(pressure: pressureValue, to: pressureSetting) 33 | } 34 | 35 | public var pressureUnit: String { 36 | let pressureSettings = SettingsManager().fetchUserSettings().pressureSetting 37 | 38 | return pressureSettings 39 | } 40 | 41 | public var calculatedAltitude: Double { 42 | let calculation = CalculationManager() 43 | let altitudeSetting = SettingsManager().fetchUserSettings().altitudeHeightSetting 44 | 45 | return calculation.calculateHeight(height: relativeAltitudeValue, to: altitudeSetting) 46 | } 47 | 48 | public var altitudeUnit: String { 49 | let altitudeSetting = SettingsManager().fetchUserSettings().altitudeHeightSetting 50 | 51 | return altitudeSetting 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Sensor-App-Framework/Model/GraphDetailModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GraphDetailModel.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 24.01.20. 6 | // Copyright © 2020 Volker Schmitt. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum GraphDetail { 12 | // LocationModel 13 | case latitude 14 | case longitude 15 | case altitude 16 | case speed 17 | case course 18 | case horizontalAccuracy 19 | case verticalAccuracy 20 | case GPSAccuracy 21 | 22 | // MotionModel 23 | case accelerationXAxis 24 | case accelerationYAxis 25 | case accelerationZAxis 26 | case gravityXAxis 27 | case gravityYAxis 28 | case gravityZAxis 29 | case gyroXAxis 30 | case gyroYAxis 31 | case gyroZAxis 32 | case magnetometerXAxis 33 | case magnetometerYAxis 34 | case magnetometerZAxis 35 | case attitudeRoll 36 | case attitudePitch 37 | case attitudeYaw 38 | case attitudeHeading 39 | 40 | // AltitudeModel 41 | case pressureValue 42 | case relativeAltitudeValue 43 | } 44 | 45 | public enum Graph { 46 | case location 47 | case motion 48 | case altitude 49 | } 50 | -------------------------------------------------------------------------------- /Sensor-App-Framework/Model/LocationModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LocationModel.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 02.09.19. 6 | // Copyright © 2019 Volker Schmitt. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | @MainActor 12 | public struct LocationModel: Hashable { 13 | public let counter: Int // Counter 14 | public var longitude: Double // Longitude in Degrees 15 | public var latitude: Double // Latitude in Degrees 16 | public var altitude: Double // Altitude measures in Meters 17 | public var speed: Double // Speed in meter per second 18 | public var course: Double // Direction the device is travelling in degrees relative to north 19 | public var horizontalAccuracy: Double // Radius of uncertainity in Meters 20 | public var verticalAccuracy: Double // Accuracy in Meters 21 | public var timestamp: String // Timestamp of the measurement 22 | public var GPSAccuracy: Double // GPS Desired Accuracy 23 | } 24 | 25 | extension LocationModel { 26 | public func graphValue(for graph: GraphDetail) -> Double { 27 | switch graph { 28 | case .latitude: return latitude 29 | case .longitude: return longitude 30 | case .altitude: return altitude 31 | case .speed: return calculatedSpeed 32 | case .course: return course 33 | case .horizontalAccuracy: return horizontalAccuracy 34 | case .verticalAccuracy: return verticalAccuracy 35 | case .GPSAccuracy: return GPSAccuracy 36 | default: return 0 37 | } 38 | } 39 | 40 | public var calculatedSpeed: Double { 41 | let calculation = CalculationManager() 42 | let speedSettings = SettingsManager().fetchUserSettings().GPSSpeedSetting 43 | 44 | return calculation.calculateSpeed(ms: speed, to: speedSettings) 45 | } 46 | 47 | public var speedUnit: String { 48 | let speedSettings = SettingsManager().fetchUserSettings().GPSSpeedSetting 49 | 50 | return speedSettings 51 | } 52 | 53 | public var calculatedAltitude: Double { 54 | let calculation = CalculationManager() 55 | let heightSettings = SettingsManager().fetchUserSettings().altitudeHeightSetting 56 | 57 | return calculation.calculateHeight(height: altitude, to: heightSettings) 58 | } 59 | 60 | public var heightUnit: String { 61 | let heightSettings = SettingsManager().fetchUserSettings().altitudeHeightSetting 62 | 63 | return heightSettings 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Sensor-App-Framework/Model/MapKitSettings.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MapKitSettings.swift 3 | // Sensor-App-Framework 4 | // 5 | // Created by Volker Schmitt on 28.03.20. 6 | // Copyright © 2020 Volker Schmitt. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum MapType: String, Codable, CaseIterable { 12 | case standard = "Standard" 13 | case satellite = "Satellite" 14 | case hybrid = "Hybrid" 15 | case satelliteFlyover = "Satellite Flyover" 16 | case hybridFlyover = "Hybrid Flyover" 17 | case mutedStandard = "Muted Standard" 18 | } 19 | 20 | public struct MapKitSettings: Codable { 21 | public var showsCompass: Bool 22 | public var showsScale: Bool 23 | public var showsBuildings: Bool 24 | public var showsTraffic: Bool 25 | public var isRotateEnabled: Bool 26 | public var isPitchEnabled: Bool 27 | public var isScrollEnabled: Bool 28 | public var mapType: MapType 29 | public var zoom: Double 30 | } 31 | -------------------------------------------------------------------------------- /Sensor-App-Framework/Model/MotionModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MotionModel.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 02.09.19. 6 | // Copyright © 2019 Volker Schmitt. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct MotionModel: Hashable { 12 | public let counter: Int 13 | public let timestamp: String 14 | public let accelerationXAxis: Double 15 | public let accelerationYAxis: Double 16 | public let accelerationZAxis: Double 17 | public let gravityXAxis: Double 18 | public let gravityYAxis: Double 19 | public let gravityZAxis: Double 20 | public let gyroXAxis: Double 21 | public let gyroYAxis: Double 22 | public let gyroZAxis: Double 23 | public let magnetometerCalibration: Int 24 | public let magnetometerXAxis: Double 25 | public let magnetometerYAxis: Double 26 | public let magnetometerZAxis: Double 27 | public let attitudeRoll: Double 28 | public let attitudePitch: Double 29 | public let attitudeYaw: Double 30 | public let attitudeHeading: Double 31 | } 32 | 33 | extension MotionModel { 34 | public func graphValue(for graph: GraphDetail) -> Double { // swiftlint:disable:this cyclomatic_complexity 35 | switch graph { 36 | case .accelerationXAxis: return accelerationXAxis 37 | case .accelerationYAxis: return accelerationYAxis 38 | case .accelerationZAxis: return accelerationZAxis 39 | case .gravityXAxis: return gravityXAxis 40 | case .gravityYAxis: return gravityYAxis 41 | case .gravityZAxis: return gravityZAxis 42 | case .gyroXAxis: return gyroXAxis 43 | case .gyroYAxis: return gyroYAxis 44 | case .gyroZAxis: return gyroZAxis 45 | case .magnetometerXAxis: return magnetometerXAxis 46 | case .magnetometerYAxis: return magnetometerYAxis 47 | case .magnetometerZAxis: return magnetometerZAxis 48 | case .attitudeRoll: return attitudeRoll * 180 / .pi 49 | case .attitudePitch: return attitudePitch * 180 / .pi 50 | case .attitudeYaw: return attitudeYaw * 180 / .pi 51 | case .attitudeHeading: return attitudeHeading 52 | default: return 0 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Sensor-App-Framework/Model/SettingsForUserDefaults.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsForUserDefaults.swift 3 | // Sensor-App-Framework 4 | // 5 | // Created by Volker Schmitt on 16.02.20. 6 | // Copyright © 2020 Volker Schmitt. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum SettingsForUserDefaults { 12 | case GPSSpeedSetting 13 | case GPSAccuracySetting 14 | case frequencySetting 15 | case pressureSetting 16 | case altitudeHeightSetting 17 | } 18 | -------------------------------------------------------------------------------- /Sensor-App-Framework/Model/UserSettings.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserSettings.swift 3 | // Sensor-App-Framework 4 | // 5 | // Created by Volker Schmitt on 16.02.20. 6 | // Copyright © 2020 Volker Schmitt. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SwiftUI 11 | 12 | public struct UserSettings: Codable { 13 | public var showReleaseNotes: Bool 14 | public var GPSSpeedSetting: String 15 | public var GPSAccuracySetting: String 16 | public var frequencySetting: Double 17 | public var pressureSetting: String 18 | public var altitudeHeightSetting: String 19 | public var graphMaxPoints: Double 20 | 21 | public func graphMaxPointsInt() -> Int { 22 | Int(graphMaxPoints) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Sensor-App-WatchApp/Layout/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // Sensor-App-WatchApp Extension 4 | // 5 | // Created by Volker Schmitt on 20.10.19. 6 | // Copyright © 2019 Volker Schmitt. All rights reserved. 7 | // 8 | 9 | import Sensor_App_Framework 10 | import SwiftUI 11 | 12 | struct ContentView: View { 13 | 14 | @State private var showSettings = false 15 | 16 | // MARK: - Body 17 | var body: some View { 18 | List { 19 | NavigationLink(destination: LocationView()) { 20 | Text("Location", comment: "ContentView - Location (watchOS)") 21 | } 22 | .accessibilityIdentifier(UIIdentifiers.ContentView.locationButton) 23 | 24 | NavigationLink(destination: AccelerationView()) { 25 | Text("Acceleration", comment: "ContentView - Acceleration (watchOS)") 26 | } 27 | .accessibilityIdentifier(UIIdentifiers.ContentView.accelerationButton) 28 | 29 | NavigationLink(destination: GravityView()) { 30 | Text("Gravity", comment: "ContentView - Gravity (watchOS)") 31 | } 32 | NavigationLink(destination: GyroscopeView()) { 33 | Text("Gyroscope", comment: "ContentView - Gyroscope (watchOS)") 34 | } 35 | NavigationLink(destination: MagnetometerView()) { 36 | Text("Magnetometer", comment: "ContentView - Magnetometer (watchOS)") 37 | } 38 | NavigationLink(destination: AttitudeView()) { 39 | Text("Attitude", comment: "ContentView - Attitude (watchOS)") 40 | } 41 | NavigationLink(destination: AltitudeView()) { 42 | Text("Altitude", comment: "ContentView - Altitude (watchOS)") 43 | } 44 | NavigationLink(destination: SettingsView()) { 45 | Text("Settings", comment: "ContentView - Settings (watchOS)") 46 | } 47 | .accessibilityIdentifier(UIIdentifiers.ContentView.settingsButton) 48 | } 49 | .accessibilityIdentifier(UIIdentifiers.ContentView.collectionView) 50 | .navigationTitle(Text("Home", comment: "NavigationBar Title - Home screen")) 51 | .listStyle(CarouselListStyle()) 52 | } 53 | } 54 | 55 | // MARK: - Preview 56 | #Preview("ContentView - English") { 57 | ContentView() 58 | } 59 | 60 | #Preview("ContentView - German") { 61 | ContentView() 62 | .previewLocalization(.german) 63 | } 64 | -------------------------------------------------------------------------------- /Sensor-App-WatchApp/Layout/Sensor_AppApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Sensor_AppApp.swift 3 | // Sensor-App-WatchApp Extension 4 | // 5 | // Created by Volker Schmitt on 19.07.20. 6 | // 7 | 8 | import Sensor_App_Framework 9 | import SwiftUI 10 | 11 | @main 12 | struct SensorAppApp: App { 13 | 14 | @State private var locationManager = LocationManager() 15 | @State private var motionManager = MotionManager() 16 | @State private var settingsManager = SettingsManager() 17 | @State private var calculationManager = CalculationManager() 18 | 19 | // MARK: - Body 20 | var body: some Scene { 21 | WindowGroup { 22 | NavigationStack { 23 | ContentView() 24 | } 25 | .environment(locationManager) 26 | .environment(motionManager) 27 | .environment(calculationManager) 28 | .environment(settingsManager) 29 | } 30 | 31 | WKNotificationScene(controller: NotificationController.self, category: "myCategory") 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Sensor-App-WatchApp/Miscellaneous/ComplicationController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ComplicationController.swift 3 | // Sensor-App-WatchApp Extension 4 | // 5 | // Created by Volker Schmitt on 19.07.20. 6 | // 7 | 8 | import ClockKit 9 | 10 | class ComplicationController: NSObject, CLKComplicationDataSource { 11 | 12 | // MARK: - Complication Configuration 13 | 14 | func getComplicationDescriptors(handler: @escaping ([CLKComplicationDescriptor]) -> Void) { 15 | let descriptors = [ 16 | CLKComplicationDescriptor( 17 | identifier: "complication", displayName: "Sensor-App", supportedFamilies: CLKComplicationFamily.allCases 18 | ) 19 | // Multiple complication support can be added here with more descriptors 20 | ] 21 | 22 | // Call the handler with the currently supported complication descriptors 23 | handler(descriptors) 24 | } 25 | 26 | func handleSharedComplicationDescriptors(_ complicationDescriptors: [CLKComplicationDescriptor]) { 27 | // Do any necessary work to support these newly shared complication descriptors 28 | } 29 | 30 | // MARK: - Timeline Configuration 31 | 32 | func getTimelineEndDate(for complication: CLKComplication, withHandler handler: @escaping (Date?) -> Void) { 33 | // Call the handler with the last entry date you can currently provide or nil if you can't support future timelines 34 | handler(nil) 35 | } 36 | 37 | func getPrivacyBehavior( 38 | for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationPrivacyBehavior) -> Void 39 | ) { 40 | // Call the handler with your desired behavior when the device is locked 41 | handler(.showOnLockScreen) 42 | } 43 | 44 | // MARK: - Timeline Population 45 | 46 | func getCurrentTimelineEntry( 47 | for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTimelineEntry?) -> Void 48 | ) { 49 | // Call the handler with the current timeline entry 50 | handler(nil) 51 | } 52 | 53 | func getTimelineEntries( 54 | for complication: CLKComplication, after date: Date, limit: Int, 55 | withHandler handler: @escaping ([CLKComplicationTimelineEntry]?) -> Void 56 | ) { 57 | // Call the handler with the timeline entries after the given date 58 | handler(nil) 59 | } 60 | 61 | // MARK: - Sample Templates 62 | 63 | func getLocalizableSampleTemplate( 64 | for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTemplate?) -> Void 65 | ) { 66 | // This method will be called once per supported complication, and the results will be cached 67 | handler(nil) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Sensor-App-WatchApp/Miscellaneous/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Sensor-App 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | ${MY_VERSION} 21 | CFBundleVersion 22 | ${MY_BUILD_VERSION} 23 | NSLocationWhenInUseUsageDescription 24 | Location access is needed to display your current position, travel direction, and speed in real time 25 | UISupportedInterfaceOrientations 26 | 27 | UIInterfaceOrientationPortrait 28 | UIInterfaceOrientationPortraitUpsideDown 29 | 30 | WKApplication 31 | 32 | WKCompanionAppBundleIdentifier 33 | $(BUNDLE_IDENTIFIER) 34 | WKRunsIndependentlyOfCompanionApp 35 | 36 | NSMotionUsageDescription 37 | Motion sensor access is required to display real-time movement data 38 | 39 | 40 | -------------------------------------------------------------------------------- /Sensor-App-WatchApp/Miscellaneous/NotificationController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NotificationController.swift 3 | // Sensor-App-WatchApp Extension 4 | // 5 | // Created by Volker Schmitt on 19.07.20. 6 | // 7 | 8 | import SwiftUI 9 | import UserNotifications 10 | import WatchKit 11 | 12 | class NotificationController: WKUserNotificationHostingController { 13 | 14 | override var body: NotificationView { 15 | return NotificationView() 16 | } 17 | 18 | override func didReceive(_ notification: UNNotification) { 19 | // This method is called when a notification needs to be presented. 20 | // Implement it if you use a dynamic notification interface. 21 | // Populate your dynamic notification interface as quickly as possible. 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sensor-App-WatchApp/Miscellaneous/NotificationView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NotificationView.swift 3 | // Sensor-App-WatchApp Extension 4 | // 5 | // Created by Volker Schmitt on 19.07.20. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct NotificationView: View { 11 | var body: some View { 12 | Text("Hello, World!") 13 | } 14 | } 15 | 16 | // MARK: - Preview 17 | #Preview { 18 | NotificationView() 19 | } 20 | -------------------------------------------------------------------------------- /Sensor-App-WatchApp/Miscellaneous/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Sensor-App-WatchApp/Miscellaneous/PushNotificationPayload.apns: -------------------------------------------------------------------------------- 1 | { 2 | "aps": { 3 | "alert": { 4 | "body": "Test message", 5 | "title": "Optional title", 6 | "subtitle": "Optional subtitle" 7 | }, 8 | "category": "myCategory", 9 | "thread-id": "5280" 10 | }, 11 | 12 | "WatchKit Simulator Actions": [ 13 | { 14 | "title": "First Button", 15 | "identifier": "firstButtonAction" 16 | } 17 | ], 18 | 19 | "customKey": "Use this file to define a testing payload for your notifications. The aps dictionary specifies the category, alert text and title. The WatchKit Simulator Actions array can provide info for one or more action buttons in addition to the standard Dismiss button. Any other top level keys are custom payload. If you have multiple such JSON files in your project, you'll be able to select them when choosing to debug the notification interface of your Watch App." 20 | } 21 | -------------------------------------------------------------------------------- /Sensor-App-WatchApp/Miscellaneous/UIIdentifiers.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIIdentifiers.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 31.05.25. 6 | // 7 | 8 | import Foundation 9 | 10 | enum UIIdentifiers { 11 | enum ContentView { 12 | // Collection View 13 | static let collectionView = "ContentView.list" 14 | 15 | // Navigation Buttons 16 | static let locationButton = "ContentView.button.location" 17 | static let accelerationButton = "ContentView.button.acceleration" 18 | static let settingsButton = "ContentView.button.settings" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sensor-App-WatchApp/View/AccelerationView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AccelerationView.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 17.11.19. 6 | // Copyright © 2019 Volker Schmitt. All rights reserved. 7 | // 8 | 9 | import Sensor_App_Framework 10 | import SwiftUI 11 | 12 | struct AccelerationView: View { 13 | 14 | @Environment(SettingsManager.self) private var settingsManager 15 | @Environment(MotionManager.self) private var motionManager 16 | 17 | @State private var frequency = 1.0 // Default Frequency 18 | 19 | // MARK: - Body 20 | var body: some View { 21 | List { 22 | Text( 23 | "X-Axis: \(motionManager.motion?.accelerationXAxis ?? 0.0, specifier: "%.5f") m/s^2", 24 | comment: "AccelerationView - X-Axis (watchOS)") 25 | Text( 26 | "Y-Axis: \(motionManager.motion?.accelerationYAxis ?? 0.0, specifier: "%.5f") m/s^2", 27 | comment: "AccelerationView - Y-Axis (watchOS)") 28 | Text( 29 | "Z-Axis: \(motionManager.motion?.accelerationZAxis ?? 0.0, specifier: "%.5f") m/s^2", 30 | comment: "AccelerationView - Z-Axis (watchOS)") 31 | } 32 | .navigationTitle(Text("Acceleration", comment: "NavigationBar Title - Acceleration sensor screen")) 33 | .font(.footnote) 34 | .onAppear(perform: onAppear) 35 | .onDisappear(perform: onDisappear) 36 | } 37 | 38 | // MARK: - Methods 39 | func onAppear() { 40 | frequency = settingsManager.fetchUserSettings().frequencySetting 41 | motionManager.sensorUpdateInterval = frequency 42 | 43 | // Start updating motion 44 | motionManager.sensorUpdateInterval = frequency 45 | motionManager.startMotionUpdates() 46 | } 47 | 48 | func onDisappear() { 49 | motionManager.stopMotionUpdates() 50 | motionManager.resetMotionUpdates() 51 | } 52 | } 53 | 54 | // MARK: - Preview 55 | #Preview("AccelerationView - English") { 56 | AccelerationView() 57 | } 58 | 59 | #Preview("AccelerationView - German") { 60 | AccelerationView() 61 | .previewLocalization(.german) 62 | } 63 | -------------------------------------------------------------------------------- /Sensor-App-WatchApp/View/AltitudeView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AltitudeView.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 17.11.19. 6 | // Copyright © 2019 Volker Schmitt. All rights reserved. 7 | // 8 | 9 | import Sensor_App_Framework 10 | import SwiftUI 11 | 12 | struct AltitudeView: View { 13 | 14 | @Environment(SettingsManager.self) private var settingsManager 15 | @Environment(MotionManager.self) private var motionManager 16 | 17 | @State private var frequency = 1.0 // Default Frequency 18 | 19 | // MARK: - Body 20 | var body: some View { 21 | List { 22 | Text( 23 | "Pressure: \(motionManager.altitude?.calculatedPressure ?? 0.0, specifier: "%.5f") \(motionManager.altitude?.pressureUnit ?? "")" 24 | ) 25 | Text( 26 | "Altitude change: \(motionManager.altitude?.calculatedAltitude ?? 0.0, specifier: "%.5f") \(motionManager.altitude?.altitudeUnit ?? "")" 27 | ) 28 | } 29 | .navigationTitle(Text("Altitude", comment: "NavigationBar Title - Altitude sensor screen")) 30 | .font(.footnote) 31 | .onAppear(perform: onAppear) 32 | .onDisappear(perform: onDisappear) 33 | } 34 | 35 | // MARK: - Methods 36 | func onAppear() { 37 | frequency = settingsManager.fetchUserSettings().frequencySetting 38 | motionManager.sensorUpdateInterval = frequency 39 | 40 | // Start updating motion 41 | motionManager.startAltitudeUpdates() 42 | } 43 | 44 | func onDisappear() { 45 | motionManager.stopMotionUpdates() 46 | motionManager.resetMotionUpdates() 47 | } 48 | } 49 | 50 | // MARK: - Preview 51 | #Preview("AltitudeView - English") { 52 | AltitudeView() 53 | } 54 | 55 | #Preview("AltitudeView - German") { 56 | AltitudeView() 57 | .previewLocalization(.german) 58 | } 59 | -------------------------------------------------------------------------------- /Sensor-App-WatchApp/View/AttitudeView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AttitudeView.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 17.11.19. 6 | // Copyright © 2019 Volker Schmitt. All rights reserved. 7 | // 8 | 9 | import Sensor_App_Framework 10 | import SwiftUI 11 | 12 | struct AttitudeView: View { 13 | 14 | @Environment(SettingsManager.self) private var settingsManager 15 | @Environment(MotionManager.self) private var motionManager 16 | 17 | @State private var frequency = 1.0 // Default Frequency 18 | 19 | // MARK: - Body 20 | var body: some View { 21 | List { 22 | Text( 23 | "Roll: \((motionManager.motion?.attitudeRoll ?? 0.0) * 180 / .pi, specifier: "%.5f")°", 24 | comment: "AttitudeView - Roll (watchOS)") 25 | Text( 26 | "Pitch: \((motionManager.motion?.attitudePitch ?? 0.0) * 180 / .pi, specifier: "%.5f")°", 27 | comment: "AttitudeView - Pitch (watchOS)") 28 | Text( 29 | "Yaw: \((motionManager.motion?.attitudeYaw ?? 0.0) * 180 / .pi, specifier: "%.5f")°", 30 | comment: "AttitudeView - Yaw (watchOS)") 31 | Text( 32 | "Heading: \(motionManager.motion?.attitudeHeading ?? 0.0, specifier: "%.5f")°", 33 | comment: "AttitudeView - Heading (watchOS)") 34 | } 35 | .navigationTitle(Text("Attitude", comment: "NavigationBar Title - Attitude sensor screen")) 36 | .font(.footnote) 37 | .onAppear(perform: onAppear) 38 | .onDisappear(perform: onDisappear) 39 | } 40 | 41 | // MARK: - Methods 42 | func onAppear() { 43 | frequency = settingsManager.fetchUserSettings().frequencySetting 44 | motionManager.sensorUpdateInterval = frequency 45 | 46 | // Start updating motion 47 | motionManager.startMotionUpdates() 48 | motionManager.sensorUpdateInterval = frequency 49 | } 50 | 51 | func onDisappear() { 52 | motionManager.stopMotionUpdates() 53 | motionManager.resetMotionUpdates() 54 | } 55 | } 56 | 57 | // MARK: - Preview 58 | #Preview("AttitudeView - English") { 59 | AccelerationView() 60 | } 61 | 62 | #Preview("AttitudeView - German") { 63 | AccelerationView() 64 | .previewLocalization(.german) 65 | } 66 | -------------------------------------------------------------------------------- /Sensor-App-WatchApp/View/GravityView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GravityView.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 17.11.19. 6 | // Copyright © 2019 Volker Schmitt. All rights reserved. 7 | // 8 | 9 | import Sensor_App_Framework 10 | import SwiftUI 11 | 12 | struct GravityView: View { 13 | 14 | @Environment(SettingsManager.self) private var settingsManager 15 | @Environment(MotionManager.self) private var motionManager 16 | 17 | @State private var frequency = 1.0 // Default Frequency 18 | 19 | // MARK: - Body 20 | var body: some View { 21 | List { 22 | Text( 23 | "X-Axis: \(motionManager.motion?.gravityXAxis ?? 0.0, specifier: "%.5f") g (9,81 m/s^2)", 24 | comment: "GravityView - X-Axis (watchOS)") 25 | Text( 26 | "Y-Axis: \(motionManager.motion?.gravityYAxis ?? 0.0, specifier: "%.5f") g (9,81 m/s^2)", 27 | comment: "GravityView - Y-Axis (watchOS)") 28 | Text( 29 | "Z-Axis: \(motionManager.motion?.gravityZAxis ?? 0.0, specifier: "%.5f") g (9,81 m/s^2)", 30 | comment: "GravityView - Z-Axis (watchOS)") 31 | } 32 | .navigationTitle(Text("Gravity", comment: "NavigationBar Title - Gravity sensor screen")) 33 | .font(.footnote) 34 | .onAppear(perform: onAppear) 35 | .onDisappear(perform: onDisappear) 36 | } 37 | 38 | // MARK: - Methods 39 | func onAppear() { 40 | frequency = settingsManager.fetchUserSettings().frequencySetting 41 | motionManager.sensorUpdateInterval = frequency 42 | 43 | // Start updating motion 44 | motionManager.sensorUpdateInterval = frequency 45 | motionManager.startMotionUpdates() 46 | } 47 | 48 | func onDisappear() { 49 | motionManager.startMotionUpdates() 50 | motionManager.resetMotionUpdates() 51 | } 52 | } 53 | 54 | // MARK: - Preview 55 | #Preview("GravityView - English") { 56 | GravityView() 57 | } 58 | 59 | #Preview("GravityView - German") { 60 | GravityView() 61 | .previewLocalization(.german) 62 | } 63 | -------------------------------------------------------------------------------- /Sensor-App-WatchApp/View/GyroscopeView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GyroscopeView.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 17.11.19. 6 | // Copyright © 2019 Volker Schmitt. All rights reserved. 7 | // 8 | 9 | import Sensor_App_Framework 10 | import SwiftUI 11 | 12 | struct GyroscopeView: View { 13 | 14 | @Environment(SettingsManager.self) private var settingsManager 15 | @Environment(MotionManager.self) private var motionManager 16 | 17 | @State private var frequency = 1.0 // Default Frequency 18 | 19 | // MARK: - Body 20 | var body: some View { 21 | List { 22 | Text( 23 | "X-Axis: \(motionManager.motion?.gyroXAxis ?? 0.0, specifier: "%.5f") rad/s", 24 | comment: "GyroscopeView - X-Axis (watchOS)") 25 | Text( 26 | "Y-Axis: \(motionManager.motion?.gyroYAxis ?? 0.0, specifier: "%.5f") rad/s", 27 | comment: "GyroscopeView - Y-Axis (watchOS)") 28 | Text( 29 | "Z-Axis: \(motionManager.motion?.gyroZAxis ?? 0.0, specifier: "%.5f") rad/s", 30 | comment: "GyroscopeView - Z-Axis (watchOS)") 31 | } 32 | .navigationTitle(Text("Gyroscope", comment: "NavigationBar Title - Gyroscope sensor screen")) 33 | .font(.footnote) 34 | .onAppear(perform: onAppear) 35 | .onDisappear(perform: onDisappear) 36 | } 37 | 38 | // MARK: - Methods 39 | func onAppear() { 40 | frequency = settingsManager.fetchUserSettings().frequencySetting 41 | motionManager.sensorUpdateInterval = frequency 42 | 43 | // Start updating motion 44 | motionManager.sensorUpdateInterval = frequency 45 | motionManager.startMotionUpdates() 46 | } 47 | 48 | func onDisappear() { 49 | motionManager.stopMotionUpdates() 50 | motionManager.resetMotionUpdates() 51 | } 52 | } 53 | 54 | // MARK: - Preview 55 | #Preview("GyroscopeView - English") { 56 | GyroscopeView() 57 | } 58 | 59 | #Preview("GyroscopeView - German") { 60 | GyroscopeView() 61 | .previewLocalization(.german) 62 | } 63 | -------------------------------------------------------------------------------- /Sensor-App-WatchApp/View/LocationView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LocationView.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 17.11.19. 6 | // Copyright © 2019 Volker Schmitt. All rights reserved. 7 | // 8 | 9 | import Sensor_App_Framework 10 | import SwiftUI 11 | 12 | struct LocationView: View { 13 | 14 | @Environment(LocationManager.self) private var locationManager 15 | @Environment(SettingsManager.self) private var settingsManager 16 | 17 | @State private var frequency = 1.0 // Default Frequency 18 | 19 | // MARK: - Body 20 | var body: some View { 21 | List { 22 | Text( 23 | "Latitude: \(locationManager.location?.latitude ?? 0.0, specifier: "%.10f")° ± \(locationManager.location?.horizontalAccuracy ?? 0.0, specifier: "%.2f")m", 24 | comment: "LocationView - Latitude (watchOS)") 25 | Text( 26 | "Longitude: \(locationManager.location?.longitude ?? 0.0, specifier: "%.10f")° ± \(locationManager.location?.horizontalAccuracy ?? 0.0, specifier: "%.2f")m", 27 | comment: "LocationView - Longitude (watchOS)") 28 | Text( 29 | "Altitude: \(locationManager.location?.altitude ?? 0.0, specifier: "%.2f") ± \(locationManager.location?.verticalAccuracy ?? 0.0, specifier: "%.2f")m", 30 | comment: "LocationView - Altitude (watchOS)") 31 | Text( 32 | "Direction: \(locationManager.location?.course ?? 0.0, specifier: "%.2f")°", 33 | comment: "LocationView - Direction (watchOS)") 34 | Text( 35 | "Speed: \(locationManager.location?.calculatedSpeed ?? 0.0, specifier: "%.1f") \(locationManager.location?.speedUnit ?? "")" 36 | ) 37 | } 38 | .navigationTitle(Text("Location", comment: "NavigationBar Title - Location sensor screen")) 39 | .font(.footnote) 40 | .onAppear(perform: onAppear) 41 | .onDisappear(perform: onDisappear) 42 | } 43 | 44 | // MARK: - Methods 45 | func onAppear() { 46 | locationManager.startLocationUpdates() 47 | } 48 | 49 | func onDisappear() { 50 | locationManager.stopLocationUpdates() 51 | locationManager.resetLocationUpdates() 52 | } 53 | } 54 | 55 | // MARK: - Preview 56 | #Preview("LocationView - English") { 57 | LocationView() 58 | } 59 | 60 | #Preview("LocationView - German") { 61 | LocationView() 62 | .previewLocalization(.german) 63 | } 64 | -------------------------------------------------------------------------------- /Sensor-App-WatchApp/View/MagnetometerView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MagnetometerView.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 17.11.19. 6 | // Copyright © 2019 Volker Schmitt. All rights reserved. 7 | // 8 | 9 | import Sensor_App_Framework 10 | import SwiftUI 11 | 12 | struct MagnetometerView: View { 13 | 14 | @Environment(SettingsManager.self) private var settingsManager 15 | @Environment(MotionManager.self) private var motionManager 16 | 17 | @State private var frequency = 1.0 // Default Frequency 18 | 19 | // MARK: - Body 20 | var body: some View { 21 | List { 22 | Text( 23 | "X-Axis: \(motionManager.motion?.magnetometerXAxis ?? 0.0, specifier: "%.5f") µT", 24 | comment: "MagnetometerView - X-Axis (watchOS)") 25 | Text( 26 | "Y-Axis: \(motionManager.motion?.magnetometerYAxis ?? 0.0, specifier: "%.5f") µT", 27 | comment: "MagnetometerView - Y-Axis (watchOS)") 28 | Text( 29 | "Z-Axis: \(motionManager.motion?.magnetometerZAxis ?? 0.0, specifier: "%.5f") µT", 30 | comment: "MagnetometerView - Z-Axis (watchOS)") 31 | } 32 | .navigationTitle(Text("Magnetometer", comment: "NavigationBar Title - Magnetometer sensor screen")) 33 | .font(.footnote) 34 | .onAppear(perform: onAppear) 35 | .onDisappear(perform: onDisappear) 36 | } 37 | 38 | // MARK: - Methods 39 | func onAppear() { 40 | frequency = settingsManager.fetchUserSettings().frequencySetting 41 | motionManager.sensorUpdateInterval = frequency 42 | 43 | // Start updating motion 44 | motionManager.sensorUpdateInterval = frequency 45 | motionManager.startMotionUpdates() 46 | } 47 | 48 | func onDisappear() { 49 | motionManager.stopMotionUpdates() 50 | motionManager.resetMotionUpdates() 51 | } 52 | } 53 | 54 | // MARK: - Preview 55 | #Preview("MagnetometerView - English") { 56 | MagnetometerView() 57 | } 58 | 59 | #Preview("MagnetometerView - German") { 60 | MagnetometerView() 61 | .previewLocalization(.german) 62 | } 63 | -------------------------------------------------------------------------------- /Sensor-App.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/Sensor-App.png -------------------------------------------------------------------------------- /Sensor-App.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Sensor-App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Sensor-App.xcodeproj/xcshareddata/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /Sensor-App.xcodeproj/xcshareddata/xcschemes/Sensor-App-Framework.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 57 | 58 | 59 | 60 | 62 | 63 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /Sensor-App.xcodeproj/xcuserdata/volkerschmitt.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | MyPlayground (Playground) 1.xcscheme 8 | 9 | isShown 10 | 11 | orderHint 12 | 9 13 | 14 | MyPlayground (Playground) 2.xcscheme 15 | 16 | isShown 17 | 18 | orderHint 19 | 10 20 | 21 | MyPlayground (Playground).xcscheme 22 | 23 | isShown 24 | 25 | orderHint 26 | 5 27 | 28 | ScreenshotTests.xcscheme_^#shared#^_ 29 | 30 | orderHint 31 | 6 32 | 33 | Sensor-App-Framework.xcscheme_^#shared#^_ 34 | 35 | isShown 36 | 37 | orderHint 38 | 4 39 | 40 | Sensor-App-watchOS.xcscheme_^#shared#^_ 41 | 42 | orderHint 43 | 2 44 | 45 | Sensor-App.xcscheme_^#shared#^_ 46 | 47 | orderHint 48 | 0 49 | 50 | 51 | SuppressBuildableAutocreation 52 | 53 | 1527B01624C4AA240024DD78 54 | 55 | primary 56 | 57 | 58 | 1527B02724C4AA240024DD78 59 | 60 | primary 61 | 62 | 63 | 1527B03224C4AA240024DD78 64 | 65 | primary 66 | 67 | 68 | 1527B04624C4AA620024DD78 69 | 70 | primary 71 | 72 | 73 | 1527B07824C4ACA20024DD78 74 | 75 | primary 76 | 77 | 78 | 15F1BBA82CAFCF1E00C80A1F 79 | 80 | primary 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /Sensor-App/Helper/CustomToolbar.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomToolbar.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 17.08.20. 6 | // 7 | 8 | import Sensor_App_Framework 9 | import SwiftUI 10 | 11 | struct CustomToolbar: ToolbarContent { 12 | 13 | @Environment(\.showNotification) private var showNotification 14 | @Environment(LocationManager.self) private var locationManager 15 | @Environment(MotionManager.self) private var motionManager 16 | 17 | var body: some ToolbarContent { 18 | ToolbarItem(placement: .bottomBar) { Spacer() } 19 | 20 | ToolbarItem(placement: .bottomBar) { 21 | Button(action: { 22 | showNotification("Play") 23 | locationManager.startLocationUpdates() 24 | motionManager.startMotionUpdates() 25 | motionManager.startAltitudeUpdates() 26 | }) { 27 | Image(systemName: "play.circle") 28 | } 29 | .accessibilityLabel(Text("Play", comment: "Play Button to start sensor updates")) 30 | .accessibilityIdentifier(UIIdentifiers.Toolbar.playButton) 31 | } 32 | 33 | ToolbarItem(placement: .bottomBar) { Spacer() } 34 | 35 | ToolbarItem(placement: .bottomBar) { 36 | Button(action: { 37 | showNotification("Paused") 38 | locationManager.stopLocationUpdates() 39 | motionManager.stopMotionUpdates() 40 | }) { 41 | Image(systemName: "pause.circle") 42 | } 43 | .accessibilityLabel(Text("Pause", comment: "Pause Button to stop sensor updates")) 44 | .accessibilityIdentifier(UIIdentifiers.Toolbar.pauseButton) 45 | } 46 | 47 | ToolbarItem(placement: .bottomBar) { Spacer() } 48 | 49 | ToolbarItem(placement: .bottomBar) { 50 | Button(action: { 51 | showNotification("Successfully deleted") 52 | locationManager.resetLocationUpdates() 53 | motionManager.resetMotionUpdates() 54 | }) { 55 | Image(systemName: "trash.circle") 56 | } 57 | .accessibilityLabel(Text("Delete", comment: "Delete Button to delete all sensor updates")) 58 | .accessibilityIdentifier(UIIdentifiers.Toolbar.deleteButton) 59 | } 60 | 61 | ToolbarItem(placement: .bottomBar) { Spacer() } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Sensor-App/Helper/Customization/CustomModifier/ConditionalOverlay.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConditionalOverlay.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 18.07.22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ConditionalOverlay: ViewModifier { 11 | let visible: Bool 12 | 13 | func body(content: Content) -> some View { 14 | if visible { 15 | content 16 | .overlay( 17 | Image(systemName: "checkmark") 18 | .bold() 19 | .padding(3) 20 | .foregroundColor(.white), alignment: .bottomTrailing) 21 | } else { 22 | content 23 | } 24 | } 25 | } 26 | 27 | extension View { 28 | func conditionalOverlay(visible: Bool) -> some View { 29 | self.modifier(ConditionalOverlay(visible: visible)) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sensor-App/Helper/Customization/CustomModifier/NavigationBarItemModifier.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NavigationBarItemModifier.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 23.05.20. 6 | // Copyright © 2020 Volker Schmitt. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | 11 | struct NavigationBarItemModifier: ViewModifier { 12 | var accessibility: String 13 | 14 | func body(content: Content) -> some View { 15 | content 16 | .padding(10) 17 | .hoverEffect() 18 | .accessibility(identifier: accessibility) 19 | } 20 | } 21 | 22 | extension View { 23 | /// NavigationBarItem 24 | /// 25 | /// Applies customization to NavigationBarItems 26 | /// 27 | /// - Precondition: iPadOS 13.4 for HoverEffect 28 | /// - Parameter accessibility: String for UI Test 29 | /// - Returns: View 30 | func navigationBarItemModifier(accessibility: String) -> some View { 31 | modifier(NavigationBarItemModifier(accessibility: accessibility)) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Sensor-App/Helper/Customization/Extension/Extension_Map.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extension_Map.swift 3 | // Sensor-App-Framework 4 | // 5 | // Created by Volker Schmitt on 08.08.20. 6 | // 7 | 8 | import MapKit 9 | import Sensor_App_Framework 10 | import SwiftUI 11 | 12 | extension Map { 13 | /// MapStyle 14 | /// - Returns: View 15 | func mapStyle() -> some View { 16 | let settingsAPI = SettingsManager() 17 | let mapKitSettings = settingsAPI.fetchMapKitSettings() 18 | let map = MKMapView.appearance() 19 | 20 | // Map Appearance 21 | switch mapKitSettings.mapType { 22 | case .standard: map.mapType = MKMapType.standard 23 | case .satellite: map.mapType = MKMapType.satellite 24 | case .hybrid: map.mapType = MKMapType.hybrid 25 | case .satelliteFlyover: map.mapType = MKMapType.satelliteFlyover 26 | case .hybridFlyover: map.mapType = MKMapType.hybridFlyover 27 | case .mutedStandard: map.mapType = MKMapType.mutedStandard 28 | } 29 | 30 | map.showsCompass = mapKitSettings.showsCompass 31 | map.showsBuildings = mapKitSettings.showsBuildings 32 | map.showsTraffic = mapKitSettings.showsTraffic 33 | map.showsScale = mapKitSettings.showsScale 34 | 35 | return self 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Sensor-App/Helper/MotionManagerAccessView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MotionManagerAccessView.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 1/14/25. 6 | // 7 | 8 | import Sensor_App_Framework 9 | import SwiftUI 10 | 11 | struct MotionManagerAccessView: View { 12 | 13 | @Environment(MotionManager.self) private var motionManager 14 | 15 | var body: some View { 16 | Group { 17 | if motionManager.authorizationStatus != .authorized { 18 | HStack { 19 | Spacer() 20 | 21 | VStack { 22 | Text( 23 | "Access to Motion Sensor is required", comment: "Access to Motion Sensor is required" 24 | ) 25 | .foregroundColor(.red) 26 | 27 | Button { 28 | if let appSettings = URL(string: UIApplication.openSettingsURLString) { 29 | UIApplication.shared.open(appSettings) 30 | } 31 | } label: { 32 | Text("Open Settings", comment: "Open iOS Settings Menu") 33 | } 34 | } 35 | 36 | Spacer() 37 | } 38 | } 39 | } 40 | } 41 | } 42 | 43 | // MARK: - Preview 44 | #Preview("MotionManagerAccessView - English", traits: .navEmbedded) { 45 | MotionManagerAccessView() 46 | } 47 | 48 | #Preview("MotionManagerAccessView - German", traits: .navEmbedded) { 49 | MotionManagerAccessView() 50 | .previewLocalization(.german) 51 | } 52 | -------------------------------------------------------------------------------- /Sensor-App/Helper/RefreshRateView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RefreshRateView.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 13.09.19. 6 | // Copyright © 2019 Volker Schmitt. All rights reserved. 7 | // 8 | 9 | import Sensor_App_Framework 10 | import SwiftUI 11 | 12 | struct RefreshRateView: View { 13 | 14 | @Environment(SettingsManager.self) private var settingsManager 15 | @Environment(MotionManager.self) private var motionManager 16 | 17 | let show: String 18 | 19 | // MARK: - Body 20 | var body: some View { 21 | Group { 22 | if show == "header" { 23 | HStack { 24 | Stepper(value: Bindable(motionManager).sensorUpdateInterval) { _ in 25 | updateSlider() 26 | } label: { 27 | Text("Frequency: \(Double(motionManager.sensorUpdateInterval), specifier: "%.0f") Hz", 28 | comment: "Frequency of Sensor Updates in Herz") 29 | } 30 | } 31 | } else if show == "slider" { 32 | HStack { 33 | Text("1") 34 | 35 | Slider(value: Bindable(motionManager).sensorUpdateInterval, in: 1...50, step: 1) { _ in 36 | updateSlider() 37 | } 38 | .accessibilityIdentifier(UIIdentifiers.RefreshRateView.refreshRateSlider) 39 | .accessibilityLabel( 40 | Text( 41 | "Refresh Rate", 42 | comment: "Slider to adjust Frequency of Sensor Updates in Herz") 43 | ) 44 | .accessibilityLabel( 45 | Text( 46 | "\(motionManager.sensorUpdateInterval, specifier: "%.0f") per Second", 47 | comment: "RefreshRateView - Value")) 48 | 49 | Text("50") 50 | } 51 | } 52 | } 53 | .onAppear { 54 | motionManager.sensorUpdateInterval = settingsManager.fetchUserSettings().frequencySetting 55 | } 56 | } 57 | 58 | // MARK: - Methods 59 | func updateSlider() { 60 | var userSettings = settingsManager.fetchUserSettings() 61 | userSettings.frequencySetting = motionManager.sensorUpdateInterval 62 | settingsManager.saveUserSettings(userSettings: userSettings) 63 | } 64 | } 65 | 66 | #Preview(traits: .sizeThatFitsLayout, .navEmbedded) { 67 | Group { 68 | RefreshRateView(show: "header") 69 | RefreshRateView(show: "slider") 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Sensor-App/Helper/ShareSheet.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ShareSheet.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 10.08.22. 6 | // 7 | // 8 | 9 | import SwiftUI 10 | 11 | struct ShareSheet: View { 12 | 13 | let url: URL 14 | 15 | // MARK: - Body 16 | var body: some View { 17 | ShareLink(item: url) { 18 | Label { 19 | Text("Export", comment: "Export button to export data as csv file") 20 | } icon: { 21 | Image(systemName: "square.and.arrow.up") 22 | } 23 | } 24 | } 25 | } 26 | 27 | // MARK: - Preview 28 | #Preview { 29 | ShareSheet(url: URL(string: "https://www.apple.com")!) // swiftlint:disable:this force_unwrapping 30 | } 31 | -------------------------------------------------------------------------------- /Sensor-App/Layout/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 08.07.22. 6 | // 7 | 8 | import Sensor_App_Framework 9 | import SwiftUI 10 | 11 | struct ContentView: View { 12 | 13 | @Environment(MotionManager.self) private var motionManager 14 | @Environment(AppState.self) private var appState 15 | 16 | @State private var showSidebar: NavigationSplitViewVisibility = .all 17 | 18 | // MARK: - Body 19 | var body: some View { 20 | NavigationSplitView(columnVisibility: $showSidebar) { 21 | Sidebar() 22 | } detail: { 23 | NavigationStack(path: Bindable(appState).path) { 24 | DetailColumn() 25 | .navigationDestination(for: Route.self) { $0 } 26 | } 27 | } 28 | .onChange(of: appState.selectedScreen) { 29 | motionManager.stopMotionUpdates() 30 | } 31 | } 32 | } 33 | 34 | // MARK: - Preview 35 | #Preview("ContentView - English") { 36 | ContentView() 37 | .environment(AppState()) 38 | .environment(SettingsManager()) 39 | .environment(CalculationManager()) 40 | .environment(MotionManager()) 41 | .environment(LocationManager()) 42 | } 43 | 44 | #Preview("ContentView - German") { 45 | ContentView() 46 | .previewLocalization(.german) 47 | .environment(AppState()) 48 | .environment(SettingsManager()) 49 | .environment(CalculationManager()) 50 | .environment(MotionManager()) 51 | .environment(LocationManager()) 52 | } 53 | -------------------------------------------------------------------------------- /Sensor-App/Layout/DetailColumn.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailColumn.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 08.07.22. 6 | // 7 | 8 | import Sensor_App_Framework 9 | import SwiftUI 10 | 11 | struct DetailColumn: View { 12 | 13 | @Environment(AppState.self) private var appState 14 | 15 | // MARK: - Body 16 | var body: some View { 17 | appState.selectedScreen ?? .homeScreen 18 | } 19 | } 20 | 21 | // MARK: - Preview 22 | #Preview("DetailColumn - English", traits: .navEmbedded) { 23 | ContentView() 24 | } 25 | 26 | #Preview("DetailColumn - German", traits: .navEmbedded) { 27 | ContentView() 28 | .previewLocalization(.german) 29 | } 30 | -------------------------------------------------------------------------------- /Sensor-App/Layout/HomeScreen.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeScreen.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 08.07.22. 6 | // 7 | 8 | import Sensor_App_Framework 9 | import SwiftUI 10 | 11 | struct HomeScreen: View { 12 | 13 | // MARK: - Body 14 | var body: some View { 15 | Text("Welcome to Sensor-App") 16 | .navigationTitle("\(Text("Home", comment: "NavigationBar Title - Home"))") 17 | } 18 | } 19 | 20 | // MARK: - Preview 21 | #Preview("HomeScreen - English", traits: .navEmbedded) { 22 | HomeScreen() 23 | } 24 | 25 | #Preview("HomeScreen - German", traits: .navEmbedded) { 26 | HomeScreen() 27 | .previewLocalization(.german) 28 | } 29 | -------------------------------------------------------------------------------- /Sensor-App/Layout/Navigation/AppState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppState.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 08.07.22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @Observable 11 | class AppState { 12 | var path: [Route] = [] 13 | var selectedScreen: Screen? 14 | } 15 | -------------------------------------------------------------------------------- /Sensor-App/Layout/Navigation/Route.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Route.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 22.05.25. 6 | // 7 | 8 | import SwiftUI 9 | 10 | enum Route: Hashable { 11 | case location 12 | case accelerationList 13 | case altitudeList 14 | case attitudeList 15 | case gravityList 16 | case gyroscopeList 17 | case magnetometerList 18 | } 19 | 20 | extension Route: View { 21 | var body: some View { 22 | switch self { 23 | case .location: 24 | MapView() 25 | case .accelerationList: 26 | AccelerationList() 27 | case .altitudeList: 28 | AltitudeList() 29 | case .attitudeList: 30 | AttitudeList() 31 | case .gravityList: 32 | GravityList() 33 | case .gyroscopeList: 34 | GyroscopeList() 35 | case .magnetometerList: 36 | MagnetometerList() 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Sensor-App/Layout/Navigation/Screen.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Screen.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 22.05.25. 6 | // 7 | 8 | import SwiftUI 9 | 10 | enum Screen: Hashable { 11 | case homeScreen 12 | case location 13 | case acceleration 14 | case gravity 15 | case gyroscope 16 | case magnetometer 17 | case attitude 18 | case altitude 19 | case settings 20 | } 21 | 22 | extension Screen: View { 23 | var body: some View { 24 | switch self { 25 | case .homeScreen: 26 | HomeScreen() 27 | case .location: 28 | LocationScreen() 29 | case .acceleration: 30 | AccelerationScreen() 31 | case .gravity: 32 | GravityScreen() 33 | case .gyroscope: 34 | GyroscopeScreen() 35 | case .magnetometer: 36 | MagnetometerScreen() 37 | case .attitude: 38 | AttitudeScreen() 39 | case .altitude: 40 | AltitudeScreen() 41 | case .settings: 42 | SettingsScreen() 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sensor-App/Layout/SensorAppApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SensorAppApp.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 19.07.20. 6 | // 7 | 8 | import OSLog 9 | import Sensor_App_Framework 10 | import SwiftUI 11 | 12 | @main 13 | struct SensorAppApp: App { 14 | 15 | @Environment(\.scenePhase) private var scenePhase 16 | 17 | @State private var appState = AppState() 18 | @State private var update = AppUpdates() 19 | @State private var locationManager = LocationManager() 20 | @State private var motionManager = MotionManager() 21 | @State private var settingsManager = SettingsManager() 22 | @State private var calculationManager = CalculationManager() 23 | 24 | init() { 25 | #if DEBUG 26 | if CommandLine.arguments.contains("disable-animations") { 27 | UIView.setAnimationsEnabled(false) 28 | } 29 | #endif 30 | } 31 | 32 | var body: some Scene { 33 | WindowGroup { 34 | ContentView() 35 | .environment(appState) 36 | .environment(locationManager) 37 | .environment(motionManager) 38 | .environment(calculationManager) 39 | .environment(settingsManager) 40 | .onChange(of: scenePhase) { _, phase in 41 | switch phase { 42 | case .active: 43 | Logger.scenePhase.debug("ScenePhase: Active") 44 | case .inactive: 45 | Logger.scenePhase.debug("ScenePhase: Inactive") 46 | case .background: 47 | Logger.scenePhase.debug("ScenePhase: Background") 48 | @unknown default: 49 | Logger.scenePhase.debug("ScenePhase: Unknown") 50 | } 51 | } 52 | .onAppear { 53 | update.checkForUpdate() 54 | } 55 | .sheet(isPresented: $update.showReleaseNotes) { ReleaseNotesScreen() } 56 | .withNotificationView() 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Sensor-App/Miscellaneous/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "platform" : "ios", 6 | "reference" : "systemBlueColor" 7 | }, 8 | "idiom" : "universal" 9 | } 10 | ], 11 | "info" : { 12 | "author" : "xcode", 13 | "version" : 1 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Sensor-App/Miscellaneous/Assets.xcassets/App Icons/AppIcon-V1.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "ios-marketing 2.png", 5 | "idiom" : "universal", 6 | "platform" : "ios", 7 | "size" : "1024x1024" 8 | }, 9 | { 10 | "filename" : "ios-marketing 1.png", 11 | "idiom" : "universal", 12 | "platform" : "watchos", 13 | "size" : "1024x1024" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sensor-App/Miscellaneous/Assets.xcassets/App Icons/AppIcon-V1.appiconset/ios-marketing 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/Sensor-App/Miscellaneous/Assets.xcassets/App Icons/AppIcon-V1.appiconset/ios-marketing 1.png -------------------------------------------------------------------------------- /Sensor-App/Miscellaneous/Assets.xcassets/App Icons/AppIcon-V1.appiconset/ios-marketing 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/Sensor-App/Miscellaneous/Assets.xcassets/App Icons/AppIcon-V1.appiconset/ios-marketing 2.png -------------------------------------------------------------------------------- /Sensor-App/Miscellaneous/Assets.xcassets/App Icons/AppIcon-V2.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "ios-marketing 1.png", 5 | "idiom" : "universal", 6 | "platform" : "ios", 7 | "size" : "1024x1024" 8 | }, 9 | { 10 | "filename" : "ios-marketing.png", 11 | "idiom" : "universal", 12 | "platform" : "watchos", 13 | "size" : "1024x1024" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sensor-App/Miscellaneous/Assets.xcassets/App Icons/AppIcon-V2.appiconset/ios-marketing 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/Sensor-App/Miscellaneous/Assets.xcassets/App Icons/AppIcon-V2.appiconset/ios-marketing 1.png -------------------------------------------------------------------------------- /Sensor-App/Miscellaneous/Assets.xcassets/App Icons/AppIcon-V2.appiconset/ios-marketing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/Sensor-App/Miscellaneous/Assets.xcassets/App Icons/AppIcon-V2.appiconset/ios-marketing.png -------------------------------------------------------------------------------- /Sensor-App/Miscellaneous/Assets.xcassets/App Icons/AppIcon-V3.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "ios-marketing.png", 5 | "idiom" : "universal", 6 | "platform" : "ios", 7 | "size" : "1024x1024" 8 | }, 9 | { 10 | "filename" : "ios-marketing 1.png", 11 | "idiom" : "universal", 12 | "platform" : "watchos", 13 | "size" : "1024x1024" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sensor-App/Miscellaneous/Assets.xcassets/App Icons/AppIcon-V3.appiconset/ios-marketing 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/Sensor-App/Miscellaneous/Assets.xcassets/App Icons/AppIcon-V3.appiconset/ios-marketing 1.png -------------------------------------------------------------------------------- /Sensor-App/Miscellaneous/Assets.xcassets/App Icons/AppIcon-V3.appiconset/ios-marketing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/Sensor-App/Miscellaneous/Assets.xcassets/App Icons/AppIcon-V3.appiconset/ios-marketing.png -------------------------------------------------------------------------------- /Sensor-App/Miscellaneous/Assets.xcassets/App Icons/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Sensor-App/Miscellaneous/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Sensor-App/Miscellaneous/Assets.xcassets/Images/AppIcon-V1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "ios-marketing.png", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Sensor-App/Miscellaneous/Assets.xcassets/Images/AppIcon-V1.imageset/ios-marketing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/Sensor-App/Miscellaneous/Assets.xcassets/Images/AppIcon-V1.imageset/ios-marketing.png -------------------------------------------------------------------------------- /Sensor-App/Miscellaneous/Assets.xcassets/Images/AppIcon-V2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "ios-marketing.png", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Sensor-App/Miscellaneous/Assets.xcassets/Images/AppIcon-V2.imageset/ios-marketing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/Sensor-App/Miscellaneous/Assets.xcassets/Images/AppIcon-V2.imageset/ios-marketing.png -------------------------------------------------------------------------------- /Sensor-App/Miscellaneous/Assets.xcassets/Images/AppIcon-V3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "ios-marketing.png", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Sensor-App/Miscellaneous/Assets.xcassets/Images/AppIcon-V3.imageset/ios-marketing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/Sensor-App/Miscellaneous/Assets.xcassets/Images/AppIcon-V3.imageset/ios-marketing.png -------------------------------------------------------------------------------- /Sensor-App/Miscellaneous/Assets.xcassets/Images/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Sensor-App/Miscellaneous/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Sensor-App 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | ${MY_VERSION} 21 | CFBundleVersion 22 | ${MY_BUILD_VERSION} 23 | ITSAppUsesNonExemptEncryption 24 | 25 | LSRequiresIPhoneOS 26 | 27 | NSLocationWhenInUseUsageDescription 28 | Location access is needed to display your current position, travel direction, and speed in real time 29 | NSMotionUsageDescription 30 | Motion sensor access is required to display real-time movement data 31 | UIApplicationSceneManifest 32 | 33 | UIApplicationSupportsMultipleScenes 34 | 35 | 36 | UIApplicationSupportsIndirectInputEvents 37 | 38 | UILaunchScreen 39 | 40 | UIRequiredDeviceCapabilities 41 | 42 | armv7 43 | 44 | UIRequiresFullScreen 45 | 46 | UISupportedInterfaceOrientations 47 | 48 | UIInterfaceOrientationPortrait 49 | UIInterfaceOrientationLandscapeLeft 50 | UIInterfaceOrientationLandscapeRight 51 | UIInterfaceOrientationPortraitUpsideDown 52 | 53 | UISupportedInterfaceOrientations~ipad 54 | 55 | UIInterfaceOrientationPortrait 56 | UIInterfaceOrientationPortraitUpsideDown 57 | UIInterfaceOrientationLandscapeLeft 58 | UIInterfaceOrientationLandscapeRight 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /Sensor-App/Miscellaneous/MyPlayground.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | var str = "Hello, playground" 4 | -------------------------------------------------------------------------------- /Sensor-App/Miscellaneous/MyPlayground.playground/Pages/Array Transformation.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | struct User { 2 | var name: String 3 | var mail: String 4 | var age: Int 5 | } 6 | 7 | var userArray = [User]() 8 | 9 | userArray.append(User(name: "Volker", mail: "abscsd", age: 10)) 10 | userArray.append(User(name: "Volker2", mail: "abscsffdgsd", age: 11)) 11 | userArray.append(User(name: "Volker3", mail: "abscasddsd", age: 12)) 12 | userArray.append(User(name: "Volker4", mail: "abscsdsd", age: 13)) 13 | userArray.append(User(name: "Volker5", mail: "absc", age: 14)) 14 | 15 | print(userArray) 16 | 17 | var userShort = [String]() 18 | 19 | var transform = userArray.map { user in 20 | userShort.append(user.name) 21 | 22 | } 23 | 24 | print(userShort) 25 | 26 | print(userShort[2]) 27 | -------------------------------------------------------------------------------- /Sensor-App/Miscellaneous/MyPlayground.playground/Pages/Array-Search.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | // MARK: - Search Array Index 2 | let searchFor = "km/h" 3 | var searchID = 0 4 | let speedSettings = ["m/s", "km/h", "mph"] 5 | 6 | for index in speedSettings { 7 | 8 | searchID += 1 9 | 10 | if i == searchFor { 11 | break 12 | } 13 | 14 | } 15 | 16 | print(searchID) 17 | 18 | // MARK: - Array Test 19 | class DataArray { 20 | 21 | var id: Int 22 | var xAxis: Double 23 | var yAxis: Double 24 | 25 | init(id: Int, xaxis: Double, yaxis: Double) { 26 | self.id = id 27 | self.xAxis = xaxis 28 | self.yAxis = yaxis 29 | } 30 | } 31 | 32 | var testArray = [DataArray]() 33 | 34 | for index in 0..<100 { 35 | testArray.insert(DataArray(id: i, xaxis: 1.0, yaxis: 2.0), at: index) 36 | } 37 | 38 | print(testArray.last!.id) // swiftlint:disable:this force_unwrapping 39 | 40 | let arr = ["a", "b", "c", "a"] 41 | 42 | let indexOfA = arr.firstIndex(of: "a") // 0 43 | let indexOfB = arr.lastIndex(of: "a") // 3 44 | -------------------------------------------------------------------------------- /Sensor-App/Miscellaneous/MyPlayground.playground/Pages/Call function of parent View.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | let number = 5.12 4 | let formatter = NumberFormatter() 5 | formatter.numberStyle = .decimal 6 | formatter.maximumFractionDigits = 15 7 | let localized = formatter.string(from: NSNumber(value: number)) 8 | -------------------------------------------------------------------------------- /Sensor-App/Miscellaneous/MyPlayground.playground/Pages/Chart Array transformation.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | var maxGraphHeight: Double = 300 2 | 3 | var data: [Double] = [0, 4, 6, 10, 50, 100, -20, -100, -200, 50, -300, 4, 6, 10, 50, 100, -20, -100, -200, -300, -500] 4 | 5 | var transformedArray = [Double]() 6 | 7 | let dataArray = data 8 | 9 | let dataArrayMaxValue = dataArray.map(abs).max()! // swiftlint:disable:this force_unwrapping 10 | 11 | print("Max Value: \(dataArrayMaxValue)") 12 | 13 | let scaleFactor: Double = maxGraphHeight / dataArrayMaxValue 14 | print("Scale Factor: \(scaleFactor)") 15 | 16 | let array = dataArray.map { $0 * scaleFactor } 17 | print(dataArray) 18 | print(array) 19 | -------------------------------------------------------------------------------- /Sensor-App/Miscellaneous/MyPlayground.playground/Pages/Struct-Timestamp.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | // MARK: - Struct for DataArrays 4 | struct DataValues { 5 | var id: [Int] = [] 6 | var timestamp: [String] = [] 7 | var xAxis: [String] = [] 8 | } 9 | 10 | var data = dataValues() 11 | 12 | data.id.insert(1, at: 0) 13 | data.timestamp.insert("test1", at: 0) 14 | data.xAxis.insert("23.23", at: 0) 15 | 16 | print(data.id[0]) 17 | 18 | // MARK: - Timestamp 19 | let dateFormatter = DateFormatter() 20 | dateFormatter.dateFormat = "dd-MM-yyyy HH:mm:ss.SSS" 21 | let dateString = dateFormatter.string(from: NSDate() as Date) 22 | print(dateString) 23 | -------------------------------------------------------------------------------- /Sensor-App/Miscellaneous/MyPlayground.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Sensor-App/Miscellaneous/MyPlayground.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Sensor-App/Miscellaneous/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Sensor-App/Modules/Acceleration/AccelerationList.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AccelerationList.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 23.08.20. 6 | // 7 | 8 | import Sensor_App_Framework 9 | import SwiftUI 10 | 11 | struct AccelerationList: View { 12 | 13 | @Environment(MotionManager.self) private var motionManager 14 | 15 | private let exportManager = ExportManager() 16 | 17 | // MARK: - Body 18 | var body: some View { 19 | List(motionManager.motionArray.reversed(), id: \.self) { item in 20 | HStack { 21 | Text(verbatim: "#\(item.counter)") 22 | Spacer() 23 | Text("X:\(item.accelerationXAxis, specifier: "%.5f")", comment: "First Letter as shortcut for X-Axis") 24 | Spacer() 25 | Text("Y:\(item.accelerationYAxis, specifier: "%.5f")", comment: "First Letter as shortcut for Y-Axis") 26 | Spacer() 27 | Text("Z:\(item.accelerationZAxis, specifier: "%.5f")", comment: "First Letter as shortcut for Z-Axis") 28 | } 29 | .font(.footnote) 30 | } 31 | .listStyle(.plain) 32 | .navigationTitle(Text("Acceleration", comment: "NavigationBar Title - Acceleration sensor list view")) 33 | .toolbar { 34 | ToolbarItem(placement: .navigationBarTrailing) { 35 | ShareSheet(url: shareCSV()) 36 | .accessibilityLabel("Export Acceleration Data to CSV") 37 | .accessibilityIdentifier(UIIdentifiers.AccelerationList.exportButton) 38 | } 39 | CustomToolbar() 40 | } 41 | } 42 | 43 | // MARK: - Methods 44 | func shareCSV() -> URL { 45 | var csvText = 46 | NSLocalizedString("ID;Time;X-Axis;Y-Axis;Z-Axis", comment: "Export CSV Headline - Acceleration") + "\n" // swiftlint:disable:this line_length 47 | 48 | _ = motionManager.motionArray.map { 49 | csvText += 50 | "\($0.counter);\($0.timestamp);\($0.accelerationXAxis.localizedDecimal());\($0.accelerationYAxis.localizedDecimal());\($0.accelerationZAxis.localizedDecimal())\n" 51 | } 52 | return exportManager.getFile(exportText: csvText, filename: "acceleration") 53 | } 54 | } 55 | 56 | // MARK: - Preview 57 | #Preview("AccelerationList - English", traits: .navEmbedded) { 58 | AccelerationList() 59 | } 60 | 61 | #Preview("AccelerationList - German", traits: .navEmbedded) { 62 | AccelerationList() 63 | .previewLocalization(.german) 64 | } 65 | -------------------------------------------------------------------------------- /Sensor-App/Modules/Acceleration/AccelerationScreen.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AccelerationScreen.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 23.08.20. 6 | // 7 | 8 | import Sensor_App_Framework 9 | import SwiftUI 10 | 11 | struct AccelerationScreen: View { 12 | 13 | @Environment(MotionManager.self) private var motionManager 14 | 15 | // MARK: - Body 16 | var body: some View { 17 | AccelerationView() 18 | .toolbar { 19 | CustomToolbar() 20 | } 21 | .navigationTitle(Text("Acceleration", comment: "NavigationBar Title - Acceleration sensor screen")) 22 | .onAppear { 23 | motionManager.startMotionUpdates() 24 | } 25 | } 26 | } 27 | 28 | // MARK: - Preview 29 | #Preview("AccelerationScreen - English", traits: .navEmbedded) { 30 | AccelerationScreen() 31 | } 32 | 33 | #Preview("AccelerationScreen - German", traits: .navEmbedded) { 34 | AccelerationScreen() 35 | .previewLocalization(.german) 36 | } 37 | -------------------------------------------------------------------------------- /Sensor-App/Modules/Altitude/AltitudeList.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AltitudeList.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 23.08.20. 6 | // 7 | 8 | import Sensor_App_Framework 9 | import SwiftUI 10 | 11 | struct AltitudeList: View { 12 | 13 | @Environment(MotionManager.self) private var motionManager 14 | 15 | private let exportManager = ExportManager() 16 | 17 | // MARK: - Body 18 | var body: some View { 19 | List(motionManager.altitudeArray.reversed(), id: \.self) { item in 20 | HStack { 21 | Text(verbatim: "#\(item.counter)") 22 | Spacer() 23 | Text( 24 | "P:\(motionManager.altitude?.calculatedPressure ?? 0.0, specifier: "%.3f")", 25 | comment: "First Letter as shortcut for Pressure") 26 | Spacer() 27 | Text( 28 | "A:\(motionManager.altitude?.calculatedAltitude ?? 0.0, specifier: "%.3f")", 29 | comment: "First Letter as shortcut for Altitude") 30 | } 31 | .font(.footnote) 32 | } 33 | .listStyle(.plain) 34 | .navigationTitle(Text("Altitude", comment: "NavigationBar Title - Altitude sensor list view")) 35 | .toolbar { 36 | ToolbarItem(placement: .navigationBarTrailing) { 37 | ShareSheet(url: shareCSV()) 38 | .accessibilityLabel("Export Altitude Data to CSV") 39 | .accessibilityIdentifier(UIIdentifiers.AltitudeList.exportButton) 40 | } 41 | CustomToolbar() 42 | } 43 | } 44 | 45 | // MARK: - Methods 46 | func shareCSV() -> URL { 47 | var csvText = 48 | NSLocalizedString("ID;Time;Pressure;Altitude change", comment: "Export CSV Headline - altitude") + "\n" // swiftlint:disable:this line_length 49 | 50 | _ = motionManager.altitudeArray.map { 51 | csvText += 52 | "\($0.counter);\($0.timestamp);\((motionManager.altitude?.calculatedPressure ?? 0.0).localizedDecimal());\((motionManager.altitude?.calculatedAltitude ?? 0.0).localizedDecimal())\n" 53 | } 54 | return exportManager.getFile(exportText: csvText, filename: "altitude") 55 | } 56 | } 57 | 58 | // MARK: - Preview 59 | #Preview("AltitudeList - English", traits: .navEmbedded) { 60 | AltitudeList() 61 | } 62 | 63 | #Preview("AltitudeList - German", traits: .navEmbedded) { 64 | AltitudeList() 65 | .previewLocalization(.german) 66 | } 67 | -------------------------------------------------------------------------------- /Sensor-App/Modules/Altitude/AltitudeScreen.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AltitudeScreen.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 23.08.20. 6 | // 7 | 8 | import Sensor_App_Framework 9 | import SwiftUI 10 | 11 | struct AltitudeScreen: View { 12 | 13 | @Environment(MotionManager.self) private var motionManager 14 | 15 | // MARK: - Body 16 | var body: some View { 17 | AltitudeView() 18 | .toolbar { 19 | CustomToolbar() 20 | } 21 | .navigationTitle(Text("Altitude", comment: "NavigationBar Title - Altitude sensor screen")) 22 | .onAppear { 23 | motionManager.startAltitudeUpdates() 24 | } 25 | } 26 | } 27 | 28 | // MARK: - Preview 29 | #Preview("AltitudeScreen - English", traits: .navEmbedded) { 30 | AltitudeScreen() 31 | } 32 | 33 | #Preview("AltitudeScreen - German", traits: .navEmbedded) { 34 | AltitudeScreen() 35 | .previewLocalization(.german) 36 | } 37 | -------------------------------------------------------------------------------- /Sensor-App/Modules/Altitude/AltitudeView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AltitudeView.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 13.09.19. 6 | // Copyright © 2019 Volker Schmitt. All rights reserved. 7 | // 8 | 9 | import Sensor_App_Framework 10 | import SwiftUI 11 | 12 | struct AltitudeView: View { 13 | 14 | @Environment(MotionManager.self) private var motionManager 15 | 16 | @State private var showPressure = false 17 | @State private var showRelativeAltitudeChange = false 18 | 19 | // MARK: - Body 20 | var body: some View { 21 | List { 22 | Section(header: Text("Altitude", comment: "AltitudeView - Section Header")) { 23 | DisclosureGroup( 24 | isExpanded: $showPressure, 25 | content: { 26 | LineGraphSubView(graph: .altitude, showGraph: .pressureValue) 27 | .frame(height: 100, alignment: .leading) 28 | }, 29 | label: { 30 | Text( 31 | "Pressure: \(motionManager.altitude?.calculatedPressure ?? 0.0, specifier: "%.5f") \(motionManager.altitude?.pressureUnit ?? "")" 32 | ) 33 | } 34 | ) 35 | .accessibilityIdentifier(UIIdentifiers.AltitudeView.pressureRow) 36 | 37 | DisclosureGroup( 38 | isExpanded: $showRelativeAltitudeChange, 39 | content: { 40 | LineGraphSubView(graph: .altitude, showGraph: .relativeAltitudeValue) 41 | .frame(height: 100, alignment: .leading) 42 | }, 43 | label: { 44 | Text( 45 | "Altitude change: \(motionManager.altitude?.calculatedAltitude ?? 0.0, specifier: "%.5f") \(motionManager.altitude?.altitudeUnit ?? "")" 46 | ) 47 | } 48 | ) 49 | .accessibilityIdentifier(UIIdentifiers.AltitudeView.altitudeRow) 50 | 51 | NavigationLink(value: Route.altitudeList) { 52 | Text("Log", comment: "AltitudeView - Log") 53 | } 54 | .accessibilityIdentifier(UIIdentifiers.AltitudeView.logButton) 55 | } 56 | 57 | MotionManagerAccessView() 58 | } 59 | .listStyle(.insetGrouped) 60 | } 61 | } 62 | 63 | // MARK: - Preview 64 | #Preview("AltitudeView - English", traits: .navEmbedded) { 65 | AltitudeView() 66 | } 67 | 68 | #Preview("AltitudeView - German", traits: .navEmbedded) { 69 | AltitudeView() 70 | .previewLocalization(.german) 71 | } 72 | -------------------------------------------------------------------------------- /Sensor-App/Modules/Attitude/AttitudeList.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AttitudeList.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 23.08.20. 6 | // 7 | 8 | import Sensor_App_Framework 9 | import SwiftUI 10 | 11 | struct AttitudeList: View { 12 | 13 | @Environment(MotionManager.self) private var motionManager 14 | 15 | private let exportManager = ExportManager() 16 | 17 | // MARK: - Body 18 | var body: some View { 19 | List(motionManager.motionArray.reversed(), id: \.self) { item in 20 | HStack { 21 | Text(verbatim: "#\(item.counter)") 22 | Spacer() 23 | Text( 24 | "R:\(item.attitudeRoll * 180 / .pi, specifier: "%.3f")", 25 | comment: "Attitude Sensor. First Letter as shortcut for Roll") 26 | Spacer() 27 | Text( 28 | "P:\(item.attitudePitch * 180 / .pi, specifier: "%.3f")", 29 | comment: "Attitude Sensor. First Letter as shortcut for Pitch") 30 | Spacer() 31 | Text( 32 | "Y:\(item.attitudeYaw * 180 / .pi, specifier: "%.3f")", 33 | comment: "Attitude Sensor. First Letter as shortcut for Yaw") 34 | Spacer() 35 | Text( 36 | "H:\(item.attitudeHeading, specifier: "%.3f")", 37 | comment: "Attitude Sensor. First Letter as shortcut for Heading") 38 | } 39 | .font(.footnote) 40 | } 41 | .listStyle(.plain) 42 | .navigationTitle(Text("Attitude", comment: "NavigationBar Title - Attitude sensor list view")) 43 | .toolbar { 44 | ToolbarItem(placement: .navigationBarTrailing) { 45 | ShareSheet(url: shareCSV()) 46 | .accessibilityLabel("Export Attitude Data to CSV") 47 | .accessibilityIdentifier(UIIdentifiers.AttitudeList.exportButton) 48 | } 49 | CustomToolbar() 50 | } 51 | } 52 | 53 | // MARK: - Methods 54 | func shareCSV() -> URL { 55 | var csvText = 56 | NSLocalizedString("ID;Time;Roll;Pitch;Yaw;Heading", comment: "Export CSV Headline - attitude") + "\n" // swiftlint:disable:this line_length 57 | 58 | _ = motionManager.motionArray.map { 59 | csvText += 60 | "\($0.counter);\($0.timestamp);\($0.attitudeRoll.localizedDecimal());\($0.attitudePitch.localizedDecimal());\($0.attitudeYaw.localizedDecimal());\($0.attitudeHeading.localizedDecimal())\n" 61 | } 62 | return exportManager.getFile(exportText: csvText, filename: "attitude") 63 | } 64 | } 65 | 66 | // MARK: - Preview 67 | #Preview("AttitudeList - English", traits: .navEmbedded) { 68 | AttitudeList() 69 | } 70 | 71 | #Preview("AttitudeList - German", traits: .navEmbedded) { 72 | AttitudeList() 73 | .previewLocalization(.german) 74 | } 75 | -------------------------------------------------------------------------------- /Sensor-App/Modules/Attitude/AttitudeScreen.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AttitudeScreen.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 23.08.20. 6 | // 7 | 8 | import Sensor_App_Framework 9 | import SwiftUI 10 | 11 | struct AttitudeScreen: View { 12 | 13 | @Environment(MotionManager.self) private var motionManager 14 | 15 | // MARK: - Body 16 | var body: some View { 17 | AttitudeView() 18 | .toolbar { 19 | CustomToolbar() 20 | } 21 | .navigationTitle(Text("Attitude", comment: "NavigationBar Title - Attitude sensor screen")) 22 | .onAppear { 23 | motionManager.startMotionUpdates() 24 | } 25 | } 26 | } 27 | 28 | // MARK: - Preview 29 | #Preview("AttitudeScreen - English", traits: .navEmbedded) { 30 | AttitudeScreen() 31 | } 32 | 33 | #Preview("AttitudeScreen - German", traits: .navEmbedded) { 34 | AttitudeScreen() 35 | .previewLocalization(.german) 36 | } 37 | -------------------------------------------------------------------------------- /Sensor-App/Modules/Gravity/GravityList.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GravityList.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 23.08.20. 6 | // 7 | 8 | import Sensor_App_Framework 9 | import SwiftUI 10 | 11 | struct GravityList: View { 12 | 13 | @Environment(MotionManager.self) private var motionManager 14 | 15 | private let exportManager = ExportManager() 16 | 17 | // MARK: - Body 18 | var body: some View { 19 | List(motionManager.motionArray.reversed(), id: \.self) { item in 20 | HStack { 21 | Text(verbatim: "#\(item.counter)") 22 | Spacer() 23 | Text("X:\(item.gravityXAxis, specifier: "%.5f")", comment: "First Letter as shortcut for X-Axis") 24 | Spacer() 25 | Text("Y:\(item.gravityYAxis, specifier: "%.5f")", comment: "First Letter as shortcut for Y-Axis") 26 | Spacer() 27 | Text("Z:\(item.gravityZAxis, specifier: "%.5f")", comment: "First Letter as shortcut for Z-Axis") 28 | } 29 | .font(.footnote) 30 | } 31 | .listStyle(.plain) 32 | .navigationTitle(Text("Gravity", comment: "NavigationBar Title - Gravity sensor list view")) 33 | .toolbar { 34 | ToolbarItem(placement: .navigationBarTrailing) { 35 | ShareSheet(url: shareCSV()) 36 | .accessibilityLabel("Export Gravity Data to CSV") 37 | .accessibilityIdentifier(UIIdentifiers.GravityList.exportButton) 38 | } 39 | CustomToolbar() 40 | } 41 | } 42 | 43 | // MARK: - Methods 44 | func shareCSV() -> URL { 45 | var csvText = NSLocalizedString("ID;Time;X-Axis;Y-Axis;Z-Axis", comment: "Export CSV Headline - Gravity") + "\n" 46 | 47 | _ = motionManager.motionArray.map { 48 | csvText += 49 | "\($0.counter);\($0.timestamp);\($0.gravityXAxis.localizedDecimal());\($0.gravityYAxis.localizedDecimal());\($0.gravityZAxis.localizedDecimal())\n" 50 | } 51 | return exportManager.getFile(exportText: csvText, filename: "gravity") 52 | } 53 | } 54 | 55 | // MARK: - Preview 56 | #Preview("GravityList - English", traits: .navEmbedded) { 57 | GravityList() 58 | } 59 | 60 | #Preview("GravityList - German", traits: .navEmbedded) { 61 | GravityList() 62 | .previewLocalization(.german) 63 | } 64 | -------------------------------------------------------------------------------- /Sensor-App/Modules/Gravity/GravityScreen.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GravityScreen.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 23.08.20. 6 | // 7 | 8 | import Sensor_App_Framework 9 | import SwiftUI 10 | 11 | struct GravityScreen: View { 12 | 13 | @Environment(MotionManager.self) private var motionManager 14 | 15 | // MARK: - Body 16 | var body: some View { 17 | GravityView() 18 | .toolbar { 19 | CustomToolbar() 20 | } 21 | .navigationTitle(Text("Gravity", comment: "NavigationBar Title - Gravity")) 22 | .onAppear { 23 | motionManager.startMotionUpdates() 24 | } 25 | } 26 | } 27 | 28 | // MARK: - Preview 29 | #Preview("GravityScreen - English", traits: .navEmbedded) { 30 | GravityScreen() 31 | } 32 | 33 | #Preview("GravityScreen - German", traits: .navEmbedded) { 34 | GravityScreen() 35 | .previewLocalization(.german) 36 | } 37 | -------------------------------------------------------------------------------- /Sensor-App/Modules/Gyroscope/GyroscopeList.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GyroscopeList.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 23.08.20. 6 | // 7 | 8 | import Sensor_App_Framework 9 | import SwiftUI 10 | 11 | struct GyroscopeList: View { 12 | 13 | @Environment(MotionManager.self) private var motionManager 14 | 15 | private let exportManager = ExportManager() 16 | 17 | // MARK: - Body 18 | var body: some View { 19 | List(motionManager.motionArray.reversed(), id: \.self) { item in 20 | HStack { 21 | Text(verbatim: "#\(item.counter)") 22 | Spacer() 23 | Text("X:\(item.gyroXAxis, specifier: "%.5f")", comment: "First Letter as shortcut for X-Axis") 24 | Spacer() 25 | Text("Y:\(item.gyroYAxis, specifier: "%.5f")", comment: "First Letter as shortcut for Y-Axis") 26 | Spacer() 27 | Text("Z:\(item.gyroZAxis, specifier: "%.5f")", comment: "First Letter as shortcut for Z-Axis") 28 | } 29 | .font(.footnote) 30 | } 31 | .listStyle(.plain) 32 | .navigationTitle(Text("Gyroscope", comment: "NavigationBar Title - Gyroscope sensor list view")) 33 | .toolbar { 34 | ToolbarItem(placement: .navigationBarTrailing) { 35 | ShareSheet(url: shareCSV()) 36 | .accessibilityLabel("Export Gyroscope Data to CSV") 37 | .accessibilityIdentifier(UIIdentifiers.GyroscopeList.exportButton) 38 | } 39 | CustomToolbar() 40 | } 41 | } 42 | 43 | // MARK: - Methods 44 | func shareCSV() -> URL { 45 | var csvText = 46 | NSLocalizedString("ID;Time;X-Axis;Y-Axis;Z-Axis", comment: "Export CSV Headline - Gyroscope") + "\n" // swiftlint:disable:this line_length 47 | 48 | _ = motionManager.motionArray.map { 49 | csvText += 50 | "\($0.counter);\($0.timestamp);\($0.gyroXAxis.localizedDecimal());\($0.gyroYAxis.localizedDecimal());\($0.gyroZAxis.localizedDecimal())\n" 51 | } 52 | return exportManager.getFile(exportText: csvText, filename: "gyroscope") 53 | } 54 | } 55 | 56 | // MARK: - Preview 57 | #Preview("GyroscopeList - English", traits: .navEmbedded) { 58 | GyroscopeList() 59 | } 60 | 61 | #Preview("GyroscopeList - German", traits: .navEmbedded) { 62 | GyroscopeList() 63 | .previewLocalization(.german) 64 | } 65 | -------------------------------------------------------------------------------- /Sensor-App/Modules/Gyroscope/GyroscopeScreen.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GyroscopeScreen.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 23.08.20. 6 | // 7 | 8 | import Sensor_App_Framework 9 | import SwiftUI 10 | 11 | struct GyroscopeScreen: View { 12 | 13 | @Environment(MotionManager.self) private var motionManager 14 | 15 | // MARK: - Body 16 | var body: some View { 17 | GyroscopeView() 18 | .toolbar { 19 | CustomToolbar() 20 | } 21 | .navigationTitle(Text("Gyroscope", comment: "NavigationBar Title - Gyroscope sensor screen")) 22 | .onAppear { 23 | motionManager.startMotionUpdates() 24 | } 25 | } 26 | } 27 | 28 | // MARK: - Preview 29 | #Preview("GyroscopeScreen - English", traits: .navEmbedded) { 30 | GyroscopeScreen() 31 | } 32 | 33 | #Preview("GyroscopeScreen - German", traits: .navEmbedded) { 34 | GyroscopeScreen() 35 | .previewLocalization(.german) 36 | } 37 | -------------------------------------------------------------------------------- /Sensor-App/Modules/Location/LocationScreen.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LocationScreen.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 24.08.20. 6 | // 7 | 8 | import Sensor_App_Framework 9 | import StoreKit 10 | import SwiftUI 11 | 12 | struct LocationScreen: View { 13 | 14 | @Environment(\.requestReview) private var requestReview 15 | 16 | // MARK: - Body 17 | var body: some View { 18 | LocationView() 19 | .toolbar { 20 | CustomToolbar() 21 | } 22 | .navigationTitle(Text("Location", comment: "NavigationBar Title - Location view screen")) 23 | .onDisappear { 24 | #if RELEASE 25 | requestReview() 26 | #endif 27 | } 28 | } 29 | } 30 | 31 | // MARK: - Preview 32 | #Preview("LocationScreen - English", traits: .navEmbedded) { 33 | LocationScreen() 34 | } 35 | 36 | #Preview("LocationScreen - German", traits: .navEmbedded) { 37 | LocationScreen() 38 | .previewLocalization(.german) 39 | } 40 | -------------------------------------------------------------------------------- /Sensor-App/Modules/Location/MapKitView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MapKitView.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 12.07.22. 6 | // 7 | 8 | import MapKit 9 | import Sensor_App_Framework 10 | import SwiftUI 11 | 12 | struct MapKitView: UIViewRepresentable { 13 | 14 | @Environment(SettingsManager.self) private var settingsManager 15 | 16 | let mapView = MKMapView() 17 | 18 | var fullScreen: Bool 19 | 20 | init(fullScreen: Bool = false) { 21 | self.fullScreen = fullScreen 22 | } 23 | 24 | func makeUIView(context: Context) -> MKMapView { 25 | mapView.delegate = context.coordinator 26 | return mapView 27 | } 28 | 29 | func updateUIView(_ view: MKMapView, context: Context) { 30 | 31 | // Settings 32 | let mapKitSettings = settingsManager.fetchMapKitSettings() 33 | 34 | view.showsUserLocation = true 35 | view.showsCompass = mapKitSettings.showsCompass 36 | view.showsBuildings = mapKitSettings.showsBuildings 37 | view.showsTraffic = mapKitSettings.showsTraffic 38 | view.isRotateEnabled = mapKitSettings.isRotateEnabled 39 | view.isPitchEnabled = mapKitSettings.isPitchEnabled 40 | view.isScrollEnabled = mapKitSettings.isScrollEnabled 41 | view.showsScale = mapKitSettings.showsScale 42 | view.userTrackingMode = .follow 43 | 44 | // Map Appearance 45 | switch mapKitSettings.mapType { 46 | case .standard: view.mapType = MKMapType.standard 47 | case .satellite: view.mapType = MKMapType.satellite 48 | case .hybrid: view.mapType = MKMapType.hybrid 49 | case .satelliteFlyover: view.mapType = MKMapType.satelliteFlyover 50 | case .hybridFlyover: view.mapType = MKMapType.hybridFlyover 51 | case .mutedStandard: view.mapType = MKMapType.mutedStandard 52 | } 53 | 54 | // User Coordinates 55 | let coordinate = CLLocationCoordinate2D( 56 | latitude: view.userLocation.coordinate.latitude, 57 | longitude: view.userLocation.coordinate.longitude 58 | ) 59 | 60 | // Zoom Factor 61 | let region = MKCoordinateRegion( 62 | center: coordinate, latitudinalMeters: mapKitSettings.zoom, 63 | longitudinalMeters: mapKitSettings.zoom 64 | ) 65 | view.setRegion(region, animated: true) 66 | let annotation = MKPointAnnotation() 67 | annotation.coordinate = coordinate 68 | } 69 | 70 | func makeCoordinator() -> Coordinator { 71 | Coordinator(self) 72 | } 73 | } 74 | 75 | extension MapKitView { 76 | class Coordinator: NSObject, MKMapViewDelegate { 77 | var parent: MapKitView 78 | 79 | init(_ parent: MapKitView) { 80 | self.parent = parent 81 | } 82 | func mapViewDidChangeVisibleRegion(_ mapView: MKMapView) { 83 | 84 | } 85 | } 86 | } 87 | 88 | // MARK: - Preview 89 | #Preview(traits: .sizeThatFitsLayout) { 90 | MapKitView() 91 | } 92 | -------------------------------------------------------------------------------- /Sensor-App/Modules/Location/MapView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MapView.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 16.07.22. 6 | // 7 | 8 | import Sensor_App_Framework 9 | import SwiftUI 10 | 11 | struct MapView: View { 12 | 13 | // MARK: - Body 14 | var body: some View { 15 | MapKitView() 16 | .navigationTitle(Text("Map", comment: "NavigationBar Title - Map to show current location")) 17 | .navigationBarTitleDisplayMode(.inline) 18 | } 19 | } 20 | 21 | // MARK: - Preview 22 | #Preview("MapView - English", traits: .navEmbedded) { 23 | MapView() 24 | } 25 | 26 | #Preview("MapView - German", traits: .navEmbedded) { 27 | MapView() 28 | .previewLocalization(.german) 29 | } 30 | -------------------------------------------------------------------------------- /Sensor-App/Modules/Magnetometer/MagnetometerList.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MagnetometerList.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 23.08.20. 6 | // 7 | 8 | import Sensor_App_Framework 9 | import SwiftUI 10 | 11 | struct MagnetometerList: View { 12 | 13 | @Environment(MotionManager.self) private var motionManager 14 | 15 | private let exportManager = ExportManager() 16 | 17 | // MARK: - Body 18 | var body: some View { 19 | List(motionManager.motionArray.reversed(), id: \.self) { item in 20 | HStack { 21 | Text(verbatim: "#\(item.counter)") 22 | Spacer() 23 | Text("X:\(item.magnetometerXAxis, specifier: "%.5f")", comment: "First Letter as shortcut for X-Axis") 24 | Spacer() 25 | Text("Y:\(item.magnetometerYAxis, specifier: "%.5f")", comment: "First Letter as shortcut for Y-Axis") 26 | Spacer() 27 | Text("Z:\(item.magnetometerZAxis, specifier: "%.5f")", comment: "First Letter as shortcut for Z-Axis") 28 | } 29 | .font(.footnote) 30 | } 31 | .navigationTitle(Text("Magnetometer", comment: "NavigationBar Title - Magnetometer sensor list view")) 32 | .toolbar { 33 | ToolbarItem(placement: .navigationBarTrailing) { 34 | ShareSheet(url: shareCSV()) 35 | .accessibilityLabel("Export Magnetometer Data to CSV") 36 | .accessibilityIdentifier(UIIdentifiers.MagnetometerList.exportButton) 37 | } 38 | CustomToolbar() 39 | } 40 | } 41 | 42 | // MARK: - Methods 43 | func shareCSV() -> URL { 44 | var csvText = 45 | NSLocalizedString("ID;Time;X-Axis;Y-Axis;Z-Axis", comment: "Export CSV Headline - Magnetometer") + "\n" // swiftlint:disable:this line_length 46 | 47 | _ = motionManager.motionArray.map { 48 | csvText += 49 | "\($0.counter);\($0.timestamp);\($0.magnetometerXAxis.localizedDecimal());\($0.magnetometerYAxis.localizedDecimal());\($0.magnetometerZAxis.localizedDecimal())\n" 50 | } 51 | return exportManager.getFile(exportText: csvText, filename: "magnetometer") 52 | } 53 | } 54 | 55 | // MARK: - Preview 56 | #Preview("MagnetometerList - English", traits: .navEmbedded) { 57 | MagnetometerList() 58 | } 59 | 60 | #Preview("MagnetometerList - German", traits: .navEmbedded) { 61 | MagnetometerList() 62 | .previewLocalization(.german) 63 | } 64 | -------------------------------------------------------------------------------- /Sensor-App/Modules/Magnetometer/MagnetometerScreen.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MagnetometerScreen.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 23.08.20. 6 | // 7 | 8 | import Sensor_App_Framework 9 | import SwiftUI 10 | 11 | struct MagnetometerScreen: View { 12 | 13 | @Environment(MotionManager.self) private var motionManager 14 | 15 | // MARK: - Body 16 | var body: some View { 17 | MagnetometerView() 18 | .toolbar { 19 | CustomToolbar() 20 | } 21 | .navigationTitle(Text("Magnetometer", comment: "NavigationBar Title - Magnetometer sensor screen")) 22 | .onAppear { 23 | motionManager.startMotionUpdates() 24 | } 25 | } 26 | } 27 | 28 | // MARK: - Preview 29 | #Preview("MagnetometerScreen - English", traits: .navEmbedded) { 30 | MagnetometerScreen() 31 | } 32 | 33 | #Preview("MagnetometerScreen - German", traits: .navEmbedded) { 34 | MagnetometerScreen() 35 | .previewLocalization(.german) 36 | } 37 | -------------------------------------------------------------------------------- /Sensor-App/Modules/Notification/NotificationEnvironmentKey.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ShowErrorAction.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 25.09.2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ShowNotificationAction { 11 | typealias Action = (String) -> Void 12 | let action: Action 13 | func callAsFunction(_ message: String) { 14 | action(message) 15 | } 16 | } 17 | 18 | struct ShowNotificationEnvironmentKey: EnvironmentKey { 19 | nonisolated(unsafe) static let defaultValue = ShowNotificationAction { _ in } 20 | } 21 | 22 | extension EnvironmentValues { 23 | var showNotification: (ShowNotificationAction) { 24 | get { self[ShowNotificationEnvironmentKey.self] } 25 | set { self[ShowNotificationEnvironmentKey.self] = newValue } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Sensor-App/Modules/Notification/NotificationModifier.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NotificationModifier.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 25.09.2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct NotificationModifier: ViewModifier { 11 | 12 | @State private var notificationWrapper: NotificationWrapper? 13 | 14 | func body(content: Content) -> some View { 15 | content 16 | .frame(maxWidth: .infinity, maxHeight: .infinity) 17 | .overlay(alignment: .top) { 18 | if notificationWrapper != nil { 19 | NotificationView(notificationWrapper: $notificationWrapper) 20 | .padding() 21 | 22 | } 23 | }.environment(\.showNotification, ShowNotificationAction(action: showNotification)) 24 | } 25 | 26 | private func showNotification(message: String) { 27 | notificationWrapper = NotificationWrapper(message: message) 28 | } 29 | } 30 | 31 | extension View { 32 | func withNotificationView() -> some View { 33 | modifier(NotificationModifier()) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Sensor-App/Modules/Notification/NotificationView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ErrorView.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 25.09.2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct NotificationView: View { 11 | 12 | @Binding var notificationWrapper: NotificationWrapper? 13 | 14 | // MARK: - Body 15 | var body: some View { 16 | VStack { 17 | Text(notificationWrapper?.message ?? "") 18 | }.task(id: notificationWrapper?.id) { 19 | // delay 20 | try? await Task.sleep(for: .seconds(1)) 21 | guard !Task.isCancelled else { return } 22 | 23 | notificationWrapper = nil 24 | } 25 | .foregroundStyle(.white) 26 | .padding() 27 | .background(.green) 28 | .clipShape(RoundedRectangle(cornerRadius: 16.0, style: .continuous)) 29 | } 30 | } 31 | 32 | // MARK: - Preview 33 | #Preview { 34 | NotificationView( 35 | notificationWrapper: .constant( 36 | NotificationWrapper(message: "Start") 37 | )) 38 | } 39 | 40 | /// ``NotificationWrapper`` Model 41 | struct NotificationWrapper: Identifiable { 42 | 43 | /// ID 44 | let id = UUID() 45 | 46 | /// message 47 | let message: String 48 | } 49 | -------------------------------------------------------------------------------- /Sensor-App/Modules/ReleaseNotes/ReleaseNotesScreen.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReleaseNotesScreen.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 11/24/24. 6 | // 7 | 8 | import Sensor_App_Framework 9 | import SwiftUI 10 | 11 | struct ReleaseNotesScreen: View { 12 | 13 | @Environment(\.dismiss) private var dismiss 14 | 15 | @AppStorage("showReleaseNotes") private var showReleaseNotes = true 16 | 17 | /// Button to close View 18 | var closeButton: some View { 19 | Button(action: { 20 | dismiss() 21 | }) { 22 | Image(systemName: "xmark.circle") 23 | .navigationBarItemModifier(accessibility: "Close") 24 | } 25 | } 26 | 27 | // MARK: - Body 28 | var body: some View { 29 | ReleaseNotesView() 30 | .navigationTitle(Text("Release Notes", comment: "Navigation Bar Title")) 31 | .navigationBarItems(leading: closeButton) 32 | .toolbar { 33 | ToolbarItem(placement: .automatic) { 34 | Toggle( 35 | isOn: $showReleaseNotes, 36 | label: { 37 | Text("Show", comment: "Toggle to enable / disable showing release notes") 38 | } 39 | ) 40 | .toggleStyle(.switch) 41 | } 42 | } 43 | .navigationStackWrapper() 44 | } 45 | } 46 | 47 | // MARK: - Preview 48 | #Preview("ReleaseNotesScreen - English", traits: .navEmbedded) { 49 | Color.clear 50 | .sheet(isPresented: .constant(true)) { 51 | ReleaseNotesScreen() 52 | } 53 | } 54 | 55 | #Preview("ReleaseNotesScreen - German", traits: .navEmbedded) { 56 | Color.clear 57 | .sheet(isPresented: .constant(true)) { 58 | ReleaseNotesScreen() 59 | } 60 | .previewLocalization(.german) 61 | } 62 | -------------------------------------------------------------------------------- /Sensor-App/Modules/ReleaseNotes/ReleaseNotesView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReleaseNotesView.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 11/24/24. 6 | // 7 | 8 | import Sensor_App_Framework 9 | import SwiftUI 10 | 11 | struct ReleaseNotesView: View { 12 | 13 | // MARK: - Body 14 | var body: some View { 15 | List { 16 | Section { 17 | Text("Small bug fixes", comment: "Release notes") 18 | } header: { 19 | Text(verbatim: "5.2.0") 20 | } 21 | 22 | Section { 23 | Text("Added Knots as additional speed setting", comment: "Release notes") 24 | Text("Small bug fixes", comment: "Release notes") 25 | } header: { 26 | Text(verbatim: "5.1.0") 27 | } 28 | 29 | Section { 30 | Text( 31 | "Fixed a bug which caused the app to crash if no permissions to sensors were granted", 32 | comment: "Release notes") 33 | } header: { 34 | Text(verbatim: "5.0.1") 35 | } 36 | 37 | Section { 38 | Text( 39 | "Requires minimum iOS 18 and watchOS 11", 40 | comment: "Release notes") 41 | Text("Performance improvements an bug fixes", comment: "Release notes") 42 | } header: { 43 | Text(verbatim: "5.0.0") 44 | } 45 | } 46 | } 47 | } 48 | 49 | #Preview("ReleaseNotesView - English") { 50 | ReleaseNotesView() 51 | } 52 | 53 | #Preview("ReleaseNotesView - German") { 54 | ReleaseNotesView() 55 | .previewLocalization(.german) 56 | } 57 | -------------------------------------------------------------------------------- /Sensor-App/PreviewTraits/NavEmbedded.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NavEmbedded.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 11/24/24. 6 | // 7 | 8 | import Sensor_App_Framework 9 | import SwiftUI 10 | 11 | struct NavEmbedded: PreviewModifier { 12 | func body(content: Content, context: Void) -> some View { 13 | NavigationStack { 14 | content 15 | #if !os(watchOS) 16 | .environment(AppState()) 17 | #endif 18 | .environment(SettingsManager()) 19 | .environment(CalculationManager()) 20 | .environment(MotionManager()) 21 | .environment(LocationManager()) 22 | } 23 | } 24 | } 25 | 26 | extension PreviewTrait where T == Preview.ViewTraits { 27 | static var navEmbedded: Self = .modifier(NavEmbedded()) 28 | } 29 | -------------------------------------------------------------------------------- /Sensor-App/ViewModifier/NavigationStackWrapper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NavigationStackWrapper.swift 3 | // Sensor-App 4 | // 5 | // Created by Volker Schmitt on 11/24/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | /// Wraps as view into a ``NavigationStack`` 11 | struct NavigationStackWrapper: ViewModifier { 12 | func body(content: Content) -> some View { 13 | NavigationStack { 14 | content 15 | } 16 | } 17 | } 18 | 19 | extension View { 20 | /// Wraps as view into a ``NavigationStack`` 21 | /// 22 | /// - Returns: ``View`` 23 | func navigationStackWrapper() -> some View { 24 | modifier(NavigationStackWrapper()) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Tests/0_Test Plans/iOS_UITest.xctestplan: -------------------------------------------------------------------------------- 1 | { 2 | "configurations" : [ 3 | { 4 | "id" : "10B5B5EB-2913-4F4D-88FE-E5B37C8702CB", 5 | "name" : "Configuration 1 (us)", 6 | "options" : { 7 | 8 | } 9 | }, 10 | { 11 | "id" : "471055D6-971C-40AA-8FD5-69AB4A5BB6D4", 12 | "name" : "Configuration 2 (de)", 13 | "options" : { 14 | "language" : "de", 15 | "locationScenario" : { 16 | "identifier" : "Berlin, Germany", 17 | "referenceType" : "built-in" 18 | }, 19 | "region" : "DE" 20 | } 21 | } 22 | ], 23 | "defaultOptions" : { 24 | "areLocalizationScreenshotsEnabled" : true, 25 | "language" : "en", 26 | "locationScenario" : { 27 | "identifier" : "New York, NY, USA", 28 | "referenceType" : "built-in" 29 | }, 30 | "maximumTestRepetitions" : 5, 31 | "region" : "US", 32 | "repeatInNewRunnerProcess" : true, 33 | "testExecutionOrdering" : "random", 34 | "testRepetitionMode" : "retryOnFailure", 35 | "testTimeoutsEnabled" : true, 36 | "userAttachmentLifetime" : "keepAlways" 37 | }, 38 | "testTargets" : [ 39 | { 40 | "skippedTests" : [ 41 | "ScreenshotUITests", 42 | "ScreenshotUITests\/testScreenshot()" 43 | ], 44 | "target" : { 45 | "containerPath" : "container:Sensor-App.xcodeproj", 46 | "identifier" : "1527B03224C4AA240024DD78", 47 | "name" : "Sensor-AppiOSUITests" 48 | } 49 | } 50 | ], 51 | "version" : 1 52 | } 53 | -------------------------------------------------------------------------------- /Tests/0_Test Plans/iOS_UnitTest.xctestplan: -------------------------------------------------------------------------------- 1 | { 2 | "configurations" : [ 3 | { 4 | "id" : "518B1FC2-6854-4A1D-85C1-CB0AB9CCF8E6", 5 | "name" : "Configuration 1 (us)", 6 | "options" : { 7 | 8 | } 9 | }, 10 | { 11 | "id" : "2734C7C5-F784-458F-899B-92DA56240499", 12 | "name" : "Configuration 1 (de)", 13 | "options" : { 14 | "language" : "de", 15 | "locationScenario" : { 16 | "identifier" : "Berlin, Germany", 17 | "referenceType" : "built-in" 18 | }, 19 | "region" : "DE" 20 | } 21 | } 22 | ], 23 | "defaultOptions" : { 24 | "language" : "en", 25 | "locationScenario" : { 26 | "identifier" : "New York, NY, USA", 27 | "referenceType" : "built-in" 28 | }, 29 | "maximumTestRepetitions" : 5, 30 | "region" : "US", 31 | "repeatInNewRunnerProcess" : true, 32 | "testExecutionOrdering" : "random", 33 | "testRepetitionMode" : "retryOnFailure", 34 | "testTimeoutsEnabled" : true 35 | }, 36 | "testTargets" : [ 37 | { 38 | "target" : { 39 | "containerPath" : "container:Sensor-App.xcodeproj", 40 | "identifier" : "1527B02724C4AA240024DD78", 41 | "name" : "Sensor-AppiOSUnitTests" 42 | } 43 | } 44 | ], 45 | "version" : 2 46 | } 47 | -------------------------------------------------------------------------------- /Tests/0_Test Plans/watchOS_UITest.xctestplan: -------------------------------------------------------------------------------- 1 | { 2 | "configurations" : [ 3 | { 4 | "id" : "5B0D0B13-8B9F-4AAD-8A21-8DD73C6C37C1", 5 | "name" : "Configuration 1 (us)", 6 | "options" : { 7 | 8 | } 9 | }, 10 | { 11 | "id" : "F6861399-9DF2-4226-979A-CA25101A1CEB", 12 | "name" : "Configuration 2 (de)", 13 | "options" : { 14 | "language" : "de", 15 | "locationScenario" : { 16 | "identifier" : "Berlin, Germany", 17 | "referenceType" : "built-in" 18 | }, 19 | "region" : "DE" 20 | } 21 | } 22 | ], 23 | "defaultOptions" : { 24 | "language" : "en", 25 | "locationScenario" : { 26 | "identifier" : "New York, NY, USA", 27 | "referenceType" : "built-in" 28 | }, 29 | "maximumTestRepetitions" : 5, 30 | "region" : "US", 31 | "repeatInNewRunnerProcess" : true, 32 | "testExecutionOrdering" : "random", 33 | "testRepetitionMode" : "retryOnFailure", 34 | "testTimeoutsEnabled" : true, 35 | "userAttachmentLifetime" : "keepAlways" 36 | }, 37 | "testTargets" : [ 38 | { 39 | "skippedTests" : [ 40 | "ScreenshotUITests", 41 | "ScreenshotUITests\/testScreenshot()" 42 | ], 43 | "target" : { 44 | "containerPath" : "container:Sensor-App.xcodeproj", 45 | "identifier" : "15F1BBA82CAFCF1E00C80A1F", 46 | "name" : "Sensor-AppwatchOSUITests" 47 | } 48 | } 49 | ], 50 | "version" : 1 51 | } 52 | -------------------------------------------------------------------------------- /Tests/iOSUITests/AltitudeViewUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AltitudeViewUITests.swift 3 | // Sensor-AppiOSUITests 4 | // 5 | // Created by Volker Schmitt on 26.01.20. 6 | // Copyright © 2020 Volker Schmitt. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class AltitudeViewUITests: BaseTestCase { 12 | func testAltitudeViewToolbarButtons() throws { 13 | // Go to Altitude View 14 | moveToView(UIIdentifiers.Sidebar.altitudeButton) 15 | 16 | // Test Toolbar Buttons 17 | app.buttons[UIIdentifiers.Toolbar.deleteButton].tap() 18 | app.buttons[UIIdentifiers.Toolbar.pauseButton].tap() 19 | app.buttons[UIIdentifiers.Toolbar.deleteButton].tap() 20 | 21 | // Go Back to Main Menu 22 | backToHomeMenu() 23 | } 24 | 25 | func testAltitudeViewGraphs() throws { 26 | // Go to Altitude View 27 | moveToView(UIIdentifiers.Sidebar.altitudeButton) 28 | 29 | // Show all Graphs 30 | app.buttons[UIIdentifiers.AltitudeView.altitudeRow].tap() 31 | app.buttons[UIIdentifiers.AltitudeView.pressureRow].tap() 32 | 33 | // Hide all Graphs 34 | app.buttons[UIIdentifiers.AltitudeView.pressureRow].tap() 35 | app.buttons[UIIdentifiers.AltitudeView.altitudeRow].tap() 36 | 37 | // Go Back to Main Menu 38 | backToHomeMenu() 39 | } 40 | 41 | func testAltitudeViewShareSheet() throws { 42 | // Go to Altitude View 43 | moveToView(UIIdentifiers.Sidebar.altitudeButton) 44 | app.buttons[UIIdentifiers.AltitudeView.logButton].tap() 45 | 46 | // Open / Close Share Sheet 47 | app.navigationBars.buttons[UIIdentifiers.AltitudeList.exportButton].tap() 48 | sleep(1) 49 | dismissShareSheet() 50 | 51 | // Go Back to Main Menu 52 | backToHomeMenu() 53 | backToHomeMenu() 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Tests/iOSUITests/GravityViewUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GravityViewUITests.swift 3 | // Sensor-AppiOSUITests 4 | // 5 | // Created by Volker Schmitt on 26.01.20. 6 | // Copyright © 2020 Volker Schmitt. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class GravityViewUITests: BaseTestCase { 12 | func testGravityViewToolbarButtons() throws { 13 | // Go to Gravity View 14 | moveToView(UIIdentifiers.Sidebar.gravityButton) 15 | 16 | // Test Toolbar Buttons 17 | app.buttons[UIIdentifiers.Toolbar.deleteButton].tap() 18 | app.buttons[UIIdentifiers.Toolbar.pauseButton].tap() 19 | app.buttons[UIIdentifiers.Toolbar.deleteButton].tap() 20 | 21 | // Go Back to Main Menu 22 | backToHomeMenu() 23 | } 24 | 25 | func testGravityViewGraphs() throws { 26 | // Go to Gravity View 27 | moveToView(UIIdentifiers.Sidebar.gravityButton) 28 | 29 | // Show all Graphs 30 | app.buttons[UIIdentifiers.GravityView.zAxisRow].tap() 31 | app.buttons[UIIdentifiers.GravityView.yAxisRow].tap() 32 | app.buttons[UIIdentifiers.GravityView.xAxisRow].tap() 33 | 34 | // Hide all Graphs 35 | app.buttons[UIIdentifiers.GravityView.xAxisRow].tap() 36 | app.buttons[UIIdentifiers.GravityView.yAxisRow].tap() 37 | app.buttons[UIIdentifiers.GravityView.zAxisRow].tap() 38 | 39 | // Go Back to Main Menu 40 | backToHomeMenu() 41 | } 42 | 43 | func testGravityViewSlider() throws { 44 | // Go to Gravity View 45 | moveToView(UIIdentifiers.Sidebar.gravityButton) 46 | 47 | // Swipe Up 48 | app.buttons[UIIdentifiers.GravityView.xAxisRow].swipeUp() 49 | 50 | // Adjust Slider to 0% and then 100% 51 | app.sliders[UIIdentifiers.RefreshRateView.refreshRateSlider].adjust(toNormalizedSliderPosition: 0.0) 52 | app.sliders[UIIdentifiers.RefreshRateView.refreshRateSlider].adjust(toNormalizedSliderPosition: 1.0) 53 | 54 | let slider = UIIdentifiers.RefreshRateView.refreshRateSlider 55 | let updateFrequency = app.sliders[slider].value as! String // swiftlint:disable:this force_cast 56 | let splitUpdateFrequency = updateFrequency.split(separator: " ", maxSplits: 1).map(String.init) 57 | XCTAssertEqual(splitUpdateFrequency[0], "50", "Update frequency should be 50 but is \(splitUpdateFrequency)") 58 | 59 | // Go Back to Main Menu 60 | backToHomeMenu() 61 | } 62 | 63 | func testGravityViewShareSheet() throws { 64 | // Go to Gravity View 65 | moveToView(UIIdentifiers.Sidebar.gravityButton) 66 | app.buttons[UIIdentifiers.GravityView.logButton].tap() 67 | 68 | // Open / Close Share Sheet 69 | app.navigationBars.buttons[UIIdentifiers.GravityList.exportButton].tap() 70 | sleep(1) 71 | dismissShareSheet() 72 | 73 | // Go Back to Main Menu 74 | backToHomeMenu() 75 | backToHomeMenu() 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Tests/iOSUITests/GyroscopeViewUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GyroscopeViewUITests.swift 3 | // Sensor-AppiOSUITests 4 | // 5 | // Created by Volker Schmitt on 26.01.20. 6 | // Copyright © 2020 Volker Schmitt. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class GyroscopeViewUITests: BaseTestCase { 12 | func testGyroscopeViewToolbarButtons() throws { 13 | // Go to Gyroscope View 14 | moveToView(UIIdentifiers.Sidebar.gyroscopeButton) 15 | 16 | // Test Toolbar Buttons 17 | app.buttons[UIIdentifiers.Toolbar.deleteButton].tap() 18 | app.buttons[UIIdentifiers.Toolbar.pauseButton].tap() 19 | app.buttons[UIIdentifiers.Toolbar.deleteButton].tap() 20 | 21 | // Go Back to Main Menu 22 | backToHomeMenu() 23 | } 24 | 25 | func testGyroscopeViewGraphs() throws { 26 | // Go to Gyroscope View 27 | moveToView(UIIdentifiers.Sidebar.gyroscopeButton) 28 | 29 | // Show all Graphs 30 | app.buttons[UIIdentifiers.GyroscopeView.zAxisRow].tap() 31 | app.buttons[UIIdentifiers.GyroscopeView.yAxisRow].tap() 32 | app.buttons[UIIdentifiers.GyroscopeView.xAxisRow].tap() 33 | 34 | // Hide all Graphs 35 | app.buttons[UIIdentifiers.GyroscopeView.xAxisRow].tap() 36 | app.buttons[UIIdentifiers.GyroscopeView.yAxisRow].tap() 37 | app.buttons[UIIdentifiers.GyroscopeView.zAxisRow].tap() 38 | 39 | // Go Back to Main Menu 40 | backToHomeMenu() 41 | } 42 | 43 | func testGyroscopeViewSlider() throws { 44 | // Go to Gyroscope View 45 | moveToView(UIIdentifiers.Sidebar.gyroscopeButton) 46 | 47 | // Swipe Up 48 | app.buttons[UIIdentifiers.GyroscopeView.xAxisRow].swipeUp() 49 | 50 | // Adjust Slider to 0% and then 100% 51 | app.sliders[UIIdentifiers.RefreshRateView.refreshRateSlider].adjust(toNormalizedSliderPosition: 0.0) 52 | app.sliders[UIIdentifiers.RefreshRateView.refreshRateSlider].adjust(toNormalizedSliderPosition: 1.0) 53 | 54 | let slider = UIIdentifiers.RefreshRateView.refreshRateSlider 55 | let updateFrequency = app.sliders[slider].value as! String // swiftlint:disable:this force_cast 56 | let splitUpdateFrequency = updateFrequency.split(separator: " ", maxSplits: 1).map(String.init) 57 | XCTAssertEqual(splitUpdateFrequency[0], "50", "Update frequency should be 50 but is \(splitUpdateFrequency)") 58 | 59 | // Go Back to Main Menu 60 | backToHomeMenu() 61 | } 62 | 63 | func testGyroscopeViewShareSheet() throws { 64 | // Go to Gyroscope View 65 | moveToView(UIIdentifiers.Sidebar.gyroscopeButton) 66 | app.buttons[UIIdentifiers.GyroscopeView.logButton].tap() 67 | 68 | // Open / Close Share Sheet 69 | app.navigationBars.buttons[UIIdentifiers.GyroscopeList.exportButton].tap() 70 | sleep(1) 71 | dismissShareSheet() 72 | 73 | // Go Back to Main Menu 74 | backToHomeMenu() 75 | backToHomeMenu() 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Tests/iOSUITests/Helper/BaseTestCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseTestCase.swift 3 | // Sensor-AppiOSUITests 4 | // 5 | // Created by Volker Schmitt on 21.03.21. 6 | // 7 | 8 | import XCTest 9 | 10 | @testable import Sensor_App 11 | 12 | @MainActor 13 | class BaseTestCase: XCTestCase { 14 | var app: XCUIApplication! 15 | 16 | override func setUp() async throws { 17 | continueAfterFailure = false 18 | 19 | app = XCUIApplication() 20 | app.launchArguments = ["enable-testing", "disable-animations"] 21 | app.launch() 22 | 23 | // Clear User Defaults 24 | // swiftlint:disable:next force_unwrapping 25 | UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!) 26 | UserDefaults.standard.synchronize() 27 | 28 | #if os(iOS) 29 | if isIPad() { 30 | XCUIDevice.shared.orientation = .landscapeLeft 31 | } else if isIPhone() { 32 | XCUIDevice.shared.orientation = .portrait 33 | } 34 | #endif 35 | 36 | } 37 | 38 | override func tearDown() async throws { 39 | } 40 | 41 | // MARK: - Methods 42 | func isIPad() -> Bool { 43 | if UIDevice.current.userInterfaceIdiom == .pad { 44 | return true 45 | } else { 46 | return false 47 | } 48 | } 49 | 50 | func isIPhone() -> Bool { 51 | if UIDevice.current.userInterfaceIdiom == .phone { 52 | return true 53 | } else { 54 | return false 55 | } 56 | } 57 | 58 | func moveToView(_ view: String) { 59 | app.collectionViews[UIIdentifiers.Sidebar.collectionView].buttons[view].tap() 60 | } 61 | 62 | func backToHomeMenu() { 63 | if isIPhone() { 64 | app.navigationBars.buttons.element(boundBy: 0).tap() 65 | } 66 | } 67 | 68 | func dismissShareSheet() { 69 | if isIPhone() { 70 | let activityListView = app.otherElements.element( 71 | matching: .other, 72 | identifier: "ActivityListView") 73 | 74 | XCTAssertTrue(activityListView.waitForExistence(timeout: 2.0)) 75 | 76 | activityListView.buttons.element(boundBy: 0).tap() 77 | } else { 78 | app.otherElements["PopoverDismissRegion"].tap() 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Tests/iOSUITests/Helper/Extension+XCUIElement.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // Sensor-AppiOSUITests 4 | // 5 | // Created by Volker Schmitt on 30.05.25. 6 | // 7 | 8 | import Foundation 9 | import XCTest 10 | 11 | extension XCUIElement { 12 | func tapWhenReady(timeout: TimeInterval = 30) { 13 | XCTAssertTrue(self.waitForExistence(timeout: timeout), "Element did not appear in time") 14 | self.tap() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Tests/iOSUITests/LocationViewUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LocationViewUITests.swift 3 | // Sensor-AppiOSUITests 4 | // 5 | // Created by Volker Schmitt on 26.10.19. 6 | // Copyright © 2019 Volker Schmitt. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | @MainActor 12 | final class LocationViewUITests: BaseTestCase { 13 | 14 | func testLocationViewToolbarButtons() throws { 15 | // Go to Location View 16 | moveToView(UIIdentifiers.Sidebar.locationButton) 17 | 18 | // Test Toolbar Buttons 19 | app.buttons[UIIdentifiers.Toolbar.deleteButton].tap() 20 | app.buttons[UIIdentifiers.Toolbar.pauseButton].tap() 21 | app.buttons[UIIdentifiers.Toolbar.deleteButton].tap() 22 | 23 | // Go Back to Main Menu 24 | backToHomeMenu() 25 | } 26 | 27 | func testLocationViewGraphs() throws { 28 | // Go to Location View 29 | moveToView(UIIdentifiers.Sidebar.locationButton) 30 | 31 | // Show all Graphs 32 | app.buttons[UIIdentifiers.LocationView.speedRow].tap() 33 | app.buttons[UIIdentifiers.LocationView.courseRow].tap() 34 | app.buttons[UIIdentifiers.LocationView.altitudeRow].tap() 35 | app.buttons[UIIdentifiers.LocationView.longitudeRow].tap() 36 | app.buttons[UIIdentifiers.LocationView.latitudeRow].tap() 37 | 38 | // Hide all Graphs 39 | app.buttons[UIIdentifiers.LocationView.latitudeRow].tap() 40 | app.buttons[UIIdentifiers.LocationView.longitudeRow].tap() 41 | app.buttons[UIIdentifiers.LocationView.altitudeRow].tap() 42 | app.buttons[UIIdentifiers.LocationView.courseRow].tap() 43 | app.buttons[UIIdentifiers.LocationView.speedRow].tap() 44 | 45 | // Go Back to Main Menu 46 | backToHomeMenu() 47 | } 48 | 49 | func testLocationViewShareSheet() throws { 50 | // Go to Location View 51 | moveToView(UIIdentifiers.Sidebar.locationButton) 52 | 53 | // Open / Close Share Sheet 54 | app.collectionViews.buttons[UIIdentifiers.LocationView.exportButton].tap() 55 | sleep(1) 56 | dismissShareSheet() 57 | 58 | // Go Back to Main Menu 59 | backToHomeMenu() 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Tests/iOSUITests/SettingsViewUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsViewUITests.swift 3 | // Sensor-AppiOSUITests 4 | // 5 | // Created by Volker Schmitt on 26.01.20. 6 | // Copyright © 2020 Volker Schmitt. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class SettingsViewUITests: BaseTestCase { 12 | func testSettingsSelection() throws { 13 | // Go to Settings View 14 | moveToView(UIIdentifiers.Sidebar.settingsButton) 15 | 16 | // Select Speed Setting 17 | let collection = app.collectionViews[UIIdentifiers.SettingScreen.collectionView] 18 | collection.buttons[UIIdentifiers.SettingScreen.speedPicker].tap() 19 | app.buttons["km/h"].tap() 20 | 21 | // Select GPS Accuracy Setting 22 | collection.buttons[UIIdentifiers.SettingScreen.accuracyPicker].tap() 23 | app.buttons["3 Kilometer"].tap() 24 | 25 | collection.buttons[UIIdentifiers.SettingScreen.accuracyPicker].swipeUp() 26 | collection.buttons[UIIdentifiers.SettingScreen.pressurePicker].swipeUp() 27 | 28 | collection.steppers[UIIdentifiers.SettingScreen.zoomStepper].buttons.element(boundBy: 0).tap() 29 | collection.steppers[UIIdentifiers.SettingScreen.zoomStepper].buttons.element(boundBy: 1).tap() 30 | 31 | // Select Pressure Setting 32 | collection.buttons[UIIdentifiers.SettingScreen.pressurePicker].tap() 33 | app.buttons["bar"].tap() 34 | 35 | // Select Height Setting 36 | collection.buttons[UIIdentifiers.SettingScreen.altitudePicker].tap() 37 | app.buttons["ft"].tap() 38 | 39 | // Graph Settings 40 | collection.steppers[UIIdentifiers.SettingScreen.maxPointsStepper].buttons.element(boundBy: 0).tap() 41 | collection.steppers[UIIdentifiers.SettingScreen.maxPointsStepper].buttons.element(boundBy: 1).tap() 42 | 43 | // Save Settings 44 | collection.buttons[UIIdentifiers.SettingScreen.saveButton].tap() 45 | collection.buttons[UIIdentifiers.SettingScreen.discardButton].tap() 46 | 47 | // Go Back to Main Menu 48 | backToHomeMenu() 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Tests/iOSUnitTests/API/AppUpdatesTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppUpdatesTests.swift 3 | // Sensor-AppTests 4 | // 5 | // Created by Volker Schmitt on 01.02.20. 6 | // Copyright © 2020 Volker Schmitt. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Sensor_App_Framework 11 | import Testing 12 | 13 | @testable import Sensor_App 14 | 15 | @MainActor 16 | final class AppUpdateTests: BaseTestCase { 17 | 18 | // MARK: - Testing Methods 19 | @Test("Test app for first time launch") 20 | func appUpdates() async throws { 21 | let appUpdates = AppUpdates() 22 | UserDefaults.standard.removeObject(forKey: "CurrentAppVersion") 23 | appUpdates.checkForUpdate() 24 | 25 | let userDefaultsForSpeedSetting = UserDefaults.standard.string( 26 | forKey: "\(SettingsForUserDefaults.GPSSpeedSetting)") 27 | 28 | #expect(userDefaultsForSpeedSetting == nil) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Tests/iOSUnitTests/API/CalculationManagerTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CalculationManagerTests.swift 3 | // Sensor-AppTests 4 | // 5 | // Created by Volker Schmitt on 26.10.19. 6 | // Copyright © 2019 Volker Schmitt. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Sensor_App_Framework 11 | import Testing 12 | 13 | @testable import Sensor_App 14 | 15 | @MainActor 16 | final class CalculationManagerTests: BaseTestCase { 17 | 18 | // MARK: - Testing Methods 19 | @Test( 20 | "Test conversion of speed", 21 | arguments: [ 22 | (unit: UnitSpeed.metersPerSecond.symbol, result: 10.0), 23 | (unit: UnitSpeed.kilometersPerHour.symbol, result: 36.0), 24 | (unit: UnitSpeed.milesPerHour.symbol, result: 22.37), 25 | (unit: UnitSpeed.knots.symbol, result: 19.44) 26 | ]) 27 | func testSpeedCalculation(unit: String, result: Double) async throws { 28 | let input = 10.0 // m/s 29 | let calculation = calculationManager.calculateSpeed(ms: input, to: unit).rounded(toPlaces: 2) 30 | 31 | #expect(calculation == result, "\(input) m/s should equal \(result) \(unit)") 32 | } 33 | 34 | @Test( 35 | "Test conversion of pressure", 36 | arguments: [ 37 | (unit: UnitPressure.millibars.symbol, result: 1000.0), 38 | (unit: UnitPressure.bars.symbol, result: 1.0), 39 | (unit: UnitPressure.newtonsPerMetersSquared.symbol, result: 100000.0), 40 | (unit: UnitPressure.hectopascals.symbol, result: 1000.0), 41 | (unit: UnitPressure.kilopascals.symbol, result: 100.0), 42 | (unit: UnitPressure.poundsForcePerSquareInch.symbol, result: 14.50), 43 | (unit: UnitPressure.millimetersOfMercury.symbol, result: 750.06), 44 | (unit: UnitPressure.inchesOfMercury.symbol, result: 29.53) 45 | ]) 46 | func testPressureCalculation(unit: String, result: Double) async throws { 47 | let input = 100.0 // kPa 48 | let calculation = calculationManager.calculatePressure(pressure: input, to: unit).rounded(toPlaces: 2) 49 | 50 | #expect(calculation == result, "\(input) kPa should equal \(result) \(unit)") 51 | } 52 | 53 | @Test( 54 | "Test conversion of height", 55 | arguments: [ 56 | (unit: UnitLength.millimeters.symbol, result: 1000.0), 57 | (unit: UnitLength.centimeters.symbol, result: 100.0), 58 | (unit: UnitLength.meters.symbol, result: 1.0), 59 | (unit: UnitLength.inches.symbol, result: 39.37), 60 | (unit: UnitLength.feet.symbol, result: 3.28), 61 | (unit: UnitLength.yards.symbol, result: 1.09) 62 | ]) 63 | func testHeightCalculation(unit: String, result: Double) async throws { 64 | let input = 1.0 // m 65 | let calculation = calculationManager.calculateHeight(height: input, to: unit).rounded(toPlaces: 2) 66 | 67 | #expect(calculation == result, "\(input) kPa should equal \(result) \(unit)") 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Tests/iOSUnitTests/API/SettingsManagerTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsManagerTests.swift 3 | // Sensor-AppTests 4 | // 5 | // Created by Volker Schmitt on 27.01.20. 6 | // Copyright © 2020 Volker Schmitt. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Sensor_App_Framework 11 | import Testing 12 | 13 | @testable import Sensor_App 14 | 15 | @MainActor 16 | final class SettingsManagerTests: BaseTestCase { 17 | 18 | // MARK: - Testing Methods 19 | @Test("Save and read frequency") 20 | func saveAndReadFrequency() throws { 21 | var settings = settingsManager.fetchUserSettings() 22 | settings.frequencySetting = 50 23 | settingsManager.saveUserSettings(userSettings: settings) 24 | 25 | let frequency = settingsManager.fetchUserSettings().frequencySetting 26 | 27 | #expect(frequency == 50) 28 | } 29 | 30 | @Test("Save and read speed setting") 31 | func speedSetting() throws { 32 | var settings = settingsManager.fetchUserSettings() 33 | settings.GPSSpeedSetting = "mph" 34 | settingsManager.saveUserSettings(userSettings: settings) 35 | 36 | let speed = settingsManager.fetchUserSettings().GPSSpeedSetting 37 | 38 | #expect(speed == "mph") 39 | } 40 | 41 | @Test("Save and read GPS accuracy setting") 42 | func GPSAccuracySetting() throws { 43 | var settings = settingsManager.fetchUserSettings() 44 | settings.GPSAccuracySetting = "10 Meter" 45 | settingsManager.saveUserSettings(userSettings: settings) 46 | 47 | let accuracy = settingsManager.fetchUserSettings().GPSAccuracySetting 48 | 49 | #expect(accuracy == "10 Meter") 50 | } 51 | 52 | @Test("Save and read pressure setting") 53 | func pressureSetting() throws { 54 | var settings = settingsManager.fetchUserSettings() 55 | settings.pressureSetting = "bar" 56 | settingsManager.saveUserSettings(userSettings: settings) 57 | 58 | let pressure = settingsManager.fetchUserSettings().pressureSetting 59 | 60 | #expect(pressure == "bar") 61 | } 62 | 63 | @Test("Save and read height setting") 64 | func heightSetting() throws { 65 | var settings = settingsManager.fetchUserSettings() 66 | settings.altitudeHeightSetting = "cm" 67 | settingsManager.saveUserSettings(userSettings: settings) 68 | 69 | let height = settingsManager.fetchUserSettings().altitudeHeightSetting 70 | 71 | #expect(height == "cm") 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Tests/iOSUnitTests/Customization/ExtensionTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExtensionTests.swift 3 | // Sensor-AppTests 4 | // 5 | // Created by Volker Schmitt on 19.01.20. 6 | // Copyright © 2020 Volker Schmitt. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Testing 11 | 12 | @testable import Sensor_App 13 | 14 | @MainActor 15 | final class ExtensionTests: BaseTestCase { 16 | 17 | // MARK: - Testing Methods 18 | @Test( 19 | "Test double extension to round", 20 | arguments: [ 21 | (input: 100.005, result: 100.01), 22 | // (input: 100.0045, result: 100.01), 23 | (input: 100.0040, result: 100.00), 24 | (input: 100.0044, result: 100.00) 25 | ]) 26 | func doubleExtensionRound(input: Double, result: Double) throws { 27 | let calculation = input.rounded(toPlaces: 2) 28 | 29 | #expect(calculation == result) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Tests/iOSUnitTests/Helper/BaseTestCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseTestCase.swift 3 | // Sensor-AppiOSUnitTests 4 | // 5 | // Created by Volker Schmitt on 30.05.25. 6 | // 7 | 8 | import Foundation 9 | import Sensor_App_Framework 10 | import Testing 11 | 12 | @testable import Sensor_App 13 | 14 | @MainActor 15 | class BaseTestCase { 16 | 17 | let calculationManager = CalculationManager() 18 | let settingsManager = SettingsManager() 19 | 20 | init() async throws {} 21 | 22 | deinit {} 23 | } 24 | -------------------------------------------------------------------------------- /Tests/watchOSUITests/Helper/BaseTestCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseTestCase.swift 3 | // Sensor-AppwatchOSUITests 4 | // 5 | // Created by Volker Schmitt on 04.10.2024. 6 | // 7 | 8 | import XCTest 9 | 10 | @MainActor 11 | class BaseTestCase: XCTestCase { 12 | var app: XCUIApplication! 13 | 14 | override func setUp() async throws { 15 | continueAfterFailure = false 16 | 17 | app = XCUIApplication() 18 | app.launchArguments = ["enable-testing"] 19 | app.launch() 20 | 21 | // Clear User Defaults 22 | // swiftlint:disable:next force_unwrapping 23 | UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!) 24 | UserDefaults.standard.synchronize() 25 | } 26 | 27 | override func tearDown() async throws { 28 | } 29 | 30 | func backToHomeMenu() { 31 | app.navigationBars.buttons.element(boundBy: 0).tap() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Tests/watchOSUITests/ScreenshotUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScreenshotUITests.swift 3 | // Sensor-AppwatchOSUITests 4 | // 5 | // Created by Volker Schmitt on 04.10.2024. 6 | // 7 | 8 | import XCTest 9 | 10 | @MainActor 11 | class ScreenshotUITests: BaseTestCase { 12 | 13 | func testScreenshot() throws { 14 | 15 | // Wait for Location Authorization and allow access 16 | addUIInterruptionMonitor(withDescription: "Location Dialog") { (alert) -> Bool in 17 | let button = alert.buttons.element(boundBy: 0) 18 | if button.exists { 19 | button.tap() 20 | return true 21 | } 22 | return false 23 | } 24 | 25 | let collection = app.collectionViews[UIIdentifiers.ContentView.collectionView] 26 | collection.buttons[UIIdentifiers.ContentView.locationButton].swipeDown() 27 | 28 | // Take Screenshot of Home View 29 | takeScreenshotOfCurrentView(name: "0Home") 30 | 31 | // Switch to Location View 32 | collection.buttons[UIIdentifiers.ContentView.locationButton].tap() 33 | 34 | // Take Screenshot of Location and go back to Home 35 | takeScreenshotOfCurrentView(name: "1Location") 36 | backToHomeMenu() 37 | 38 | // Go to Acceleration View and take Screenshot 39 | collection.buttons[UIIdentifiers.ContentView.accelerationButton].tap() 40 | takeScreenshotOfCurrentView(name: "2Acceleration") 41 | 42 | backToHomeMenu() 43 | 44 | // Swipe up to Settings 45 | collection.swipeUp() 46 | 47 | // Go to Settings View and take Screenshot 48 | collection.buttons[UIIdentifiers.ContentView.settingsButton].tap() 49 | 50 | takeScreenshotOfCurrentView(name: "4Settings") 51 | } 52 | 53 | func takeScreenshotOfCurrentView(name: String, delay: UInt32 = 1) { 54 | sleep(delay) 55 | 56 | let fullScreenshot = XCUIScreen.main.screenshot() 57 | 58 | let screenshot = XCTAttachment( 59 | uniformTypeIdentifier: "public.png", 60 | name: "\(String(getLanguageISO()))_\(name)-AppleWatch.png", 61 | payload: fullScreenshot.pngRepresentation, 62 | userInfo: nil 63 | ) 64 | screenshot.lifetime = .keepAlways 65 | add(screenshot) 66 | } 67 | 68 | func getLanguageISO() -> String { 69 | let locale = Locale.current.identifier 70 | return locale 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /fastlane/metadata/copyright.txt: -------------------------------------------------------------------------------- 1 | Copyright © 2024 Volker Schmitt -------------------------------------------------------------------------------- /fastlane/metadata/de-DE/description.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/fastlane/metadata/de-DE/description.txt -------------------------------------------------------------------------------- /fastlane/metadata/de-DE/keywords.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/fastlane/metadata/de-DE/keywords.txt -------------------------------------------------------------------------------- /fastlane/metadata/de-DE/marketing_url.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/fastlane/metadata/de-DE/marketing_url.txt -------------------------------------------------------------------------------- /fastlane/metadata/de-DE/name.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/fastlane/metadata/de-DE/name.txt -------------------------------------------------------------------------------- /fastlane/metadata/de-DE/privacy_url.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/fastlane/metadata/de-DE/privacy_url.txt -------------------------------------------------------------------------------- /fastlane/metadata/de-DE/promotional_text.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/fastlane/metadata/de-DE/promotional_text.txt -------------------------------------------------------------------------------- /fastlane/metadata/de-DE/release_notes.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/fastlane/metadata/de-DE/release_notes.txt -------------------------------------------------------------------------------- /fastlane/metadata/de-DE/subtitle.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/fastlane/metadata/de-DE/subtitle.txt -------------------------------------------------------------------------------- /fastlane/metadata/de-DE/support_url.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/fastlane/metadata/de-DE/support_url.txt -------------------------------------------------------------------------------- /fastlane/metadata/en-US/description.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/fastlane/metadata/en-US/description.txt -------------------------------------------------------------------------------- /fastlane/metadata/en-US/keywords.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/fastlane/metadata/en-US/keywords.txt -------------------------------------------------------------------------------- /fastlane/metadata/en-US/marketing_url.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/fastlane/metadata/en-US/marketing_url.txt -------------------------------------------------------------------------------- /fastlane/metadata/en-US/name.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/fastlane/metadata/en-US/name.txt -------------------------------------------------------------------------------- /fastlane/metadata/en-US/privacy_url.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/fastlane/metadata/en-US/privacy_url.txt -------------------------------------------------------------------------------- /fastlane/metadata/en-US/promotional_text.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/fastlane/metadata/en-US/promotional_text.txt -------------------------------------------------------------------------------- /fastlane/metadata/en-US/release_notes.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/fastlane/metadata/en-US/release_notes.txt -------------------------------------------------------------------------------- /fastlane/metadata/en-US/subtitle.txt: -------------------------------------------------------------------------------- 1 | Manage 3D Printer and Filament -------------------------------------------------------------------------------- /fastlane/metadata/en-US/support_url.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/fastlane/metadata/en-US/support_url.txt -------------------------------------------------------------------------------- /fastlane/metadata/review_information/demo_password.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/fastlane/metadata/review_information/demo_password.txt -------------------------------------------------------------------------------- /fastlane/metadata/review_information/demo_user.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/fastlane/metadata/review_information/demo_user.txt -------------------------------------------------------------------------------- /fastlane/metadata/review_information/email.txt: -------------------------------------------------------------------------------- 1 | apple@volkerschmitt.eu -------------------------------------------------------------------------------- /fastlane/metadata/review_information/first_name.txt: -------------------------------------------------------------------------------- 1 | Volker -------------------------------------------------------------------------------- /fastlane/metadata/review_information/last_name.txt: -------------------------------------------------------------------------------- 1 | Schmitt -------------------------------------------------------------------------------- /fastlane/metadata/review_information/notes.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/fastlane/metadata/review_information/notes.txt -------------------------------------------------------------------------------- /fastlane/metadata/review_information/phone_number.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/fastlane/metadata/review_information/phone_number.txt -------------------------------------------------------------------------------- /screenshots.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #xcrun simctl list devices 4 | 5 | # Define Simulator UUIDs 6 | iPhone="2A48624C-7026-42E8-8C4E-18E120D93492" 7 | iPad="3775D7AE-4C08-4B16-B320-0115FC021B24" 8 | 9 | # Define the OS version in a variable 10 | OS_VERSION="18.1" 11 | 12 | # Define Appearance light / dark 13 | APPEARANCE="light" 14 | 15 | xcrun simctl boot $iPhone 16 | xcrun simctl boot $iPad 17 | 18 | xcrun simctl status_bar $iPhone override --time "9:41" 19 | xcrun simctl status_bar $iPad override --time "9:41" 20 | 21 | xcrun simctl ui $iPhone appearance $APPEARANCE 22 | xcrun simctl ui $iPad appearance $APPEARANCE 23 | 24 | #xcodebuild clean -project 'Print Commander.xcodeproj' 25 | 26 | 27 | # Run xcodebuild with the OS version variable 28 | xcodebuild test -testPlan iOS_ScreenshotTest -project 'Sensor-App.xcodeproj' -scheme 'Sensor-App' \ 29 | -destination "platform=iOS Simulator,name=iPhone 16 Pro Max,OS=$OS_VERSION" \ 30 | -destination "platform=iOS Simulator,name=iPad Pro 13-inch (M4),OS=$OS_VERSION" \ 31 | -parallel-testing-enabled YES \ 32 | -derivedDataPath '/tmp/PrintCommanderDerivedData/' 33 | 34 | cd /tmp/PrintCommanderDerivedData 35 | open . 36 | 37 | 38 | # xcparse screenshots --os --model --test-plan-config test.xcresult ~/Desktop --legacy 39 | -------------------------------------------------------------------------------- /screenshots/cs_CZ/cs_CZ_0Home-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/cs_CZ/cs_CZ_0Home-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/cs_CZ/cs_CZ_0Home-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/cs_CZ/cs_CZ_0Home-iPad.png -------------------------------------------------------------------------------- /screenshots/cs_CZ/cs_CZ_0Home-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/cs_CZ/cs_CZ_0Home-iPhone.png -------------------------------------------------------------------------------- /screenshots/cs_CZ/cs_CZ_1Location-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/cs_CZ/cs_CZ_1Location-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/cs_CZ/cs_CZ_1Location-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/cs_CZ/cs_CZ_1Location-iPad.png -------------------------------------------------------------------------------- /screenshots/cs_CZ/cs_CZ_1Location-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/cs_CZ/cs_CZ_1Location-iPhone.png -------------------------------------------------------------------------------- /screenshots/cs_CZ/cs_CZ_2Acceleration-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/cs_CZ/cs_CZ_2Acceleration-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/cs_CZ/cs_CZ_2Acceleration-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/cs_CZ/cs_CZ_2Acceleration-iPad.png -------------------------------------------------------------------------------- /screenshots/cs_CZ/cs_CZ_2Acceleration-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/cs_CZ/cs_CZ_2Acceleration-iPhone.png -------------------------------------------------------------------------------- /screenshots/cs_CZ/cs_CZ_3Acceleration_Log-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/cs_CZ/cs_CZ_3Acceleration_Log-iPad.png -------------------------------------------------------------------------------- /screenshots/cs_CZ/cs_CZ_3Acceleration_Log-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/cs_CZ/cs_CZ_3Acceleration_Log-iPhone.png -------------------------------------------------------------------------------- /screenshots/cs_CZ/cs_CZ_4Settings-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/cs_CZ/cs_CZ_4Settings-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/cs_CZ/cs_CZ_4Settings-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/cs_CZ/cs_CZ_4Settings-iPad.png -------------------------------------------------------------------------------- /screenshots/cs_CZ/cs_CZ_4Settings-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/cs_CZ/cs_CZ_4Settings-iPhone.png -------------------------------------------------------------------------------- /screenshots/de_DE/de_DE_0Home-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/de_DE/de_DE_0Home-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/de_DE/de_DE_0Home-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/de_DE/de_DE_0Home-iPad.png -------------------------------------------------------------------------------- /screenshots/de_DE/de_DE_0Home-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/de_DE/de_DE_0Home-iPhone.png -------------------------------------------------------------------------------- /screenshots/de_DE/de_DE_1Location-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/de_DE/de_DE_1Location-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/de_DE/de_DE_1Location-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/de_DE/de_DE_1Location-iPad.png -------------------------------------------------------------------------------- /screenshots/de_DE/de_DE_1Location-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/de_DE/de_DE_1Location-iPhone.png -------------------------------------------------------------------------------- /screenshots/de_DE/de_DE_2Acceleration-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/de_DE/de_DE_2Acceleration-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/de_DE/de_DE_2Acceleration-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/de_DE/de_DE_2Acceleration-iPad.png -------------------------------------------------------------------------------- /screenshots/de_DE/de_DE_2Acceleration-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/de_DE/de_DE_2Acceleration-iPhone.png -------------------------------------------------------------------------------- /screenshots/de_DE/de_DE_3Acceleration_Log-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/de_DE/de_DE_3Acceleration_Log-iPad.png -------------------------------------------------------------------------------- /screenshots/de_DE/de_DE_3Acceleration_Log-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/de_DE/de_DE_3Acceleration_Log-iPhone.png -------------------------------------------------------------------------------- /screenshots/de_DE/de_DE_4Settings-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/de_DE/de_DE_4Settings-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/de_DE/de_DE_4Settings-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/de_DE/de_DE_4Settings-iPad.png -------------------------------------------------------------------------------- /screenshots/de_DE/de_DE_4Settings-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/de_DE/de_DE_4Settings-iPhone.png -------------------------------------------------------------------------------- /screenshots/en_US/en_US_0Home-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/en_US/en_US_0Home-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/en_US/en_US_0Home-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/en_US/en_US_0Home-iPad.png -------------------------------------------------------------------------------- /screenshots/en_US/en_US_0Home-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/en_US/en_US_0Home-iPhone.png -------------------------------------------------------------------------------- /screenshots/en_US/en_US_1Location-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/en_US/en_US_1Location-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/en_US/en_US_1Location-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/en_US/en_US_1Location-iPad.png -------------------------------------------------------------------------------- /screenshots/en_US/en_US_1Location-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/en_US/en_US_1Location-iPhone.png -------------------------------------------------------------------------------- /screenshots/en_US/en_US_2Acceleration-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/en_US/en_US_2Acceleration-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/en_US/en_US_2Acceleration-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/en_US/en_US_2Acceleration-iPad.png -------------------------------------------------------------------------------- /screenshots/en_US/en_US_2Acceleration-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/en_US/en_US_2Acceleration-iPhone.png -------------------------------------------------------------------------------- /screenshots/en_US/en_US_3Acceleration_Log-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/en_US/en_US_3Acceleration_Log-iPad.png -------------------------------------------------------------------------------- /screenshots/en_US/en_US_3Acceleration_Log-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/en_US/en_US_3Acceleration_Log-iPhone.png -------------------------------------------------------------------------------- /screenshots/en_US/en_US_4Settings-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/en_US/en_US_4Settings-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/en_US/en_US_4Settings-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/en_US/en_US_4Settings-iPad.png -------------------------------------------------------------------------------- /screenshots/en_US/en_US_4Settings-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/en_US/en_US_4Settings-iPhone.png -------------------------------------------------------------------------------- /screenshots/es_ES/es_ES_0Home-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/es_ES/es_ES_0Home-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/es_ES/es_ES_0Home-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/es_ES/es_ES_0Home-iPad.png -------------------------------------------------------------------------------- /screenshots/es_ES/es_ES_0Home-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/es_ES/es_ES_0Home-iPhone.png -------------------------------------------------------------------------------- /screenshots/es_ES/es_ES_1Location-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/es_ES/es_ES_1Location-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/es_ES/es_ES_1Location-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/es_ES/es_ES_1Location-iPad.png -------------------------------------------------------------------------------- /screenshots/es_ES/es_ES_1Location-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/es_ES/es_ES_1Location-iPhone.png -------------------------------------------------------------------------------- /screenshots/es_ES/es_ES_2Acceleration-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/es_ES/es_ES_2Acceleration-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/es_ES/es_ES_2Acceleration-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/es_ES/es_ES_2Acceleration-iPad.png -------------------------------------------------------------------------------- /screenshots/es_ES/es_ES_2Acceleration-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/es_ES/es_ES_2Acceleration-iPhone.png -------------------------------------------------------------------------------- /screenshots/es_ES/es_ES_3Acceleration_Log-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/es_ES/es_ES_3Acceleration_Log-iPad.png -------------------------------------------------------------------------------- /screenshots/es_ES/es_ES_3Acceleration_Log-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/es_ES/es_ES_3Acceleration_Log-iPhone.png -------------------------------------------------------------------------------- /screenshots/es_ES/es_ES_4Settings-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/es_ES/es_ES_4Settings-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/es_ES/es_ES_4Settings-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/es_ES/es_ES_4Settings-iPad.png -------------------------------------------------------------------------------- /screenshots/es_ES/es_ES_4Settings-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/es_ES/es_ES_4Settings-iPhone.png -------------------------------------------------------------------------------- /screenshots/fr_FR/fr_FR_0Home-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/fr_FR/fr_FR_0Home-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/fr_FR/fr_FR_0Home-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/fr_FR/fr_FR_0Home-iPad.png -------------------------------------------------------------------------------- /screenshots/fr_FR/fr_FR_0Home-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/fr_FR/fr_FR_0Home-iPhone.png -------------------------------------------------------------------------------- /screenshots/fr_FR/fr_FR_1Location-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/fr_FR/fr_FR_1Location-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/fr_FR/fr_FR_1Location-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/fr_FR/fr_FR_1Location-iPad.png -------------------------------------------------------------------------------- /screenshots/fr_FR/fr_FR_1Location-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/fr_FR/fr_FR_1Location-iPhone.png -------------------------------------------------------------------------------- /screenshots/fr_FR/fr_FR_2Acceleration-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/fr_FR/fr_FR_2Acceleration-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/fr_FR/fr_FR_2Acceleration-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/fr_FR/fr_FR_2Acceleration-iPad.png -------------------------------------------------------------------------------- /screenshots/fr_FR/fr_FR_2Acceleration-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/fr_FR/fr_FR_2Acceleration-iPhone.png -------------------------------------------------------------------------------- /screenshots/fr_FR/fr_FR_3Acceleration_Log-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/fr_FR/fr_FR_3Acceleration_Log-iPad.png -------------------------------------------------------------------------------- /screenshots/fr_FR/fr_FR_3Acceleration_Log-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/fr_FR/fr_FR_3Acceleration_Log-iPhone.png -------------------------------------------------------------------------------- /screenshots/fr_FR/fr_FR_4Settings-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/fr_FR/fr_FR_4Settings-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/fr_FR/fr_FR_4Settings-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/fr_FR/fr_FR_4Settings-iPad.png -------------------------------------------------------------------------------- /screenshots/fr_FR/fr_FR_4Settings-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/fr_FR/fr_FR_4Settings-iPhone.png -------------------------------------------------------------------------------- /screenshots/it_IT/it_IT_0Home-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/it_IT/it_IT_0Home-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/it_IT/it_IT_0Home-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/it_IT/it_IT_0Home-iPad.png -------------------------------------------------------------------------------- /screenshots/it_IT/it_IT_0Home-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/it_IT/it_IT_0Home-iPhone.png -------------------------------------------------------------------------------- /screenshots/it_IT/it_IT_1Location-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/it_IT/it_IT_1Location-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/it_IT/it_IT_1Location-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/it_IT/it_IT_1Location-iPad.png -------------------------------------------------------------------------------- /screenshots/it_IT/it_IT_1Location-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/it_IT/it_IT_1Location-iPhone.png -------------------------------------------------------------------------------- /screenshots/it_IT/it_IT_2Acceleration-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/it_IT/it_IT_2Acceleration-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/it_IT/it_IT_2Acceleration-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/it_IT/it_IT_2Acceleration-iPad.png -------------------------------------------------------------------------------- /screenshots/it_IT/it_IT_2Acceleration-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/it_IT/it_IT_2Acceleration-iPhone.png -------------------------------------------------------------------------------- /screenshots/it_IT/it_IT_3Acceleration_Log-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/it_IT/it_IT_3Acceleration_Log-iPad.png -------------------------------------------------------------------------------- /screenshots/it_IT/it_IT_3Acceleration_Log-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/it_IT/it_IT_3Acceleration_Log-iPhone.png -------------------------------------------------------------------------------- /screenshots/it_IT/it_IT_4Settings-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/it_IT/it_IT_4Settings-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/it_IT/it_IT_4Settings-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/it_IT/it_IT_4Settings-iPad.png -------------------------------------------------------------------------------- /screenshots/it_IT/it_IT_4Settings-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/it_IT/it_IT_4Settings-iPhone.png -------------------------------------------------------------------------------- /screenshots/ja_JP/ja_JP_0Home-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/ja_JP/ja_JP_0Home-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/ja_JP/ja_JP_0Home-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/ja_JP/ja_JP_0Home-iPad.png -------------------------------------------------------------------------------- /screenshots/ja_JP/ja_JP_0Home-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/ja_JP/ja_JP_0Home-iPhone.png -------------------------------------------------------------------------------- /screenshots/ja_JP/ja_JP_1Location-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/ja_JP/ja_JP_1Location-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/ja_JP/ja_JP_1Location-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/ja_JP/ja_JP_1Location-iPad.png -------------------------------------------------------------------------------- /screenshots/ja_JP/ja_JP_1Location-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/ja_JP/ja_JP_1Location-iPhone.png -------------------------------------------------------------------------------- /screenshots/ja_JP/ja_JP_2Acceleration-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/ja_JP/ja_JP_2Acceleration-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/ja_JP/ja_JP_2Acceleration-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/ja_JP/ja_JP_2Acceleration-iPad.png -------------------------------------------------------------------------------- /screenshots/ja_JP/ja_JP_2Acceleration-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/ja_JP/ja_JP_2Acceleration-iPhone.png -------------------------------------------------------------------------------- /screenshots/ja_JP/ja_JP_3Acceleration_Log-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/ja_JP/ja_JP_3Acceleration_Log-iPad.png -------------------------------------------------------------------------------- /screenshots/ja_JP/ja_JP_3Acceleration_Log-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/ja_JP/ja_JP_3Acceleration_Log-iPhone.png -------------------------------------------------------------------------------- /screenshots/ja_JP/ja_JP_4Settings-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/ja_JP/ja_JP_4Settings-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/ja_JP/ja_JP_4Settings-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/ja_JP/ja_JP_4Settings-iPad.png -------------------------------------------------------------------------------- /screenshots/ja_JP/ja_JP_4Settings-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/ja_JP/ja_JP_4Settings-iPhone.png -------------------------------------------------------------------------------- /screenshots/ko_KR/ko_KR_0Home-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/ko_KR/ko_KR_0Home-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/ko_KR/ko_KR_0Home-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/ko_KR/ko_KR_0Home-iPad.png -------------------------------------------------------------------------------- /screenshots/ko_KR/ko_KR_0Home-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/ko_KR/ko_KR_0Home-iPhone.png -------------------------------------------------------------------------------- /screenshots/ko_KR/ko_KR_1Location-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/ko_KR/ko_KR_1Location-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/ko_KR/ko_KR_1Location-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/ko_KR/ko_KR_1Location-iPad.png -------------------------------------------------------------------------------- /screenshots/ko_KR/ko_KR_1Location-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/ko_KR/ko_KR_1Location-iPhone.png -------------------------------------------------------------------------------- /screenshots/ko_KR/ko_KR_2Acceleration-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/ko_KR/ko_KR_2Acceleration-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/ko_KR/ko_KR_2Acceleration-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/ko_KR/ko_KR_2Acceleration-iPad.png -------------------------------------------------------------------------------- /screenshots/ko_KR/ko_KR_2Acceleration-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/ko_KR/ko_KR_2Acceleration-iPhone.png -------------------------------------------------------------------------------- /screenshots/ko_KR/ko_KR_3Acceleration_Log-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/ko_KR/ko_KR_3Acceleration_Log-iPad.png -------------------------------------------------------------------------------- /screenshots/ko_KR/ko_KR_3Acceleration_Log-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/ko_KR/ko_KR_3Acceleration_Log-iPhone.png -------------------------------------------------------------------------------- /screenshots/ko_KR/ko_KR_4Settings-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/ko_KR/ko_KR_4Settings-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/ko_KR/ko_KR_4Settings-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/ko_KR/ko_KR_4Settings-iPad.png -------------------------------------------------------------------------------- /screenshots/ko_KR/ko_KR_4Settings-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/ko_KR/ko_KR_4Settings-iPhone.png -------------------------------------------------------------------------------- /screenshots/pt_PT/pt_PT_0Home-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/pt_PT/pt_PT_0Home-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/pt_PT/pt_PT_0Home-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/pt_PT/pt_PT_0Home-iPad.png -------------------------------------------------------------------------------- /screenshots/pt_PT/pt_PT_0Home-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/pt_PT/pt_PT_0Home-iPhone.png -------------------------------------------------------------------------------- /screenshots/pt_PT/pt_PT_1Location-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/pt_PT/pt_PT_1Location-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/pt_PT/pt_PT_1Location-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/pt_PT/pt_PT_1Location-iPad.png -------------------------------------------------------------------------------- /screenshots/pt_PT/pt_PT_1Location-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/pt_PT/pt_PT_1Location-iPhone.png -------------------------------------------------------------------------------- /screenshots/pt_PT/pt_PT_2Acceleration-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/pt_PT/pt_PT_2Acceleration-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/pt_PT/pt_PT_2Acceleration-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/pt_PT/pt_PT_2Acceleration-iPad.png -------------------------------------------------------------------------------- /screenshots/pt_PT/pt_PT_2Acceleration-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/pt_PT/pt_PT_2Acceleration-iPhone.png -------------------------------------------------------------------------------- /screenshots/pt_PT/pt_PT_3Acceleration_Log-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/pt_PT/pt_PT_3Acceleration_Log-iPad.png -------------------------------------------------------------------------------- /screenshots/pt_PT/pt_PT_3Acceleration_Log-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/pt_PT/pt_PT_3Acceleration_Log-iPhone.png -------------------------------------------------------------------------------- /screenshots/pt_PT/pt_PT_4Settings-AppleWatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/pt_PT/pt_PT_4Settings-AppleWatch.png -------------------------------------------------------------------------------- /screenshots/pt_PT/pt_PT_4Settings-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/pt_PT/pt_PT_4Settings-iPad.png -------------------------------------------------------------------------------- /screenshots/pt_PT/pt_PT_4Settings-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/pt_PT/pt_PT_4Settings-iPhone.png -------------------------------------------------------------------------------- /screenshots/zh_Hans/zh_Hans_0Home-Applewatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/zh_Hans/zh_Hans_0Home-Applewatch.png -------------------------------------------------------------------------------- /screenshots/zh_Hans/zh_Hans_0Home-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/zh_Hans/zh_Hans_0Home-iPad.png -------------------------------------------------------------------------------- /screenshots/zh_Hans/zh_Hans_0Home-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/zh_Hans/zh_Hans_0Home-iPhone.png -------------------------------------------------------------------------------- /screenshots/zh_Hans/zh_Hans_1Location-Applewatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/zh_Hans/zh_Hans_1Location-Applewatch.png -------------------------------------------------------------------------------- /screenshots/zh_Hans/zh_Hans_1Location-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/zh_Hans/zh_Hans_1Location-iPad.png -------------------------------------------------------------------------------- /screenshots/zh_Hans/zh_Hans_1Location-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/zh_Hans/zh_Hans_1Location-iPhone.png -------------------------------------------------------------------------------- /screenshots/zh_Hans/zh_Hans_2Acceleration-Applewatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/zh_Hans/zh_Hans_2Acceleration-Applewatch.png -------------------------------------------------------------------------------- /screenshots/zh_Hans/zh_Hans_2Acceleration-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/zh_Hans/zh_Hans_2Acceleration-iPad.png -------------------------------------------------------------------------------- /screenshots/zh_Hans/zh_Hans_2Acceleration-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/zh_Hans/zh_Hans_2Acceleration-iPhone.png -------------------------------------------------------------------------------- /screenshots/zh_Hans/zh_Hans_3Acceleration_Log-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/zh_Hans/zh_Hans_3Acceleration_Log-iPad.png -------------------------------------------------------------------------------- /screenshots/zh_Hans/zh_Hans_3Acceleration_Log-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/zh_Hans/zh_Hans_3Acceleration_Log-iPhone.png -------------------------------------------------------------------------------- /screenshots/zh_Hans/zh_Hans_4Settings-Applewatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/zh_Hans/zh_Hans_4Settings-Applewatch.png -------------------------------------------------------------------------------- /screenshots/zh_Hans/zh_Hans_4Settings-iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/zh_Hans/zh_Hans_4Settings-iPad.png -------------------------------------------------------------------------------- /screenshots/zh_Hans/zh_Hans_4Settings-iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volker88/Sensor-App/25b1221f21fdf7eeffda8e7577b4e0a0e56b0ef8/screenshots/zh_Hans/zh_Hans_4Settings-iPhone.png --------------------------------------------------------------------------------