diff --git a/ios/AlerteSecours.xcodeproj/project.pbxproj b/ios/AlerteSecours.xcodeproj/project.pbxproj index 50a42c2..5d658d2 100644 --- a/ios/AlerteSecours.xcodeproj/project.pbxproj +++ b/ios/AlerteSecours.xcodeproj/project.pbxproj @@ -3,13 +3,14 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; + 14D5B34E2ED75B470019B7A4 /* MapLibre in Frameworks */ = {isa = PBXBuildFile; productRef = 190140CCA04FF5320B13EF02 /* MapLibre */; }; 1BBEED8C23934E2FB66E5E99 /* noop-file.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA27B437DADB447888114546 /* noop-file.swift */; }; 35CD05A718E2ABA5E9E4C339 /* Pods_AlerteSecours.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7E5BBA8AFAF4DDE1BA21D931 /* Pods_AlerteSecours.framework */; }; 3D137242100D4373995DC4E1 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7ECA2CAAE2DA44D8B2037DD1 /* GoogleService-Info.plist */; }; @@ -35,7 +36,7 @@ BA27B437DADB447888114546 /* noop-file.swift */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.swift; name = "noop-file.swift"; path = "AlerteSecours/noop-file.swift"; sourceTree = ""; }; BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Expo.plist; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; - F1755F69573E34927548F1C6 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; name = PrivacyInfo.xcprivacy; path = AlerteSecours/PrivacyInfo.xcprivacy; sourceTree = ""; }; + F1755F69573E34927548F1C6 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = AlerteSecours/PrivacyInfo.xcprivacy; sourceTree = ""; }; FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-AlerteSecours/ExpoModulesProvider.swift"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -44,6 +45,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 14D5B34E2ED75B470019B7A4 /* MapLibre in Frameworks */, 35CD05A718E2ABA5E9E4C339 /* Pods_AlerteSecours.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -957,7 +959,10 @@ ); INFOPLIST_FILE = AlerteSecours/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.1; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); MARKETING_VERSION = 1.0; OTHER_LDFLAGS = ( "$(inherited)", @@ -982,13 +987,17 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = AlerteSecours/AlerteSecours.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 2PZ49Y23LX; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; INFOPLIST_FILE = AlerteSecours/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.1; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); MARKETING_VERSION = 1.0; OTHER_LDFLAGS = ( "$(inherited)", @@ -998,6 +1007,7 @@ OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = com.alertesecours.alertesecours; PRODUCT_NAME = AlerteSecours; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "AlerteSecours/AlerteSecours-Bridging-Header.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -1064,7 +1074,10 @@ "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios", ); IPHONEOS_DEPLOYMENT_TARGET = 15.1; - LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)"; + LD_RUNPATH_SEARCH_PATHS = ( + /usr/lib/swift, + "$(inherited)", + ); LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\""; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; @@ -1131,7 +1144,10 @@ "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios", ); IPHONEOS_DEPLOYMENT_TARGET = 15.1; - LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)"; + LD_RUNPATH_SEARCH_PATHS = ( + /usr/lib/swift, + "$(inherited)", + ); LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\""; MTL_ENABLE_DEBUG_INFO = NO; OTHER_LDFLAGS = ( diff --git a/ios/AlerteSecours/AlerteSecours-Bridging-Header.h b/ios/AlerteSecours/AlerteSecours-Bridging-Header.h index e11d920..bbefcdb 100644 --- a/ios/AlerteSecours/AlerteSecours-Bridging-Header.h +++ b/ios/AlerteSecours/AlerteSecours-Bridging-Header.h @@ -1,3 +1,10 @@ // // Use this file to import your target's public headers that you would like to expose to Swift. // + +#if __has_include() +#import +#elif __has_include() +// for importing the header from framework, the dash will be transformed to underscore +#import +#endif diff --git a/ios/AlerteSecours/AppDelegate.h b/ios/AlerteSecours/AppDelegate.h index 1658a43..6e220cb 100644 --- a/ios/AlerteSecours/AppDelegate.h +++ b/ios/AlerteSecours/AppDelegate.h @@ -1,7 +1,10 @@ -#import #import -#import -@interface AppDelegate : EXAppDelegateWrapper +// Legacy Objective-C AppDelegate left in place for compatibility. +// The actual application delegate is implemented in Swift as `AppDelegate` +// (subclassing ExpoAppDelegate). +@interface LegacyAppDelegate : UIResponder + +@property (nonatomic, strong) UIWindow *window; @end diff --git a/ios/AlerteSecours/AppDelegate.mm b/ios/AlerteSecours/AppDelegate.mm index cd4abbf..c1b3ece 100644 --- a/ios/AlerteSecours/AppDelegate.mm +++ b/ios/AlerteSecours/AppDelegate.mm @@ -1,75 +1,11 @@ #import "AppDelegate.h" -// @generated begin react-native-background-fetch-import - expo prebuild (DO NOT MODIFY) sync-fb890e6efd6cc6e67ebbda1087e0a6d7e0bcc527 -#import -// @generated end react-native-background-fetch-import -#import -#import -#import +@implementation LegacyAppDelegate -@implementation AppDelegate - -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions -{ -// @generated begin @react-native-firebase/app-didFinishLaunchingWithOptions - expo prebuild (DO NOT MODIFY) sync-ecd111c37e49fdd1ed6354203cd6b1e2a38cccda -[FIRApp configure]; -// @generated end @react-native-firebase/app-didFinishLaunchingWithOptions - self.moduleName = @"main"; - - // You can add your custom initial props in the dictionary below. - // They will be passed down to the ViewController used by React Native. - self.initialProps = @{}; -// @generated begin react-native-background-fetch-didFinishLaunchingWithOptions - expo prebuild (DO NOT MODIFY) sync-2d5ef5f3788ef11f6e72e5480fdbef5f7a21f0f0 - [[TSBackgroundFetch sharedInstance] didFinishLaunching]; -// @generated end react-native-background-fetch-didFinishLaunchingWithOptions -// @generated begin react-native-background-fetch-didFinishLaunching - expo prebuild (DO NOT MODIFY) sync-2d5ef5f3788ef11f6e72e5480fdbef5f7a21f0f0 - [[TSBackgroundFetch sharedInstance] didFinishLaunching]; -// @generated end react-native-background-fetch-didFinishLaunching - - return [super application:application didFinishLaunchingWithOptions:launchOptions]; -} - -- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge -{ - return [self bundleURL]; -} - -- (NSURL *)bundleURL -{ -#if DEBUG - return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@".expo/.virtual-metro-entry"]; -#else - return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; -#endif -} - -// Linking API -- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options { - return [super application:application openURL:url options:options] || [RCTLinkingManager application:application openURL:url options:options]; -} - -// Universal Links -- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray> * _Nullable))restorationHandler { - BOOL result = [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler]; - return [super application:application continueUserActivity:userActivity restorationHandler:restorationHandler] || result; -} - -// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries -- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken -{ - return [super application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; -} - -// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries -- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error -{ - return [super application:application didFailToRegisterForRemoteNotificationsWithError:error]; -} - -// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries -- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler -{ - return [super application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler]; -} +// This legacy Objective-C AppDelegate is no longer used as the main +// application delegate. The app now uses the Swift-based +// `AppDelegate: ExpoAppDelegate` defined in AppDelegate.swift. +// We keep this stub implementation so that any generated references +// to `LegacyAppDelegate` continue to compile. @end diff --git a/ios/AlerteSecours/AppDelegate.swift b/ios/AlerteSecours/AppDelegate.swift new file mode 100644 index 0000000..c2d7a0d --- /dev/null +++ b/ios/AlerteSecours/AppDelegate.swift @@ -0,0 +1,29 @@ +import ExpoModulesCore +import FirebaseCore +import UIKit + +@objc(AppDelegate) +class AppDelegate: ExpoAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil + ) -> Bool { + // Log to verify the new ExpoAppDelegate-based path runs on iOS. + NSLog("[AppDelegate] didFinishLaunchingWithOptions (ExpoAppDelegate)") + + // Configure Firebase (react-native-firebase) + FirebaseApp.configure() + + // Transistorsoft background fetch bootstrap. + TSBackgroundFetch.sharedInstance().didFinishLaunching() + NSLog("[AppDelegate] TSBackgroundFetch didFinishLaunching") + + // Configure React Native root module. + moduleName = "main" + initialProps = [:] + + let result = super.application(application, didFinishLaunchingWithOptions: launchOptions) + NSLog("[AppDelegate] super.application(...) -> %@", result ? "true" : "false") + return result + } +} diff --git a/ios/AlerteSecours/main.m b/ios/AlerteSecours/main.m index 25181b6..bb7a13b 100644 --- a/ios/AlerteSecours/main.m +++ b/ios/AlerteSecours/main.m @@ -1,10 +1,12 @@ #import - -#import "AppDelegate.h" +#import "AlerteSecours-Swift.h" int main(int argc, char * argv[]) { @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + // Use the Swift ExpoAppDelegate-based AppDelegate as the principal class. + // The Objective-C runtime name of the Swift class is `AppDelegate` + // because it is annotated with `@objc(AppDelegate)` in AppDelegate.swift. + return UIApplicationMain(argc, argv, nil, @"AppDelegate"); } } diff --git a/ios/AlerteSecours/noop-file.swift b/ios/AlerteSecours/noop-file.swift index b2ffafb..3e20af0 100644 --- a/ios/AlerteSecours/noop-file.swift +++ b/ios/AlerteSecours/noop-file.swift @@ -1,4 +1,71 @@ -// -// @generated -// A blank Swift file must be created for native modules with Swift files to work correctly. -// +import EXUpdates +import FirebaseCore +import React +import TSBackgroundFetch +import UIKit + +@objc(AppDelegate) +class AppDelegate: RCTAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil + ) -> Bool { + NSLog("[AppDelegate] didFinishLaunchingWithOptions (RCTAppDelegate)") + + // Configure Firebase (react-native-firebase). + FirebaseApp.configure() + + // Transistorsoft background fetch bootstrap. + TSBackgroundFetch.sharedInstance().didFinishLaunching() + NSLog("[AppDelegate] TSBackgroundFetch didFinishLaunching") + + // Initialize expo-updates controller so AppController.sharedInstance can be used safely. + AppController.initializeWithoutStarting() + + // Configure React Native root module. + self.moduleName = "main" + self.initialProps = [:] + + let result = super.application(application, didFinishLaunchingWithOptions: launchOptions) + NSLog("[AppDelegate] super.application(...) -> %@", result ? "true" : "false") + return result + } + + // MARK: - RCTBridgeDelegate / JS bundle location + + @objc + override func sourceURL(for bridge: RCTBridge!) -> URL! { + #if DEBUG + let url = RCTBundleURLProvider.sharedSettings().jsBundleURL( + forBundleRoot: ".expo/.virtual-metro-entry", + fallbackExtension: nil + ) + NSLog("[AppDelegate] sourceURL(for:) (DEBUG) -> %@", url?.absoluteString ?? "nil") + return url + #else + let url = self.bundleURL() + NSLog("[AppDelegate] sourceURL(for:) (RELEASE) -> %@", url?.absoluteString ?? "nil") + return url + #endif + } + + @objc + override func bundleURL() -> URL! { + #if DEBUG + let url = RCTBundleURLProvider.sharedSettings().jsBundleURL( + forBundleRoot: ".expo/.virtual-metro-entry", + fallbackExtension: nil + ) + NSLog("[AppDelegate] bundleURL() (DEBUG) -> %@", url?.absoluteString ?? "nil") + return url + #else + let url = Bundle.main.url(forResource: "main", withExtension: "jsbundle") + if let url = url { + NSLog("[AppDelegate] bundleURL() (RELEASE) -> %@", url.absoluteString) + } else { + NSLog("[AppDelegate] bundleURL() (RELEASE) -> nil (main.jsbundle not found)") + } + return url + #endif + } +} diff --git a/ios/RNBackgroundFetch+AppDelegate.m b/ios/RNBackgroundFetch+AppDelegate.m index 986b11c..d554945 100644 --- a/ios/RNBackgroundFetch+AppDelegate.m +++ b/ios/RNBackgroundFetch+AppDelegate.m @@ -9,10 +9,11 @@ #import #import "AppDelegate.h" #import +#import @implementation AppDelegate(AppDelegate) --(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler +- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { NSLog(@"RNBackgroundFetch AppDelegate received fetch event"); TSBackgroundFetch *fetchManager = [TSBackgroundFetch sharedInstance]; @@ -20,3 +21,59 @@ } @end + +// Guard against duplicate BGTaskScheduler registrations by TSBGTask. +// Crash observed: +// 'Launch handler for task with identifier com.transistorsoft has already been registered' +// thrown when BGTaskScheduler is asked to register the same identifier multiple times. +// We swizzle +[TSBGTask registerForTaskWithIdentifier:] to: +// - log each registration attempt +// - ignore subsequent attempts for the same identifier instead of crashing. + +@interface TSBGTask : NSObject ++ (void)registerForTaskWithIdentifier:(NSString *)identifier; +@end + +@implementation TSBGTask (ASGuard) + ++ (void)load +{ + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + Class cls = object_getClass((id)self); + SEL originalSelector = @selector(registerForTaskWithIdentifier:); + SEL swizzledSelector = @selector(as_registerForTaskWithIdentifier:); + + Method originalMethod = class_getClassMethod(cls, originalSelector); + Method swizzledMethod = class_getClassMethod(cls, swizzledSelector); + + if (originalMethod && swizzledMethod) { + method_exchangeImplementations(originalMethod, swizzledMethod); + NSLog(@"[TSBGTask+ASGuard] Swizzled +registerForTaskWithIdentifier:"); + } else { + NSLog(@"[TSBGTask+ASGuard] Failed to swizzle +registerForTaskWithIdentifier: (original=%p, swizzled=%p)", originalMethod, swizzledMethod); + } + }); +} + ++ (void)as_registerForTaskWithIdentifier:(NSString *)identifier +{ + static NSMutableSet *as_registeredTaskIdentifiers; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + as_registeredTaskIdentifiers = [NSMutableSet set]; + }); + + if ([as_registeredTaskIdentifiers containsObject:identifier]) { + NSLog(@"[TSBGTask+ASGuard] Skipping duplicate BGTaskScheduler registration for identifier '%@'", identifier); + return; + } + + NSLog(@"[TSBGTask+ASGuard] Registering BGTask identifier '%@'", identifier); + [as_registeredTaskIdentifiers addObject:identifier]; + + // Call original implementation (now swizzled). + [self as_registerForTaskWithIdentifier:identifier]; +} + +@end diff --git a/ios/exportOptions.plist b/ios/exportOptions.plist index 06b59ab..f21ed1d 100644 --- a/ios/exportOptions.plist +++ b/ios/exportOptions.plist @@ -3,7 +3,7 @@ method - app-store + app-store-connect teamID 2PZ49Y23LX compileBitcode @@ -14,11 +14,6 @@ manageAppVersionAndBuildNumber - provisioningProfiles - - com.alertesecours.alertesecours - match AppStore com.alertesecours.alertesecours - destination export distributionBundleIdentifier diff --git a/package.json b/package.json index dadbab3..1b83fd6 100644 --- a/package.json +++ b/package.json @@ -279,4 +279,4 @@ } }, "packageManager": "yarn@4.5.3" -} \ No newline at end of file +} diff --git a/scripts/ios-export.sh b/scripts/ios-export.sh index d138b9b..c84d118 100755 --- a/scripts/ios-export.sh +++ b/scripts/ios-export.sh @@ -22,6 +22,9 @@ echo "Auth key file exists at: $ASC_API_KEY_PATH" echo "File permissions:" ls -l "$ASC_API_KEY_PATH" +echo "Available code signing identities (security find-identity -v -p codesigning):" +xcrun security find-identity -v -p codesigning || echo "No code signing identities found or security tool error" + # Execute xcodebuild with the environment variables cd ios && xcodebuild -exportArchive \ -archivePath AlerteSecours.xcarchive \