customerio-expo-plugin 2.5.0 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/package.json +13 -31
  2. package/plugin/app.plugin.js +1 -1
  3. package/plugin/lib/commonjs/android/withAndroidManifestUpdates.js +56 -15
  4. package/plugin/lib/commonjs/android/withAndroidManifestUpdates.js.map +1 -1
  5. package/plugin/lib/commonjs/android/withCIOAndroid.js +7 -2
  6. package/plugin/lib/commonjs/android/withCIOAndroid.js.map +1 -1
  7. package/plugin/lib/commonjs/android/withGoogleServicesJSON.js +1 -1
  8. package/plugin/lib/commonjs/android/withGoogleServicesJSON.js.map +1 -1
  9. package/plugin/lib/commonjs/android/withMainApplicationModifications.js +45 -0
  10. package/plugin/lib/commonjs/android/withMainApplicationModifications.js.map +1 -0
  11. package/plugin/lib/commonjs/android/withNotificationChannelMetadata.js +1 -1
  12. package/plugin/lib/commonjs/android/withNotificationChannelMetadata.js.map +1 -1
  13. package/plugin/lib/commonjs/android/withProjectStrings.js +14 -7
  14. package/plugin/lib/commonjs/android/withProjectStrings.js.map +1 -1
  15. package/plugin/lib/commonjs/helpers/constants/android.js +7 -1
  16. package/plugin/lib/commonjs/helpers/constants/android.js.map +1 -1
  17. package/plugin/lib/commonjs/helpers/constants/common.js +18 -0
  18. package/plugin/lib/commonjs/helpers/constants/common.js.map +1 -0
  19. package/plugin/lib/commonjs/helpers/constants/ios.js +7 -7
  20. package/plugin/lib/commonjs/helpers/constants/ios.js.map +1 -1
  21. package/plugin/lib/commonjs/helpers/native-files/android/CustomerIOSDKInitializer.kt +64 -0
  22. package/plugin/lib/commonjs/helpers/native-files/ios/CustomerIOSDKInitializer.swift +54 -0
  23. package/plugin/lib/commonjs/helpers/utils/injectCIOPodfileCode.js +2 -2
  24. package/plugin/lib/commonjs/helpers/utils/injectCIOPodfileCode.js.map +1 -1
  25. package/plugin/lib/commonjs/helpers/utils/patchPluginNativeCode.js +62 -0
  26. package/plugin/lib/commonjs/helpers/utils/patchPluginNativeCode.js.map +1 -0
  27. package/plugin/lib/commonjs/index.js +13 -2
  28. package/plugin/lib/commonjs/index.js.map +1 -1
  29. package/plugin/lib/commonjs/ios/utils.js +1 -1
  30. package/plugin/lib/commonjs/ios/utils.js.map +1 -1
  31. package/plugin/lib/commonjs/ios/withAppDelegateModifications.js +1 -1
  32. package/plugin/lib/commonjs/ios/withAppDelegateModifications.js.map +1 -1
  33. package/plugin/lib/commonjs/ios/withCIOIos.js +17 -11
  34. package/plugin/lib/commonjs/ios/withCIOIos.js.map +1 -1
  35. package/plugin/lib/commonjs/ios/withCIOIosSwift.js +133 -49
  36. package/plugin/lib/commonjs/ios/withCIOIosSwift.js.map +1 -1
  37. package/plugin/lib/commonjs/ios/withGoogleServicesJsonFile.js +1 -1
  38. package/plugin/lib/commonjs/ios/withGoogleServicesJsonFile.js.map +1 -1
  39. package/plugin/lib/commonjs/ios/withNotificationsXcodeProject.js +17 -19
  40. package/plugin/lib/commonjs/ios/withNotificationsXcodeProject.js.map +1 -1
  41. package/plugin/lib/commonjs/ios/withXcodeProject.js +1 -1
  42. package/plugin/lib/commonjs/ios/withXcodeProject.js.map +1 -1
  43. package/plugin/lib/commonjs/postInstallHelper.js.map +1 -1
  44. package/plugin/lib/commonjs/types/cio-types.js.map +1 -1
  45. package/plugin/lib/commonjs/utils/android.js +109 -0
  46. package/plugin/lib/commonjs/utils/android.js.map +1 -0
  47. package/plugin/lib/commonjs/utils/config.js +43 -0
  48. package/plugin/lib/commonjs/utils/config.js.map +1 -0
  49. package/plugin/lib/commonjs/utils/plugin.js +49 -0
  50. package/plugin/lib/commonjs/utils/plugin.js.map +1 -0
  51. package/plugin/lib/commonjs/utils/validation.js +44 -0
  52. package/plugin/lib/commonjs/utils/validation.js.map +1 -0
  53. package/plugin/lib/commonjs/utils/xcode.js +67 -0
  54. package/plugin/lib/commonjs/utils/xcode.js.map +1 -0
  55. package/plugin/lib/module/android/withAndroidManifestUpdates.js +55 -14
  56. package/plugin/lib/module/android/withAndroidManifestUpdates.js.map +1 -1
  57. package/plugin/lib/module/android/withCIOAndroid.js +7 -2
  58. package/plugin/lib/module/android/withCIOAndroid.js.map +1 -1
  59. package/plugin/lib/module/android/withGoogleServicesJSON.js +1 -1
  60. package/plugin/lib/module/android/withGoogleServicesJSON.js.map +1 -1
  61. package/plugin/lib/module/android/withMainApplicationModifications.js +38 -0
  62. package/plugin/lib/module/android/withMainApplicationModifications.js.map +1 -0
  63. package/plugin/lib/module/android/withNotificationChannelMetadata.js +1 -1
  64. package/plugin/lib/module/android/withNotificationChannelMetadata.js.map +1 -1
  65. package/plugin/lib/module/android/withProjectStrings.js +13 -6
  66. package/plugin/lib/module/android/withProjectStrings.js.map +1 -1
  67. package/plugin/lib/module/helpers/constants/android.js +6 -0
  68. package/plugin/lib/module/helpers/constants/android.js.map +1 -1
  69. package/plugin/lib/module/helpers/constants/common.js +12 -0
  70. package/plugin/lib/module/helpers/constants/common.js.map +1 -0
  71. package/plugin/lib/module/helpers/constants/ios.js +6 -6
  72. package/plugin/lib/module/helpers/constants/ios.js.map +1 -1
  73. package/plugin/lib/module/helpers/native-files/android/CustomerIOSDKInitializer.kt +64 -0
  74. package/plugin/lib/module/helpers/native-files/ios/CustomerIOSDKInitializer.swift +54 -0
  75. package/plugin/lib/module/helpers/utils/injectCIOPodfileCode.js +2 -2
  76. package/plugin/lib/module/helpers/utils/injectCIOPodfileCode.js.map +1 -1
  77. package/plugin/lib/module/helpers/utils/patchPluginNativeCode.js +57 -0
  78. package/plugin/lib/module/helpers/utils/patchPluginNativeCode.js.map +1 -0
  79. package/plugin/lib/module/index.js +14 -2
  80. package/plugin/lib/module/index.js.map +1 -1
  81. package/plugin/lib/module/ios/utils.js +1 -2
  82. package/plugin/lib/module/ios/utils.js.map +1 -1
  83. package/plugin/lib/module/ios/withAppDelegateModifications.js +3 -3
  84. package/plugin/lib/module/ios/withAppDelegateModifications.js.map +1 -1
  85. package/plugin/lib/module/ios/withCIOIos.js +17 -11
  86. package/plugin/lib/module/ios/withCIOIos.js.map +1 -1
  87. package/plugin/lib/module/ios/withCIOIosSwift.js +134 -50
  88. package/plugin/lib/module/ios/withCIOIosSwift.js.map +1 -1
  89. package/plugin/lib/module/ios/withGoogleServicesJsonFile.js +2 -2
  90. package/plugin/lib/module/ios/withGoogleServicesJsonFile.js.map +1 -1
  91. package/plugin/lib/module/ios/withNotificationsXcodeProject.js +18 -20
  92. package/plugin/lib/module/ios/withNotificationsXcodeProject.js.map +1 -1
  93. package/plugin/lib/module/ios/withXcodeProject.js +1 -1
  94. package/plugin/lib/module/ios/withXcodeProject.js.map +1 -1
  95. package/plugin/lib/module/postInstallHelper.js.map +1 -1
  96. package/plugin/lib/module/types/cio-types.js.map +1 -1
  97. package/plugin/lib/module/utils/android.js +100 -0
  98. package/plugin/lib/module/utils/android.js.map +1 -0
  99. package/plugin/lib/module/utils/config.js +38 -0
  100. package/plugin/lib/module/utils/config.js.map +1 -0
  101. package/plugin/lib/module/utils/plugin.js +38 -0
  102. package/plugin/lib/module/utils/plugin.js.map +1 -0
  103. package/plugin/lib/module/utils/validation.js +39 -0
  104. package/plugin/lib/module/utils/validation.js.map +1 -0
  105. package/plugin/lib/module/utils/xcode.js +60 -0
  106. package/plugin/lib/module/utils/xcode.js.map +1 -0
  107. package/plugin/lib/typescript/android/withAndroidManifestUpdates.d.ts +1 -0
  108. package/plugin/lib/typescript/android/withCIOAndroid.d.ts +2 -2
  109. package/plugin/lib/typescript/android/withMainApplicationModifications.d.ts +3 -0
  110. package/plugin/lib/typescript/android/withProjectStrings.d.ts +2 -1
  111. package/plugin/lib/typescript/helpers/constants/android.d.ts +3 -0
  112. package/plugin/lib/typescript/helpers/constants/common.d.ts +11 -0
  113. package/plugin/lib/typescript/helpers/constants/ios.d.ts +3 -1
  114. package/plugin/lib/typescript/helpers/utils/patchPluginNativeCode.d.ts +7 -0
  115. package/plugin/lib/typescript/ios/utils.d.ts +2 -2
  116. package/plugin/lib/typescript/ios/withCIOIos.d.ts +2 -2
  117. package/plugin/lib/typescript/ios/withCIOIosSwift.d.ts +3 -3
  118. package/plugin/lib/typescript/types/cio-types.d.ts +46 -6
  119. package/plugin/lib/typescript/utils/android.d.ts +5 -0
  120. package/plugin/lib/typescript/utils/config.d.ts +8 -0
  121. package/plugin/lib/typescript/utils/plugin.d.ts +4 -0
  122. package/plugin/lib/typescript/utils/validation.d.ts +3 -0
  123. package/plugin/lib/typescript/utils/xcode.d.ts +28 -0
  124. package/plugin/src/android/withAndroidManifestUpdates.ts +70 -21
  125. package/plugin/src/android/withCIOAndroid.ts +8 -2
  126. package/plugin/src/android/withGoogleServicesJSON.ts +2 -2
  127. package/plugin/src/android/withMainApplicationModifications.ts +50 -0
  128. package/plugin/src/android/withNotificationChannelMetadata.ts +7 -3
  129. package/plugin/src/android/withProjectStrings.ts +20 -10
  130. package/plugin/src/helpers/constants/android.ts +7 -0
  131. package/plugin/src/helpers/constants/common.ts +12 -0
  132. package/plugin/src/helpers/constants/ios.ts +11 -13
  133. package/plugin/src/helpers/native-files/android/CustomerIOSDKInitializer.kt +64 -0
  134. package/plugin/src/helpers/native-files/ios/CustomerIOSDKInitializer.swift +54 -0
  135. package/plugin/src/helpers/utils/injectCIOPodfileCode.ts +8 -7
  136. package/plugin/src/helpers/utils/patchPluginNativeCode.ts +97 -0
  137. package/plugin/src/index.ts +18 -2
  138. package/plugin/src/ios/utils.ts +5 -5
  139. package/plugin/src/ios/withAppDelegateModifications.ts +11 -8
  140. package/plugin/src/ios/withCIOIos.ts +19 -11
  141. package/plugin/src/ios/withCIOIosSwift.ts +195 -73
  142. package/plugin/src/ios/withGoogleServicesJsonFile.ts +7 -10
  143. package/plugin/src/ios/withNotificationsXcodeProject.ts +25 -26
  144. package/plugin/src/ios/withXcodeProject.ts +1 -1
  145. package/plugin/src/postInstallHelper.js +1 -1
  146. package/plugin/src/types/cio-types.ts +48 -8
  147. package/plugin/src/utils/android.ts +112 -0
  148. package/plugin/src/utils/config.ts +53 -0
  149. package/plugin/src/utils/plugin.ts +46 -0
  150. package/plugin/src/utils/validation.ts +54 -0
  151. package/plugin/src/utils/xcode.ts +74 -0
  152. package/plugin/lib/commonjs/helpers/constants/globals.d.js +0 -2
  153. package/plugin/lib/commonjs/helpers/constants/globals.d.js.map +0 -1
  154. package/plugin/lib/commonjs/helpers/utils/pluginUtils.js +0 -26
  155. package/plugin/lib/commonjs/helpers/utils/pluginUtils.js.map +0 -1
  156. package/plugin/lib/module/helpers/constants/globals.d.js +0 -2
  157. package/plugin/lib/module/helpers/constants/globals.d.js.map +0 -1
  158. package/plugin/lib/module/helpers/utils/pluginUtils.js +0 -19
  159. package/plugin/lib/module/helpers/utils/pluginUtils.js.map +0 -1
  160. package/plugin/lib/typescript/helpers/utils/pluginUtils.d.ts +0 -4
  161. package/plugin/src/helpers/constants/globals.d.ts +0 -8
  162. package/plugin/src/helpers/utils/pluginUtils.ts +0 -22
@@ -1,18 +1,25 @@
1
- import type { ConfigPlugin } from '@expo/config-plugins';
2
- import {
3
- withAppDelegate,
4
- withXcodeProject,
1
+ import type {
2
+ ExportedConfigWithProps,
3
+ XcodeProject,
5
4
  } from '@expo/config-plugins';
5
+ import { withAppDelegate, withXcodeProject } from '@expo/config-plugins';
6
+ import type { ExpoConfig } from '@expo/config-types';
6
7
  import path from 'path';
7
- import type { CustomerIOPluginOptionsIOS } from '../types/cio-types';
8
- import { FileManagement } from '../helpers/utils/fileManagement';
8
+ import { PLATFORM } from '../helpers/constants/common';
9
9
  import {
10
- LOCAL_PATH_TO_CIO_NSE_FILES,
10
+ CIO_CONFIGUREDEEPLINK_KILLEDSTATE_SWIFT_SNIPPET,
11
+ CIO_MESSAGING_PUSH_APP_DELEGATE_INIT_REGEX,
12
+ CIO_NATIVE_SDK_INITIALIZE_CALL,
13
+ CIO_NATIVE_SDK_INITIALIZE_SNIPPET,
11
14
  CIO_REGISTER_PUSHNOTIFICATION_SNIPPET_v2,
12
15
  CIO_REGISTER_PUSH_NOTIFICATION_PLACEHOLDER,
13
- CIO_CONFIGUREDEEPLINK_KILLEDSTATE_SWIFT_SNIPPET,
14
16
  } from '../helpers/constants/ios';
15
17
  import { replaceCodeByRegex } from '../helpers/utils/codeInjection';
18
+ import { FileManagement } from '../helpers/utils/fileManagement';
19
+ import { patchNativeSDKInitializer } from '../helpers/utils/patchPluginNativeCode';
20
+ import type { CustomerIOPluginOptionsIOS, NativeSDKConfig } from '../types/cio-types';
21
+ import { getIosNativeFilesPath } from '../utils/plugin';
22
+ import { copyFileToXcode, getOrCreateCustomerIOGroup } from '../utils/xcode';
16
23
  import { isFcmPushProvider } from './utils';
17
24
 
18
25
  // Constants
@@ -23,29 +30,73 @@ const CIO_SDK_APP_DELEGATE_HANDLER_FILENAME = `${CIO_SDK_APP_DELEGATE_HANDLER_CL
23
30
  * Copy and configure the CioSdkAppDelegateHandler.swift file
24
31
  */
25
32
  const copyAndConfigureAppDelegateHandler = (
26
- config: any,
27
- props: CustomerIOPluginOptionsIOS
28
- ): any => {
33
+ config: ExportedConfigWithProps<XcodeProject>,
34
+ sdkConfig: NativeSDKConfig | undefined,
35
+ props: CustomerIOPluginOptionsIOS,
36
+ ): ExportedConfigWithProps<XcodeProject> => {
37
+ // Destination path in the iOS project
38
+ const projectName = config.modRequest.projectName || '';
39
+ if (!projectName) {
40
+ console.warn(
41
+ 'Project name is undefined, cannot copy CustomerIO files'
42
+ );
43
+ return config;
44
+ }
45
+
46
+ // Add files to the Xcode project
47
+ const xcodeProject = config.modResults;
29
48
  const projectRoot = config.modRequest.projectRoot;
30
49
  const iosProjectRoot = path.join(projectRoot, 'ios');
50
+
51
+ const group = getOrCreateCustomerIOGroup(xcodeProject, projectName);
52
+ if (props.pushNotification) {
53
+ // Copy CioSdkAppDelegateHandler.swift for full push notification + auto-init support
54
+ copyAndConfigurePushAppDelegateHandler({
55
+ xcodeProject,
56
+ group,
57
+ iosProjectRoot,
58
+ projectName,
59
+ sdkConfig,
60
+ props,
61
+ });
62
+ } else if (sdkConfig) {
63
+ // Copy only CustomerIOSDKInitializer.swift for auto-init without push notifications
64
+ copyAndConfigureNativeSDKInitializer({
65
+ xcodeProject,
66
+ group,
67
+ iosProjectRoot,
68
+ projectName,
69
+ sdkConfig,
70
+ });
71
+ }
72
+
73
+ return config;
74
+ };
75
+
76
+ const copyAndConfigurePushAppDelegateHandler = ({
77
+ xcodeProject,
78
+ group,
79
+ iosProjectRoot,
80
+ projectName,
81
+ sdkConfig,
82
+ props,
83
+ }: {
84
+ xcodeProject: XcodeProject;
85
+ group: XcodeProject['pbxCreateGroup'];
86
+ iosProjectRoot: string;
87
+ projectName: string;
88
+ sdkConfig: NativeSDKConfig | undefined;
89
+ props: CustomerIOPluginOptionsIOS;
90
+ }) => {
31
91
  const useFcm = isFcmPushProvider(props);
32
92
 
33
93
  // Source path for the handler file
34
94
  const handlerSourcePath = path.join(
35
- LOCAL_PATH_TO_CIO_NSE_FILES,
95
+ getIosNativeFilesPath(),
36
96
  useFcm ? 'fcm' : 'apn',
37
97
  CIO_SDK_APP_DELEGATE_HANDLER_FILENAME
38
98
  );
39
99
 
40
- // Destination path in the iOS project
41
- const projectName = config.modRequest.projectName || '';
42
- if (!projectName) {
43
- console.warn(
44
- 'Project name is undefined, cannot copy CioSdkAppDelegateHandler.swift'
45
- );
46
- return config;
47
- }
48
-
49
100
  const handlerDestPath = path.join(
50
101
  iosProjectRoot,
51
102
  projectName,
@@ -54,20 +105,6 @@ const copyAndConfigureAppDelegateHandler = (
54
105
 
55
106
  FileManagement.copyFile(handlerSourcePath, handlerDestPath);
56
107
 
57
- // Add the file to the Xcode project
58
- const xcodeProject = config.modResults;
59
-
60
- // Create a group for CustomerIO files if it doesn't exist
61
- let group;
62
- const existingGroup = xcodeProject.pbxGroupByName('CustomerIO');
63
- if (existingGroup) {
64
- group = existingGroup;
65
- } else {
66
- group = xcodeProject.pbxCreateGroup('CustomerIO');
67
- const classesKey = xcodeProject.findPBXGroupKey({ name: projectName });
68
- xcodeProject.addToPbxGroup(group, classesKey);
69
- }
70
-
71
108
  // Add the file to the Xcode project
72
109
  xcodeProject.addSourceFile(
73
110
  `${projectName}/${CIO_SDK_APP_DELEGATE_HANDLER_FILENAME}`,
@@ -115,32 +152,79 @@ const copyAndConfigureAppDelegateHandler = (
115
152
  showPushAppInForeground.toString()
116
153
  );
117
154
 
155
+ // Add auto initialization if sdkConfig is provided
156
+ if (sdkConfig) {
157
+ // Also copy CustomerIOSDKInitializer.swift for auto-initialization
158
+ copyAndConfigureNativeSDKInitializer({ xcodeProject, group, iosProjectRoot, projectName, sdkConfig });
159
+
160
+ // Inject auto initialization call before MessagingPush initialization
161
+ handlerFileContent = handlerFileContent.replace(CIO_MESSAGING_PUSH_APP_DELEGATE_INIT_REGEX, CIO_NATIVE_SDK_INITIALIZE_SNIPPET + '$1');
162
+ }
163
+
118
164
  FileManagement.writeFile(handlerDestPath, handlerFileContent);
165
+ };
119
166
 
120
- return config;
167
+ const copyAndConfigureNativeSDKInitializer = ({
168
+ xcodeProject,
169
+ group,
170
+ iosProjectRoot,
171
+ projectName,
172
+ sdkConfig,
173
+ }: {
174
+ xcodeProject: XcodeProject;
175
+ group: XcodeProject['pbxCreateGroup'];
176
+ iosProjectRoot: string;
177
+ projectName: string;
178
+ sdkConfig: NativeSDKConfig;
179
+ }) => {
180
+ const filename = 'CustomerIOSDKInitializer.swift';
181
+ const sourcePath = path.join(getIosNativeFilesPath(), filename);
182
+ // Add the CustomerIOSDKInitializer.swift file to the same Xcode group as CioSdkAppDelegateHandler
183
+ copyFileToXcode({
184
+ xcodeProject,
185
+ iosProjectRoot,
186
+ projectName,
187
+ sourceFilePath: sourcePath,
188
+ targetFileName: filename,
189
+ transform: (content) => patchNativeSDKInitializer(content, PLATFORM.IOS, sdkConfig),
190
+ customerIOGroup: group,
191
+ });
121
192
  };
122
193
 
123
- export const withCIOIosSwift: ConfigPlugin<CustomerIOPluginOptionsIOS> = (
124
- configOuter,
125
- props
194
+ export const withCIOIosSwift = (
195
+ configOuter: ExpoConfig,
196
+ sdkConfig: NativeSDKConfig | undefined,
197
+ props: CustomerIOPluginOptionsIOS,
126
198
  ) => {
127
- // First, copy the CioSdkAppDelegateHandler.swift file to the iOS project and add it to Xcode project
199
+ // First, copy required swift files to iOS folder and add it to Xcode project
128
200
  configOuter = withXcodeProject(configOuter, async (config) => {
129
- return copyAndConfigureAppDelegateHandler(config, props);
201
+ return copyAndConfigureAppDelegateHandler(config, sdkConfig, props);
130
202
  });
131
203
 
132
- // Then modify the AppDelegate
133
- return withAppDelegate(configOuter, async (config) => {
134
- return modifyAppDelegate(config, props);
135
- });
204
+ // Modify the AppDelegate based on configuration
205
+ if (props.pushNotification) {
206
+ // With push notifications: delegate to CioSdkAppDelegateHandler for both push and auto-init
207
+ return withAppDelegate(configOuter, async (config) => {
208
+ return modifyAppDelegateWithPushAppDelegateHandler(config, props);
209
+ });
210
+ } else if (sdkConfig) {
211
+ // Without push notifications: directly inject auto initialization into AppDelegate
212
+ return withAppDelegate(configOuter, async (config) => {
213
+ return modifyAppDelegateWithNativeSDKInitializer(config);
214
+ });
215
+ } else {
216
+ return configOuter;
217
+ }
136
218
  };
137
219
 
138
220
  /**
139
221
  * Modify the AppDelegate to integrate with Customer.io SDK
140
222
  */
141
- const modifyAppDelegate = (
223
+ const modifyAppDelegateWithPushAppDelegateHandler = (
224
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
142
225
  config: any,
143
226
  props: CustomerIOPluginOptionsIOS
227
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
144
228
  ): any => {
145
229
  const appDelegateContent = config.modResults.contents;
146
230
 
@@ -156,7 +240,10 @@ const modifyAppDelegate = (
156
240
  let modifiedContent = addHandlerPropertyDeclaration(appDelegateContent);
157
241
 
158
242
  // Modify didFinishLaunchingWithOptions to initialize and call the handler
159
- modifiedContent = modifyDidFinishLaunchingWithOptions(modifiedContent);
243
+ modifiedContent = modifyDidFinishLaunchingWithOptions(
244
+ modifiedContent,
245
+ ` cioSdkHandler.application(application, didFinishLaunchingWithOptions: launchOptions)\n\n `
246
+ );
160
247
 
161
248
  // Add didRegisterForRemoteNotificationsWithDeviceToken implementation
162
249
  modifiedContent =
@@ -165,7 +252,7 @@ const modifyAppDelegate = (
165
252
  // Add didFailToRegisterForRemoteNotificationsWithError implementation
166
253
  modifiedContent =
167
254
  addDidFailToRegisterForRemoteNotificationsWithError(modifiedContent);
168
-
255
+
169
256
  // Add deep link handling for killed state if enabled
170
257
  if (props.pushNotification?.handleDeeplinkInKilledState === true) {
171
258
  modifiedContent = addHandleDeeplinkInKilledState(modifiedContent);
@@ -175,6 +262,34 @@ const modifyAppDelegate = (
175
262
  return config;
176
263
  };
177
264
 
265
+ /**
266
+ * Modify the AppDelegate to integrate with Customer.io SDK
267
+ */
268
+ const modifyAppDelegateWithNativeSDKInitializer = (
269
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
270
+ config: any,
271
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
272
+ ): any => {
273
+ const appDelegateContent = config.modResults.contents;
274
+
275
+ // Check if modifications have already been applied
276
+ if (appDelegateContent.includes(CIO_NATIVE_SDK_INITIALIZE_CALL)) {
277
+ console.log(
278
+ 'CustomerIO Swift AppDelegate changes already exist. Skipping...'
279
+ );
280
+ return config;
281
+ }
282
+
283
+ // Modify didFinishLaunchingWithOptions to initialize and call the handler
284
+ const modifiedContent = modifyDidFinishLaunchingWithOptions(
285
+ appDelegateContent,
286
+ CIO_NATIVE_SDK_INITIALIZE_SNIPPET,
287
+ );
288
+
289
+ config.modResults.contents = modifiedContent;
290
+ return config;
291
+ };
292
+
178
293
  /**
179
294
  * Check if a method exists in the AppDelegate content
180
295
  * @param content The AppDelegate content
@@ -203,7 +318,7 @@ const addHandlerPropertyDeclaration = (content: string): string => {
203
318
  return content;
204
319
  }
205
320
 
206
- const position = match.index! + match[0].length;
321
+ const position = (match.index ?? 0) + match[0].length;
207
322
  return (
208
323
  content.substring(0, position) +
209
324
  `\n let cioSdkHandler = ${CIO_SDK_APP_DELEGATE_HANDLER_CLASS}()\n` +
@@ -212,14 +327,15 @@ const addHandlerPropertyDeclaration = (content: string): string => {
212
327
  };
213
328
 
214
329
  /**
215
- * Modify didFinishLaunchingWithOptions to call the handler
216
- * This adds the handler call before the return statement in didFinishLaunchingWithOptions
330
+ * Modify didFinishLaunchingWithOptions to inject Customer.io code
331
+ * Injects the provided code (either handler call or auto initialization) before the return statement
217
332
  */
218
- const modifyDidFinishLaunchingWithOptions = (content: string): string => {
333
+ const modifyDidFinishLaunchingWithOptions = (content: string, codeToInject: string): string => {
219
334
  // Find the return statement in didFinishLaunchingWithOptions
220
335
  // Always look for launchOptions since modifiedLaunchOptions is only set later
221
- const returnStatementRegex = /return\s+super\.application\s*\(\s*application\s*,\s*didFinishLaunchingWithOptions\s*:\s*launchOptions\s*\)/;
222
-
336
+ const returnStatementRegex =
337
+ /return\s+super\.application\s*\(\s*application\s*,\s*didFinishLaunchingWithOptions\s*:\s*launchOptions\s*\)/;
338
+
223
339
  const returnStatementMatch = content.match(returnStatementRegex);
224
340
 
225
341
  if (!returnStatementMatch) {
@@ -229,13 +345,12 @@ const modifyDidFinishLaunchingWithOptions = (content: string): string => {
229
345
  return content;
230
346
  }
231
347
 
232
- // Add handler call before the return statement
233
- const insertPosition = returnStatementMatch.index!;
234
- const handlerCallCode = ` cioSdkHandler.application(application, didFinishLaunchingWithOptions: launchOptions)\n\n `;
235
-
348
+ // Inject Customer.io code before the return statement
349
+ const insertPosition = returnStatementMatch.index ?? 0;
350
+
236
351
  return (
237
352
  content.substring(0, insertPosition) +
238
- handlerCallCode +
353
+ codeToInject +
239
354
  content.substring(insertPosition)
240
355
  );
241
356
  };
@@ -248,8 +363,9 @@ const modifyDidFinishLaunchingWithOptions = (content: string): string => {
248
363
  const addDidRegisterForRemoteNotificationsWithDeviceToken = (
249
364
  content: string
250
365
  ): string => {
251
- const methodSignature = 'func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken:';
252
-
366
+ const methodSignature =
367
+ 'func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken:';
368
+
253
369
  // Check if method already exists
254
370
  if (methodExistsInAppDelegate(content, methodSignature)) {
255
371
  // Method exists, modify it to call our handler
@@ -283,7 +399,7 @@ const addDidRegisterForRemoteNotificationsWithDeviceToken = (
283
399
  }
284
400
 
285
401
  // Insert the method inside the class
286
- const position = classEndMatch.index!;
402
+ const position = classEndMatch.index ?? 0;
287
403
  return (
288
404
  content.substring(0, position) +
289
405
  '\n // Handle device token registration\n' +
@@ -305,8 +421,9 @@ const addDidRegisterForRemoteNotificationsWithDeviceToken = (
305
421
  const addDidFailToRegisterForRemoteNotificationsWithError = (
306
422
  content: string
307
423
  ): string => {
308
- const methodSignature = 'func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error:';
309
-
424
+ const methodSignature =
425
+ 'func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error:';
426
+
310
427
  // Check if method already exists
311
428
  if (methodExistsInAppDelegate(content, methodSignature)) {
312
429
  // Method exists, modify it to call our handler
@@ -340,7 +457,7 @@ const addDidFailToRegisterForRemoteNotificationsWithError = (
340
457
  }
341
458
 
342
459
  // Insert the method inside the class
343
- const position = classEndMatch.index!;
460
+ const position = classEndMatch.index ?? 0;
344
461
  return (
345
462
  content.substring(0, position) +
346
463
  '\n // Handle remote notification registration errors\n' +
@@ -361,24 +478,29 @@ const addDidFailToRegisterForRemoteNotificationsWithError = (
361
478
  */
362
479
  const addHandleDeeplinkInKilledState = (content: string): string => {
363
480
  // Check if deep link code snippet is already present
364
- const deepLinkMarker = "Deep link workaround for app killed state start";
481
+ const deepLinkMarker = 'Deep link workaround for app killed state start';
365
482
  if (content.includes(deepLinkMarker)) {
366
483
  return content;
367
484
  }
368
485
 
369
486
  // Find the return statement with launchOptions
370
- const returnStatementRegex = /return\s+super\.application\s*\(\s*application\s*,\s*didFinishLaunchingWithOptions\s*:\s*launchOptions\s*\)/;
487
+ const returnStatementRegex =
488
+ /return\s+super\.application\s*\(\s*application\s*,\s*didFinishLaunchingWithOptions\s*:\s*launchOptions\s*\)/;
371
489
  const returnStatementMatch = content.match(returnStatementRegex);
372
-
490
+
373
491
  if (!returnStatementMatch) {
374
- console.warn("Could not find return statement with launchOptions");
492
+ console.warn('Could not find return statement with launchOptions');
375
493
  return content;
376
494
  }
377
-
495
+
378
496
  // Create the replacement code with deep link handling and modified return statement
379
- const modifiedReturnStatement = "return super.application(application, didFinishLaunchingWithOptions: modifiedLaunchOptions)";
380
- const replacementCode = CIO_CONFIGUREDEEPLINK_KILLEDSTATE_SWIFT_SNIPPET + "\n\n " + modifiedReturnStatement;
381
-
497
+ const modifiedReturnStatement =
498
+ 'return super.application(application, didFinishLaunchingWithOptions: modifiedLaunchOptions)';
499
+ const replacementCode =
500
+ CIO_CONFIGUREDEEPLINK_KILLEDSTATE_SWIFT_SNIPPET +
501
+ '\n\n ' +
502
+ modifiedReturnStatement;
503
+
382
504
  // Replace the return statement with deep link handling code and modified return statement
383
505
  return content.replace(returnStatementRegex, replacementCode);
384
506
  };
@@ -1,11 +1,8 @@
1
- import {
2
- withXcodeProject,
3
- IOSConfig,
4
- } from '@expo/config-plugins';
5
- import type { ConfigPlugin } from '@expo/config-plugins';
1
+ import type { ConfigPlugin, XcodeProject } from '@expo/config-plugins';
2
+ import { IOSConfig, withXcodeProject } from '@expo/config-plugins';
6
3
 
7
- import { FileManagement } from './../helpers/utils/fileManagement';
8
4
  import type { CustomerIOPluginOptionsIOS } from '../types/cio-types';
5
+ import { FileManagement } from './../helpers/utils/fileManagement';
9
6
  import { isFcmPushProvider } from './utils';
10
7
 
11
8
  export const withGoogleServicesJsonFile: ConfigPlugin<
@@ -20,7 +17,7 @@ export const withGoogleServicesJsonFile: ConfigPlugin<
20
17
 
21
18
  console.log(
22
19
  'Only specify Customer.io ios.pushNotification.googleServicesFile config if you are not already including' +
23
- ' GoogleService-Info.plist as part of Firebase integration'
20
+ ' GoogleService-Info.plist as part of Firebase integration'
24
21
  );
25
22
 
26
23
  // googleServicesFile
@@ -50,7 +47,7 @@ export const withGoogleServicesJsonFile: ConfigPlugin<
50
47
  if (config.ios?.googleServicesFile) {
51
48
  console.warn(
52
49
  'Specifying both Expo ios.googleServicesFile and Customer.io ios.pushNotification.googleServicesFile can cause a conflict' +
53
- ' duplicating GoogleService-Info.plist in the iOS project resources. Please remove Customer.io ios.pushNotification.googleServicesFile'
50
+ ' duplicating GoogleService-Info.plist in the iOS project resources. Please remove Customer.io ios.pushNotification.googleServicesFile'
54
51
  );
55
52
  }
56
53
 
@@ -61,7 +58,7 @@ export const withGoogleServicesJsonFile: ConfigPlugin<
61
58
  );
62
59
 
63
60
  addFileToXcodeProject(props.modResults, 'GoogleService-Info.plist');
64
- } catch (e) {
61
+ } catch {
65
62
  console.error(
66
63
  `There was an error copying your GoogleService-Info.plist file. You can copy it manually into ${iosPath}/GoogleService-Info.plist`
67
64
  );
@@ -76,7 +73,7 @@ export const withGoogleServicesJsonFile: ConfigPlugin<
76
73
  });
77
74
  };
78
75
 
79
- function addFileToXcodeProject(project: any, fileName: string) {
76
+ function addFileToXcodeProject(project: XcodeProject, fileName: string) {
80
77
  const groupName = 'Resources';
81
78
  const filepath = fileName;
82
79
 
@@ -5,11 +5,11 @@ import {
5
5
  CIO_NOTIFICATION_TARGET_NAME,
6
6
  CIO_REGISTER_PUSHNOTIFICATION_SNIPPET,
7
7
  DEFAULT_BUNDLE_VERSION,
8
- LOCAL_PATH_TO_CIO_NSE_FILES,
9
8
  } from '../helpers/constants/ios';
10
9
  import { replaceCodeByRegex } from '../helpers/utils/codeInjection';
11
10
  import { injectCIONotificationPodfileCode } from '../helpers/utils/injectCIOPodfileCode';
12
- import type { CustomerIOPluginOptionsIOS } from '../types/cio-types';
11
+ import type { CustomerIOPluginOptionsIOS, RichPushConfig } from '../types/cio-types';
12
+ import { getIosNativeFilesPath } from '../utils/plugin';
13
13
  import { FileManagement } from './../helpers/utils/fileManagement';
14
14
  import { isExpoVersion53OrHigher, isFcmPushProvider } from './utils';
15
15
 
@@ -21,11 +21,11 @@ const TARGETED_DEVICE_FAMILY = `"1,2"`;
21
21
  const addNotificationServiceExtension = async (
22
22
  options: CustomerIOPluginOptionsIOS,
23
23
  xcodeProject: XcodeProject,
24
- isExpoVersion53OrHigher: boolean
24
+ isExpo53OrHigher: boolean,
25
25
  ) => {
26
26
  try {
27
27
  // PushService file is only needed for pre-Expo 53 code generation
28
- if (options.pushNotification && !isExpoVersion53OrHigher) {
28
+ if (options.pushNotification && !isExpo53OrHigher) {
29
29
  await addPushNotificationFile(options, xcodeProject);
30
30
  }
31
31
 
@@ -33,7 +33,7 @@ const addNotificationServiceExtension = async (
33
33
  await addRichPushXcodeProj(options, xcodeProject);
34
34
  }
35
35
  return xcodeProject;
36
- } catch (error: any) {
36
+ } catch (error: unknown) {
37
37
  console.error(error);
38
38
  return null;
39
39
  }
@@ -88,7 +88,7 @@ export const withCioNotificationsXcodeProject: ConfigPlugin<
88
88
  const modifiedProjectFile = await addNotificationServiceExtension(
89
89
  options,
90
90
  config.modResults,
91
- isExpoVersion53OrHigher(configOuter)
91
+ isExpoVersion53OrHigher(configOuter),
92
92
  );
93
93
 
94
94
  if (modifiedProjectFile) {
@@ -101,7 +101,7 @@ export const withCioNotificationsXcodeProject: ConfigPlugin<
101
101
 
102
102
  const addRichPushXcodeProj = async (
103
103
  options: CustomerIOPluginOptionsIOS,
104
- xcodeProject: any
104
+ xcodeProject: XcodeProject,
105
105
  ) => {
106
106
  const {
107
107
  appleTeamId,
@@ -145,8 +145,7 @@ const addRichPushXcodeProj = async (
145
145
  platformSpecificFiles.forEach((filename) => {
146
146
  const targetFile = getTargetFile(filename);
147
147
  FileManagement.copyFile(
148
- `${LOCAL_PATH_TO_CIO_NSE_FILES}/${
149
- isFcmProvider ? 'fcm' : 'apn'
148
+ `${getIosNativeFilesPath()}/${isFcmProvider ? 'fcm' : 'apn'
150
149
  }/${filename}`,
151
150
  targetFile
152
151
  );
@@ -156,7 +155,7 @@ const addRichPushXcodeProj = async (
156
155
  commonFiles.forEach((filename) => {
157
156
  const targetFile = getTargetFile(filename);
158
157
  FileManagement.copyFile(
159
- `${LOCAL_PATH_TO_CIO_NSE_FILES}/common/${filename}`,
158
+ `${getIosNativeFilesPath()}/common/${filename}`,
160
159
  targetFile
161
160
  );
162
161
  });
@@ -168,7 +167,7 @@ const addRichPushXcodeProj = async (
168
167
  bundleShortVersion,
169
168
  infoPlistTargetFile,
170
169
  });
171
- updateNseEnv(options, getTargetFile(ENV_FILENAME));
170
+ updateNseEnv(getTargetFile(ENV_FILENAME), options.pushNotification?.env);
172
171
 
173
172
  // Create new PBXGroup for the extension
174
173
  const extGroup = xcodeProject.addPbxGroup(
@@ -179,7 +178,7 @@ const addRichPushXcodeProj = async (
179
178
 
180
179
  // Add the new PBXGroup to the top level group. This makes the
181
180
  // files / folder appear in the file explorer in Xcode.
182
- const groups = xcodeProject.hash.project.objects['PBXGroup'];
181
+ const groups = xcodeProject.hash.project.objects.PBXGroup;
183
182
  Object.keys(groups).forEach((key) => {
184
183
  if (groups[key].name === undefined && groups[key].path === undefined) {
185
184
  xcodeProject.addToPbxGroup(extGroup.uuid, key);
@@ -191,9 +190,8 @@ const addRichPushXcodeProj = async (
191
190
  // An upstream fix should be made to the code referenced in this link:
192
191
  // - https://github.com/apache/cordova-node-xcode/blob/8b98cabc5978359db88dc9ff2d4c015cba40f150/lib/pbxProject.js#L860
193
192
  const projObjects = xcodeProject.hash.project.objects;
194
- projObjects['PBXTargetDependency'] = projObjects['PBXTargetDependency'] || {};
195
- projObjects['PBXContainerItemProxy'] =
196
- projObjects['PBXTargetDependency'] || {};
193
+ projObjects.PBXTargetDependency = projObjects.PBXTargetDependency || {};
194
+ projObjects.PBXContainerItemProxy = projObjects.PBXTargetDependency || {};
197
195
 
198
196
  if (xcodeProject.pbxTargetByName(CIO_NOTIFICATION_TARGET_NAME)) {
199
197
  console.warn(
@@ -238,7 +236,7 @@ const addRichPushXcodeProj = async (
238
236
  if (
239
237
  typeof configurations[key].buildSettings !== 'undefined' &&
240
238
  configurations[key].buildSettings.PRODUCT_NAME ===
241
- `"${CIO_NOTIFICATION_TARGET_NAME}"`
239
+ `"${CIO_NOTIFICATION_TARGET_NAME}"`
242
240
  ) {
243
241
  const buildSettingsObj = configurations[key].buildSettings;
244
242
  buildSettingsObj.DEVELOPMENT_TEAM = appleTeamId;
@@ -285,21 +283,21 @@ const updateNseInfoPlist = (payload: {
285
283
  };
286
284
 
287
285
  const updateNseEnv = (
288
- options: CustomerIOPluginOptionsIOS,
289
- envFileName: string
286
+ envFileName: string,
287
+ richPushConfig?: RichPushConfig
290
288
  ) => {
291
289
  const CDP_API_KEY_RE = /\{\{CDP_API_KEY\}\}/;
292
290
  const REGION_RE = /\{\{REGION\}\}/;
293
291
 
294
292
  let envFileContent = FileManagement.readFile(envFileName);
295
- const { cdpApiKey, region } = options.pushNotification?.env || {
296
- cdpApiKey: undefined,
297
- region: undefined,
298
- };
293
+
294
+ // Use merged config values (config takes precedence over env)
295
+ const cdpApiKey = richPushConfig?.cdpApiKey;
296
+ const region = richPushConfig?.region;
299
297
 
300
298
  if (!cdpApiKey) {
301
299
  throw new Error(
302
- 'Adding NotificationServiceExtension failed: ios.pushNotification.env.cdpApiKey is missing from app.config.js or app.json.'
300
+ 'NotificationServiceExtension failed: cdpApiKey missing. Provide in config.cdpApiKey or ios.pushNotification.env.cdpApiKey.'
303
301
  );
304
302
  }
305
303
  envFileContent = replaceCodeByRegex(
@@ -313,7 +311,8 @@ const updateNseEnv = (
313
311
  us: 'Region.US',
314
312
  eu: 'Region.EU',
315
313
  };
316
- const mappedRegion = (regionMap as any)[region.toLowerCase()] || '';
314
+ const mappedRegion =
315
+ regionMap[region.toLowerCase() as keyof typeof regionMap] || '';
317
316
  if (!mappedRegion) {
318
317
  console.warn(
319
318
  `${region} is an invalid region. Please use the values from the docs: https://customer.io/docs/sdk/expo/getting-started/#configure-the-plugin`
@@ -332,7 +331,7 @@ const updateNseEnv = (
332
331
 
333
332
  async function addPushNotificationFile(
334
333
  options: CustomerIOPluginOptionsIOS,
335
- xcodeProject: any
334
+ xcodeProject: XcodeProject
336
335
  ) {
337
336
  // Maybe copy a different file with FCM config based on config
338
337
  const { iosPath, appName } = options;
@@ -352,7 +351,7 @@ async function addPushNotificationFile(
352
351
  });
353
352
 
354
353
  FileManagement.copyFile(
355
- `${LOCAL_PATH_TO_CIO_NSE_FILES}/${sourceFile}`,
354
+ `${getIosNativeFilesPath()}/${sourceFile}`,
356
355
  targetFile
357
356
  );
358
357
  } else {
@@ -1,9 +1,9 @@
1
1
  import type { ConfigPlugin } from '@expo/config-plugins';
2
2
  import { withXcodeProject } from '@expo/config-plugins';
3
3
 
4
- import { isFcmPushProvider } from './utils';
5
4
  import { injectCIOPodfileCode } from '../helpers/utils/injectCIOPodfileCode';
6
5
  import type { CustomerIOPluginOptionsIOS } from '../types/cio-types';
6
+ import { isFcmPushProvider } from './utils';
7
7
 
8
8
  export const withCioXcodeProject: ConfigPlugin<CustomerIOPluginOptionsIOS> = (
9
9
  config,
@@ -29,4 +29,4 @@ function runPostInstall() {
29
29
  }
30
30
  }
31
31
 
32
- exports.runPostInstall = runPostInstall;
32
+ exports.runPostInstall = runPostInstall;