From 0af8284456dbfb06e70ff3fbc8f8466e20f4cb74 Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Thu, 16 Jul 2026 07:44:50 -0700 Subject: [PATCH] chore(mobile): expose signing and bundle identities through envvars and config (#29077) * chore(mobile): expose signing and bundle identities through envvars and config * Move instructions to now shared docs/developer/setup.md --- docs/docs/developer/setup.md | 15 +++++++ mobile/ios/.gitignore | 3 ++ mobile/ios/Runner.xcodeproj/project.pbxproj | 47 ++++++++++++--------- mobile/ios/Signing.xcconfig | 29 +++++++++++++ 4 files changed, 73 insertions(+), 21 deletions(-) create mode 100644 mobile/ios/Signing.xcconfig diff --git a/docs/docs/developer/setup.md b/docs/docs/developer/setup.md index 8789d0c4e9..de02ab3e54 100644 --- a/docs/docs/developer/setup.md +++ b/docs/docs/developer/setup.md @@ -99,6 +99,21 @@ To see local changes to `@immich/ui` in Immich, do the following: 2. Run `mise //mobile:translation` to generate the translation file. 3. Change to the `mobile/` directory and run `flutter run` to start the app. +##### iOS Code Signing + +The Immich Apple Team ID and bundle IDs are specified in `mobile/ios/Signing.xcconfig`. For local development, we provide an override mechanism. + +Create `mobile/ios/Signing.local.xcconfig` and populate it with the necessary values needed to build and sign Immich yourself. This local override file is gitignored. + +``` +IMMICH_TEAM_ID = ABCDE12345 +IMMICH_BUNDLE_ID_PROD = com.customuniqueid.immich +IMMICH_BUNDLE_ID_DEV = com.customuniqueid.immichdev +IMMICH_GROUP_ID = group.com.customuniqueid.immich +``` + +The environment values are used across Immich's targets and schemes to prevent redundant edits by contributors. + #### Translation To add a new translation text, enter the key-value pair in the `i18n/en.json` in the root of the immich project. Then run: diff --git a/mobile/ios/.gitignore b/mobile/ios/.gitignore index 63e84080df..8c827d90e9 100644 --- a/mobile/ios/.gitignore +++ b/mobile/ios/.gitignore @@ -26,6 +26,9 @@ Flutter/flutter_export_environment.sh ServiceDefinitions.json Runner/GeneratedPluginRegistrant.* +# Local signing overrides. +Signing.local.xcconfig + # Exceptions to above rules. !default.mode1v3 !default.mode2v3 diff --git a/mobile/ios/Runner.xcodeproj/project.pbxproj b/mobile/ios/Runner.xcodeproj/project.pbxproj index 96d304f3d7..293df47a6a 100644 --- a/mobile/ios/Runner.xcodeproj/project.pbxproj +++ b/mobile/ios/Runner.xcodeproj/project.pbxproj @@ -96,6 +96,7 @@ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 51516E0F2A0000000000C0F1 /* Signing.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Signing.xcconfig; sourceTree = ""; }; 8AB817AA297EDEC88B23F3F6 /* Pods_ShareExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ShareExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 937632897A02DE9C249F20A6 /* Pods-ShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.debug.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.debug.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; @@ -256,6 +257,7 @@ 97C146E51CF9000F007C117D = { isa = PBXGroup; children = ( + 51516E0F2A0000000000C0F1 /* Signing.xcconfig */, 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, FAC6F8B62D287F120078CB2F /* ShareExtension */, @@ -714,6 +716,7 @@ /* Begin XCBuildConfiguration section */ 249021D3217E4FDB00AE95B9 /* Profile */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 51516E0F2A0000000000C0F1 /* Signing.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; @@ -744,7 +747,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; - CUSTOM_GROUP_ID = group.app.immich.share.profile; + CUSTOM_GROUP_ID = "$(IMMICH_GROUP_ID).profile"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -777,7 +780,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 4; - DEVELOPMENT_TEAM = 2W7AC6T8T5; + DEVELOPMENT_TEAM = "$(IMMICH_TEAM_ID)"; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -786,7 +789,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 3.0.0; - PRODUCT_BUNDLE_IDENTIFIER = app.futo.immich.profile; + PRODUCT_BUNDLE_IDENTIFIER = "$(IMMICH_BUNDLE_ID_DEV).profile"; PRODUCT_NAME = "Immich-Profile"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -797,6 +800,7 @@ }; 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 51516E0F2A0000000000C0F1 /* Signing.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon; @@ -827,7 +831,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; - CUSTOM_GROUP_ID = group.app.immich.share.debug; + CUSTOM_GROUP_ID = "$(IMMICH_GROUP_ID).debug"; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -857,6 +861,7 @@ }; 97C147041CF9000F007C117D /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 51516E0F2A0000000000C0F1 /* Signing.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon; @@ -887,7 +892,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; - CUSTOM_GROUP_ID = group.app.immich.share; + CUSTOM_GROUP_ID = "$(IMMICH_GROUP_ID)"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -922,7 +927,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 4; - DEVELOPMENT_TEAM = 2W7AC6T8T5; + DEVELOPMENT_TEAM = "$(IMMICH_TEAM_ID)"; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -931,7 +936,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 3.0.0; - PRODUCT_BUNDLE_IDENTIFIER = app.futo.immich.debug; + PRODUCT_BUNDLE_IDENTIFIER = "$(IMMICH_BUNDLE_ID_DEV).debug"; PRODUCT_NAME = "Immich-Debug"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -951,7 +956,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 4; - DEVELOPMENT_TEAM = 2W7AC6T8T5; + DEVELOPMENT_TEAM = "$(IMMICH_TEAM_ID)"; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -960,7 +965,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 3.0.0; - PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich; + PRODUCT_BUNDLE_IDENTIFIER = "$(IMMICH_BUNDLE_ID_PROD)"; PRODUCT_NAME = Immich; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -984,7 +989,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 240; - DEVELOPMENT_TEAM = 2W7AC6T8T5; + DEVELOPMENT_TEAM = "$(IMMICH_TEAM_ID)"; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; @@ -1001,7 +1006,7 @@ MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = app.futo.immich.debug.Widget; + PRODUCT_BUNDLE_IDENTIFIER = "$(IMMICH_BUNDLE_ID_DEV).debug.Widget"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; @@ -1027,7 +1032,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 240; - DEVELOPMENT_TEAM = 2W7AC6T8T5; + DEVELOPMENT_TEAM = "$(IMMICH_TEAM_ID)"; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; @@ -1043,7 +1048,7 @@ LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MARKETING_VERSION = 1.0; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.Widget; + PRODUCT_BUNDLE_IDENTIFIER = "$(IMMICH_BUNDLE_ID_PROD).Widget"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; @@ -1067,7 +1072,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 240; - DEVELOPMENT_TEAM = 2W7AC6T8T5; + DEVELOPMENT_TEAM = "$(IMMICH_TEAM_ID)"; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; @@ -1083,7 +1088,7 @@ LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MARKETING_VERSION = 1.0; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = app.futo.immich.profile.Widget; + PRODUCT_BUNDLE_IDENTIFIER = "$(IMMICH_BUNDLE_ID_DEV).profile.Widget"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; @@ -1106,7 +1111,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 240; - DEVELOPMENT_TEAM = 2W7AC6T8T5; + DEVELOPMENT_TEAM = "$(IMMICH_TEAM_ID)"; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; @@ -1123,7 +1128,7 @@ MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = app.futo.immich.debug.ShareExtension; + PRODUCT_BUNDLE_IDENTIFIER = "$(IMMICH_BUNDLE_ID_DEV).debug.ShareExtension"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; @@ -1149,7 +1154,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 240; - DEVELOPMENT_TEAM = 2W7AC6T8T5; + DEVELOPMENT_TEAM = "$(IMMICH_TEAM_ID)"; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; @@ -1165,7 +1170,7 @@ LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MARKETING_VERSION = 1.0; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.ShareExtension; + PRODUCT_BUNDLE_IDENTIFIER = "$(IMMICH_BUNDLE_ID_PROD).ShareExtension"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; @@ -1189,7 +1194,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 240; - DEVELOPMENT_TEAM = 2W7AC6T8T5; + DEVELOPMENT_TEAM = "$(IMMICH_TEAM_ID)"; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; @@ -1205,7 +1210,7 @@ LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MARKETING_VERSION = 1.0; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = app.futo.immich.profile.ShareExtension; + PRODUCT_BUNDLE_IDENTIFIER = "$(IMMICH_BUNDLE_ID_DEV).profile.ShareExtension"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; diff --git a/mobile/ios/Signing.xcconfig b/mobile/ios/Signing.xcconfig new file mode 100644 index 0000000000..bd6b270772 --- /dev/null +++ b/mobile/ios/Signing.xcconfig @@ -0,0 +1,29 @@ +// Xcode Signing Constants +// +// These are integrated into the Xcode project at build time to allow for dynamic signing configs +// across our many targets. +// +// Create `Signing.local.xcconfig` overriding each variable to provide your own personal signing +// overrides during local development: +// IMMICH_TEAM_ID = ABCDE12345 +// IMMICH_BUNDLE_ID_PROD = com.customuniqueid.immich +// IMMICH_BUNDLE_ID_DEV = com.customuniqueid.immichdev +// IMMICH_GROUP_ID = group.com.customuniqueid.immich + +// Apple Developer Team ID used for automatically provisioning signing credentials +IMMICH_TEAM_ID = 2W7AC6T8T5 + +// Root bundle identifier for the Release configuration. +// Extension suffixes (`.ShareExtension`/`.Widget`) will be appended +IMMICH_BUNDLE_ID_PROD = app.alextran.immich + +// Root bundle identifier for the Debug and Profile configurations. +// Build scheme suffixes (`.debug`/`.profile`) will be appended +IMMICH_BUNDLE_ID_DEV = app.futo.immich + +// Root App Group identifier. +// Build scheme suffixes (`.debug`/`.profile`) will be appended +IMMICH_GROUP_ID = group.app.immich.share + +// Optional per-developer configuration override +#include? "Signing.local.xcconfig"