diff --git a/PRIVACYPOLICY.md b/PRIVACYPOLICY.md new file mode 100644 index 000000000..63a5e40a0 --- /dev/null +++ b/PRIVACYPOLICY.md @@ -0,0 +1,11 @@ +# Privacy Policy + +Your privacy is very important to us. Therefore we consider al your personal information yours and yours alone. No data is stored or transmitted and we do no include any advertising or analytics software that talks to third parties. + +## What information is collected? + +Formulus does not collect any personal information. All login information is only stored locally on your device. + +## Contact + +If you have questions or concerns about this Privacy Policy, please [contact us](https://forum.opendataensemble.org/). \ No newline at end of file diff --git a/formulus/assets/images/AppleAppStore_screenshots.xcf b/formulus/assets/images/AppleAppStore_screenshots.xcf new file mode 100644 index 000000000..305269ba4 Binary files /dev/null and b/formulus/assets/images/AppleAppStore_screenshots.xcf differ diff --git a/formulus/assets/images/logo_1024x1024.png b/formulus/assets/images/logo_1024x1024.png new file mode 100644 index 000000000..480e3731b Binary files /dev/null and b/formulus/assets/images/logo_1024x1024.png differ diff --git a/formulus/assets/images/logo_1024x1024.xcf b/formulus/assets/images/logo_1024x1024.xcf new file mode 100644 index 000000000..fd729ac62 Binary files /dev/null and b/formulus/assets/images/logo_1024x1024.xcf differ diff --git a/formulus/assets/images/logo_1024x1024_tinted.png b/formulus/assets/images/logo_1024x1024_tinted.png new file mode 100644 index 000000000..3ee552dec Binary files /dev/null and b/formulus/assets/images/logo_1024x1024_tinted.png differ diff --git a/formulus/ios/Formulus.xcodeproj/project.pbxproj b/formulus/ios/Formulus.xcodeproj/project.pbxproj index 4cf88c521..ccd9c60e1 100644 --- a/formulus/ios/Formulus.xcodeproj/project.pbxproj +++ b/formulus/ios/Formulus.xcodeproj/project.pbxproj @@ -14,6 +14,7 @@ 761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; 92DE80C6F6664AA2AF375F73 /* formplayer_dist in Resources */ = {isa = PBXBuildFile; fileRef = EBDD571F89894B28B8C91913 /* formplayer_dist */; }; + A1B2C3D4E5F60718293A4B5C /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3D4E5F60718293A4B5D /* SceneDelegate.swift */; }; A403F6B5C831432092D6432A /* formulus-api.js in Resources */ = {isa = PBXBuildFile; fileRef = 672207178378430481699308 /* formulus-api.js */; }; D1F3D891BD4E91B83D93379D /* libPods-Formulus.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 35A5068D571FAAC91153000D /* libPods-Formulus.a */; }; E2D4C3847B9CFE032014F121 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; }; @@ -31,6 +32,7 @@ 761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = Formulus/AppDelegate.swift; sourceTree = ""; }; 771907CB95B14EB19B4CB206 /* placeholder_app.html */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = placeholder_app.html; path = ../assets/webview/placeholder_app.html; sourceTree = ""; }; 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = Formulus/LaunchScreen.storyboard; sourceTree = ""; }; + A1B2C3D4E5F60718293A4B5D /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SceneDelegate.swift; path = Formulus/SceneDelegate.swift; sourceTree = ""; }; B069022F6C1C274ADFA7DCBD /* Pods-Formulus.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Formulus.debug.xcconfig"; path = "Target Support Files/Pods-Formulus/Pods-Formulus.debug.xcconfig"; sourceTree = ""; }; B9003E8457E34C9983C8BD62 /* FormulusInjectionScript.js */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = FormulusInjectionScript.js; path = ../assets/webview/FormulusInjectionScript.js; sourceTree = ""; }; EBDD571F89894B28B8C91913 /* formplayer_dist */ = {isa = PBXFileReference; lastKnownFileType = folder; path = formplayer_dist; sourceTree = ""; }; @@ -54,6 +56,7 @@ children = ( 13B07FB51A68108700A75B9A /* Images.xcassets */, 761780EC2CA45674006654EE /* AppDelegate.swift */, + A1B2C3D4E5F60718293A4B5D /* SceneDelegate.swift */, 13B07FB61A68108700A75B9A /* Info.plist */, 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */, 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */, @@ -134,6 +137,7 @@ 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, 563235D0618B399E9E2C5576 /* [CP] Embed Pods Frameworks */, + A7E4F1C29B8D4056A13E70D2 /* Generate Hermes dSYM */, FFE8138CF617206B3248B8EF /* [CP] Copy Pods Resources */, ); buildRules = ( @@ -254,6 +258,23 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Formulus/Pods-Formulus-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; + A7E4F1C29B8D4056A13E70D2 /* Generate Hermes dSYM */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/hermes-engine/destroot/Library/Frameworks/universal/hermesvm.xcframework/ios-arm64/hermesvm.framework/hermesvm", + ); + name = "Generate Hermes dSYM"; + outputPaths = ( + "${DWARF_DSYM_FOLDER_PATH}/hermesvm.framework.dSYM", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/scripts/generate-hermes-dsym.sh\"\n"; + showEnvVarsInLog = 0; + }; FFE8138CF617206B3248B8EF /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -283,6 +304,7 @@ buildActionMask = 2147483647; files = ( 761780ED2CA45674006654EE /* AppDelegate.swift in Sources */, + A1B2C3D4E5F60718293A4B5C /* SceneDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -294,9 +316,10 @@ baseConfigurationReference = B069022F6C1C274ADFA7DCBD /* Pods-Formulus.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = 24; - DEVELOPMENT_TEAM = ZZ9482ZVMJ; + DEVELOPMENT_TEAM = 57WY3GA5K7; ENABLE_BITCODE = NO; INFOPLIST_FILE = Formulus/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.1; @@ -310,7 +333,7 @@ "-ObjC", "-lc++", ); - PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.opendataensemble.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = Formulus; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; @@ -328,9 +351,10 @@ baseConfigurationReference = 4BC86BCC31A36E17B33DBE8D /* Pods-Formulus.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = 24; - DEVELOPMENT_TEAM = ZZ9482ZVMJ; + DEVELOPMENT_TEAM = 57WY3GA5K7; INFOPLIST_FILE = Formulus/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.1; LD_RUNPATH_SEARCH_PATHS = ( @@ -343,7 +367,7 @@ "-ObjC", "-lc++", ); - PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.opendataensemble.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = Formulus; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; @@ -386,8 +410,10 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; + DEVELOPMENT_TEAM = 57WY3GA5K7; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = ""; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; @@ -427,6 +453,7 @@ ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; + STRING_CATALOG_GENERATE_SYMBOLS = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; USE_HERMES = true; }; @@ -463,8 +490,10 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = YES; + DEVELOPMENT_TEAM = 57WY3GA5K7; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = ""; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; diff --git a/formulus/ios/Formulus/AppDelegate.swift b/formulus/ios/Formulus/AppDelegate.swift index d5b205bf8..4ff37653c 100644 --- a/formulus/ios/Formulus/AppDelegate.swift +++ b/formulus/ios/Formulus/AppDelegate.swift @@ -9,11 +9,14 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var reactNativeDelegate: ReactNativeDelegate? var reactNativeFactory: RCTReactNativeFactory? + var launchOptions: [UIApplication.LaunchOptionsKey: Any]? func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil ) -> Bool { + self.launchOptions = launchOptions + let delegate = ReactNativeDelegate() let factory = RCTReactNativeFactory(delegate: delegate) delegate.dependencyProvider = RCTAppDependencyProvider() @@ -21,14 +24,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { reactNativeDelegate = delegate reactNativeFactory = factory - window = UIWindow(frame: UIScreen.main.bounds) - - factory.startReactNative( - withModuleName: "Formulus", - in: window, - launchOptions: launchOptions - ) - return true } } diff --git a/formulus/ios/Formulus/Images.xcassets/AppIcon.appiconset/Contents.json b/formulus/ios/Formulus/Images.xcassets/AppIcon.appiconset/Contents.json index 81213230d..ca593e53b 100644 --- a/formulus/ios/Formulus/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/formulus/ios/Formulus/Images.xcassets/AppIcon.appiconset/Contents.json @@ -1,48 +1,33 @@ { "images" : [ { - "idiom" : "iphone", - "scale" : "2x", - "size" : "20x20" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "20x20" - }, - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "29x29" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "29x29" - }, - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "40x40" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "40x40" - }, - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "60x60" + "filename" : "logo_1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" }, { - "idiom" : "iphone", - "scale" : "3x", - "size" : "60x60" + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "logo_1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" }, { - "idiom" : "ios-marketing", - "scale" : "1x", + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "logo_1024x1024_tinted.png", + "idiom" : "universal", + "platform" : "ios", "size" : "1024x1024" } ], diff --git a/formulus/ios/Formulus/Images.xcassets/AppIcon.appiconset/logo_1024x1024.png b/formulus/ios/Formulus/Images.xcassets/AppIcon.appiconset/logo_1024x1024.png new file mode 100644 index 000000000..480e3731b Binary files /dev/null and b/formulus/ios/Formulus/Images.xcassets/AppIcon.appiconset/logo_1024x1024.png differ diff --git a/formulus/ios/Formulus/Images.xcassets/AppIcon.appiconset/logo_1024x1024_tinted.png b/formulus/ios/Formulus/Images.xcassets/AppIcon.appiconset/logo_1024x1024_tinted.png new file mode 100644 index 000000000..3ee552dec Binary files /dev/null and b/formulus/ios/Formulus/Images.xcassets/AppIcon.appiconset/logo_1024x1024_tinted.png differ diff --git a/formulus/ios/Formulus/Images.xcassets/Contents.json b/formulus/ios/Formulus/Images.xcassets/Contents.json index 2d92bd53f..73c00596a 100644 --- a/formulus/ios/Formulus/Images.xcassets/Contents.json +++ b/formulus/ios/Formulus/Images.xcassets/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } } diff --git a/formulus/ios/Formulus/Info.plist b/formulus/ios/Formulus/Info.plist index a307aa846..a39b6628c 100644 --- a/formulus/ios/Formulus/Info.plist +++ b/formulus/ios/Formulus/Info.plist @@ -37,8 +37,34 @@ Formulus needs access to your location to add geolocation data to observations for accurate data collection. NSLocationWhenInUseUsageDescription Formulus needs access to your location to add geolocation data to observations for accurate data collection. + NSMicrophoneUsageDescription + Formulus needs access to your microphone to add voice note data to observations for accurate data collection. + NSPhotoLibraryUsageDescription + Formulus needs access to your photo library if you wish to upload photo observations for accurate data collection. RCTNewArchEnabled + UIAppFonts + + MaterialDesignIcons.ttf + MaterialIcons.ttf + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + + + + UILaunchStoryboardName LaunchScreen UIRequiredDeviceCapabilities @@ -51,12 +77,14 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight - UIViewControllerBasedStatusBarAppearance - - UIAppFonts + UISupportedInterfaceOrientations~ipad - MaterialDesignIcons.ttf - MaterialIcons.ttf + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + UIViewControllerBasedStatusBarAppearance + diff --git a/formulus/ios/Formulus/SceneDelegate.swift b/formulus/ios/Formulus/SceneDelegate.swift new file mode 100644 index 000000000..6df46edca --- /dev/null +++ b/formulus/ios/Formulus/SceneDelegate.swift @@ -0,0 +1,32 @@ +import UIKit + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + var window: UIWindow? + + func scene( + _ scene: UIScene, + willConnectTo session: UISceneSession, + options connectionOptions: UIScene.ConnectionOptions + ) { + guard let windowScene = scene as? UIWindowScene else { + return + } + + guard let appDelegate = UIApplication.shared.delegate as? AppDelegate, + let factory = appDelegate.reactNativeFactory + else { + return + } + + let window = UIWindow(windowScene: windowScene) + self.window = window + // Keep AppDelegate.window in sync for libraries that still read it. + appDelegate.window = window + + factory.startReactNative( + withModuleName: "Formulus", + in: window, + launchOptions: appDelegate.launchOptions + ) + } +} diff --git a/formulus/ios/scripts/generate-hermes-dsym.sh b/formulus/ios/scripts/generate-hermes-dsym.sh new file mode 100755 index 000000000..a3b0f619c --- /dev/null +++ b/formulus/ios/scripts/generate-hermes-dsym.sh @@ -0,0 +1,31 @@ +#!/bin/sh +# Generate hermesvm.framework.dSYM for App Store symbol upload. +# Prebuilt Hermes ships without a matching dSYM; Xcode 16+ warns on archive upload. +set -e + +if [ "${CONFIGURATION}" != "Release" ]; then + echo "Skipping Hermes dSYM (CONFIGURATION=${CONFIGURATION})" + exit 0 +fi + +EMBEDDED_HERMES_BIN="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermesvm.framework/hermesvm" +PODS_HERMES_BIN="${PODS_ROOT}/hermes-engine/destroot/Library/Frameworks/universal/hermesvm.xcframework/ios-arm64/hermesvm.framework/hermesvm" + +if [ -f "${EMBEDDED_HERMES_BIN}" ]; then + HERMES_BIN="${EMBEDDED_HERMES_BIN}" +elif [ -f "${PODS_HERMES_BIN}" ]; then + HERMES_BIN="${PODS_HERMES_BIN}" +else + echo "warning: hermesvm binary not found; skipping dSYM generation" + exit 0 +fi + +DSYM_OUTPUT="${DWARF_DSYM_FOLDER_PATH}/hermesvm.framework.dSYM" + +echo "Generating Hermes dSYM from: ${HERMES_BIN}" +echo "Output: ${DSYM_OUTPUT}" + +rm -rf "${DSYM_OUTPUT}" +# Prebuilt Hermes has no local object files; dsymutil still emits a UUID-matched +# dSYM that satisfies App Store symbol upload checks (stderr is noisy otherwise). +dsymutil "${HERMES_BIN}" -o "${DSYM_OUTPUT}" 2>/dev/null