11const { withDangerousMod, withAppDelegate, WarningAggregator } = require ( '@expo/config-plugins' ) ;
22const { mergeContents } = require ( '@expo/config-plugins/build/utils/generateCode' ) ;
3+ const { getAppDelegate } = require ( '@expo/config-plugins/build/ios/Paths' ) ;
34const fs = require ( 'fs' ) ;
45const path = require ( 'path' ) ;
56
6- const RNAPPSFLYER_IMPORT = `#import <RNAppsFlyer.h>\n` ;
7- const RNAPPSFLYER_CONTINUE_USER_ACTIVITY_IDENTIFIER = `- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler {` ;
8- const RNAPPSFLYER_OPENURL_IDENTIFIER = `- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {` ;
9- const RNAPPSFLYER_CONTINUE_USER_ACTIVITY_CODE = `[[AppsFlyerAttribution shared] continueUserActivity:userActivity restorationHandler:restorationHandler];\n` ;
10- const RNAPPSFLYER_OPENURL_CODE = `[[AppsFlyerAttribution shared] handleOpenUrl:url options:options];\n` ;
7+ function modifyObjcAppDelegate ( appDelegate ) {
8+ const RNAPPSFLYER_IMPORT = `#import <RNAppsFlyer.h>\n` ;
9+ const RNAPPSFLYER_CONTINUE_USER_ACTIVITY_IDENTIFIER = `- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler {` ;
10+ const RNAPPSFLYER_OPENURL_IDENTIFIER = `- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {` ;
11+ const RNAPPSFLYER_CONTINUE_USER_ACTIVITY_CODE = `[[AppsFlyerAttribution shared] continueUserActivity:userActivity restorationHandler:restorationHandler];\n` ;
12+ const RNAPPSFLYER_OPENURL_CODE = `[[AppsFlyerAttribution shared] handleOpenUrl:url options:options];\n` ;
1113
12- function modifyAppDelegate ( appDelegate ) {
1314 if ( ! appDelegate . includes ( RNAPPSFLYER_IMPORT ) ) {
1415 appDelegate = RNAPPSFLYER_IMPORT + appDelegate ;
1516 }
@@ -28,17 +29,108 @@ function modifyAppDelegate(appDelegate) {
2829 return appDelegate ;
2930}
3031
32+ function modifySwiftAppDelegate ( appDelegateContents ) {
33+ const SWIFT_OPENURL_IDENTIFIER = ` public override func application(
34+ _ app: UIApplication,
35+ open url: URL,
36+ options: [UIApplication.OpenURLOptionsKey: Any] = [:]
37+ ) -> Bool {` ;
38+ const RNAPPSFLYER_SWIFT_OPENURL_CODE = 'AppsFlyerAttribution.shared().handleOpen(url, options: options)' ;
39+
40+ const SWIFT_CONTINUE_USER_ACTIVITY_IDENTIFIER = ` public override func application(
41+ _ application: UIApplication,
42+ continue userActivity: NSUserActivity,
43+ restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
44+ ) -> Bool {` ;
45+ const RNAPPSFLYER_SWIFT_CONTINUE_USER_ACTIVITY_CODE = 'AppsFlyerAttribution.shared().continue(userActivity, restorationHandler: nil)' ;
46+
47+ if ( appDelegateContents . includes ( SWIFT_OPENURL_IDENTIFIER ) && ! appDelegateContents . includes ( RNAPPSFLYER_SWIFT_OPENURL_CODE ) ) {
48+ appDelegateContents = appDelegateContents . replace ( SWIFT_OPENURL_IDENTIFIER , `${ SWIFT_OPENURL_IDENTIFIER } \n ${ RNAPPSFLYER_SWIFT_OPENURL_CODE } ` ) ;
49+ }
50+
51+ if ( appDelegateContents . includes ( SWIFT_CONTINUE_USER_ACTIVITY_IDENTIFIER ) && ! appDelegateContents . includes ( RNAPPSFLYER_SWIFT_CONTINUE_USER_ACTIVITY_CODE ) ) {
52+ appDelegateContents = appDelegateContents . replace ( SWIFT_CONTINUE_USER_ACTIVITY_IDENTIFIER , `${ SWIFT_CONTINUE_USER_ACTIVITY_IDENTIFIER } \n ${ RNAPPSFLYER_SWIFT_CONTINUE_USER_ACTIVITY_CODE } ` ) ;
53+ }
54+
55+ if ( ! appDelegateContents . includes ( RNAPPSFLYER_SWIFT_OPENURL_CODE ) || ! appDelegateContents . includes ( RNAPPSFLYER_SWIFT_CONTINUE_USER_ACTIVITY_CODE ) ) {
56+ WarningAggregator . addWarningIOS (
57+ 'withAppsFlyerAppDelegate' ,
58+ `
59+ Automatic Swift AppDelegate modification failed.
60+ Please add AppsFlyer integration manually:
61+
62+ 1. Add this to your openURL method:
63+ AppsFlyerAttribution.shared().handleOpen(url, options: options)
64+
65+ 2. Add this to your continueUserActivity method:
66+ AppsFlyerAttribution.shared().continue(userActivity, restorationHandler: nil)
67+
68+ Supported format: Expo SDK default template
69+ `
70+ ) ;
71+ }
72+
73+ return appDelegateContents ;
74+ }
75+
3176function withAppsFlyerAppDelegate ( config ) {
3277 return withAppDelegate ( config , ( config ) => {
33- if ( [ 'objc' , 'objcpp' ] . includes ( config . modResults . language ) ) {
34- config . modResults . contents = modifyAppDelegate ( config . modResults . contents ) ;
78+ const language = config . modResults . language ;
79+
80+ if ( [ 'objc' , 'objcpp' ] . includes ( language ) ) {
81+ config . modResults . contents = modifyObjcAppDelegate ( config . modResults . contents ) ;
82+ } else if ( language === 'swift' ) {
83+ config . modResults . contents = modifySwiftAppDelegate ( config . modResults . contents ) ;
3584 } else {
36- WarningAggregator . addWarningIOS ( 'withAppsFlyerAppDelegate' , `${ config . modResults . language } AppDelegate file is not supported yet` ) ;
85+ WarningAggregator . addWarningIOS ( 'withAppsFlyerAppDelegate' , `${ language } AppDelegate file is not supported yet` ) ;
3786 }
3887 return config ;
3988 } ) ;
4089}
4190
91+ function withIosBridgingHeader ( config ) {
92+ return withDangerousMod ( config , [
93+ 'ios' ,
94+ async ( config ) => {
95+ const projectRoot = config . modRequest . projectRoot ;
96+ const projectName = config . modRequest . projectName || config . name ;
97+ const appDelegate = getAppDelegate ( projectRoot ) ;
98+
99+ if ( appDelegate . language === 'swift' ) {
100+ const bridgingHeaderPath = path . join (
101+ config . modRequest . platformProjectRoot ,
102+ config . name ,
103+ `${ projectName } -Bridging-Header.h` ,
104+ ) ;
105+
106+ if ( fs . existsSync ( bridgingHeaderPath ) ) {
107+ let content = fs . readFileSync ( bridgingHeaderPath , 'utf8' ) ;
108+ const appsFlyerImport = '#import <RNAppsFlyer.h>' ;
109+
110+ if ( ! content . includes ( appsFlyerImport ) ) {
111+ content += `${ appsFlyerImport } \n` ;
112+ fs . writeFileSync ( bridgingHeaderPath , content ) ;
113+ }
114+ } else {
115+ WarningAggregator . addWarningIOS (
116+ 'withIosBridgingHeader' ,
117+ `
118+ Failed to detect ${ bridgingHeaderPath } file. Please add AppsFlyer integration manually:
119+ #import <RNAppsFlyer.h>
120+
121+ Supported format: Expo SDK default template
122+ `
123+ ) ;
124+ }
125+
126+ return config ;
127+ }
128+
129+ return config ;
130+ } ,
131+ ] ) ;
132+ } ;
133+
42134function withPodfile ( config , shouldUseStrictMode ) {
43135 return withDangerousMod ( config , [
44136 'ios' ,
@@ -69,6 +161,7 @@ function withPodfile(config, shouldUseStrictMode) {
69161
70162module . exports = function withAppsFlyerIos ( config , shouldUseStrictMode ) {
71163 config = withPodfile ( config , shouldUseStrictMode ) ;
164+ config = withIosBridgingHeader ( config ) ;
72165 config = withAppsFlyerAppDelegate ( config ) ;
73166 return config ;
74167} ;
0 commit comments