customerio-expo-plugin 1.0.0-beta.11 → 1.0.0-beta.13

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 (31) hide show
  1. package/lib/commonjs/helpers/constants/ios.js +50 -14
  2. package/lib/commonjs/helpers/constants/ios.js.map +1 -1
  3. package/lib/commonjs/helpers/utils/codeInjection.js +8 -0
  4. package/lib/commonjs/helpers/utils/codeInjection.js.map +1 -1
  5. package/lib/commonjs/helpers/utils/injectCIOPodfileCode.js +3 -2
  6. package/lib/commonjs/helpers/utils/injectCIOPodfileCode.js.map +1 -1
  7. package/lib/commonjs/ios/withAppDelegateModifications.js +54 -2
  8. package/lib/commonjs/ios/withAppDelegateModifications.js.map +1 -1
  9. package/lib/commonjs/ios/withNotificationsXcodeProject.js +12 -12
  10. package/lib/commonjs/ios/withNotificationsXcodeProject.js.map +1 -1
  11. package/lib/commonjs/types/cio-types.js.map +1 -1
  12. package/lib/module/helpers/constants/ios.js +37 -11
  13. package/lib/module/helpers/constants/ios.js.map +1 -1
  14. package/lib/module/helpers/utils/codeInjection.js +6 -0
  15. package/lib/module/helpers/utils/codeInjection.js.map +1 -1
  16. package/lib/module/helpers/utils/injectCIOPodfileCode.js +3 -2
  17. package/lib/module/helpers/utils/injectCIOPodfileCode.js.map +1 -1
  18. package/lib/module/ios/withAppDelegateModifications.js +56 -4
  19. package/lib/module/ios/withAppDelegateModifications.js.map +1 -1
  20. package/lib/module/ios/withNotificationsXcodeProject.js +12 -12
  21. package/lib/module/ios/withNotificationsXcodeProject.js.map +1 -1
  22. package/lib/module/types/cio-types.js.map +1 -1
  23. package/lib/typescript/helpers/constants/ios.d.ts +13 -3
  24. package/lib/typescript/helpers/utils/codeInjection.d.ts +2 -0
  25. package/lib/typescript/types/cio-types.d.ts +2 -0
  26. package/package.json +1 -1
  27. package/src/helpers/constants/ios.ts +51 -11
  28. package/src/helpers/utils/codeInjection.ts +11 -0
  29. package/src/helpers/utils/injectCIOPodfileCode.ts +3 -2
  30. package/src/ios/withAppDelegateModifications.ts +89 -5
  31. package/src/types/cio-types.ts +2 -0
@@ -12,6 +12,8 @@ export type CustomerIOPluginOptionsIOS = {
12
12
  appleTeamId?: string;
13
13
  appName?: string;
14
14
  disableNotificationRegistration?: boolean;
15
+ handleNotificationClick?: boolean;
16
+ handleDeeplinkInKilledState?: boolean;
15
17
  useFrameworks?: 'static' | 'dynamic';
16
18
  pushNotification?: {
17
19
  useRichPush: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "customerio-expo-plugin",
3
- "version": "1.0.0-beta.11",
3
+ "version": "1.0.0-beta.13",
4
4
  "description": "Expo config plugin for the Customer IO React Native SDK",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",
@@ -6,16 +6,26 @@ let pluginPackageRoot = f.next().filename;
6
6
  // This is the path to the root of the customerio-expo-plugin package
7
7
  pluginPackageRoot = path.dirname(pluginPackageRoot);
8
8
 
9
+ export const LOCAL_PATH_TO_RN_SDK = path.join(
10
+ pluginPackageRoot,
11
+ '../customerio-reactnative'
12
+ )
13
+
9
14
  export const LOCAL_PATH_TO_CIO_NSE_FILES = path.join(
10
15
  pluginPackageRoot,
11
16
  'src/helpers/native-files/ios'
12
17
  );
18
+
19
+ export function getRelativePathToRNSDK(currentFile: string) {
20
+ return path.relative(path.dirname(currentFile), LOCAL_PATH_TO_RN_SDK);
21
+ }
22
+
13
23
  export const IOS_DEPLOYMENT_TARGET = '13.0';
14
24
  export const GROUP_IDENTIFIER_TEMPLATE_REGEX = /{{GROUP_IDENTIFIER}}/gm;
15
25
  export const BUNDLE_SHORT_VERSION_TEMPLATE_REGEX = /{{BUNDLE_SHORT_VERSION}}/gm;
16
26
  export const BUNDLE_VERSION_TEMPLATE_REGEX = /{{BUNDLE_VERSION}}/gm;
17
27
  export const CIO_DIDFINISHLAUNCHINGMETHOD_REGEX =
18
- /(- \(BOOL\)application:\(UIApplication \*\)application didFinishLaunchingWithOptions:\(NSDictionary \*\)launchOptions(\s|\n)*?\{)((.|\n)*)\[super(\s)application:application(\s)didFinishLaunchingWithOptions:launchOptions\];/;
28
+ /.*\[super(\s)application:application(\s)didFinishLaunchingWithOptions:launchOptions\];/;
19
29
 
20
30
  export const CIO_DIDFAILTOREGISTERFORREMOTENOTIFICATIONSWITHERROR_REGEX =
21
31
  /return \[super application:application didFailToRegisterForRemoteNotificationsWithError:error\];/;
@@ -30,20 +40,31 @@ export const CIO_APPDELEGATEDECLARATION_REGEX =
30
40
  /@implementation AppDelegate(.|\n)/;
31
41
 
32
42
  export const CIO_APPDELEGATEHEADER_REGEX =
33
- /@interface AppDelegate : EXAppDelegateWrapper <RCTBridgeDelegate>/;
43
+ /(@interface AppDelegate\s*:\s*EXAppDelegateWrapper\s*)(<([^>]+)>)?/;
44
+
45
+ export const CIO_RCTBRIDGE_DEEPLINK_MODIFIEDOPTIONS_REGEX =
46
+ /^\s*RCTBridge\s*\*\s*\w+\s*=\s*\[\s*self\.reactDelegate\s+createBridgeWithDelegate:self\s+launchOptions:launchOptions\s*\];\s*$/gm;
47
+
48
+ export const CIO_LAUNCHOPTIONS_DEEPLINK_MODIFIEDOPTIONS_REGEX =
49
+ /^\s*return\s\[\s*super\s*application:\s*application\s*didFinishLaunchingWithOptions\s*:\s*launchOptions\s*\];/gm;
50
+
51
+ export const CIO_DEEPLINK_COMMENT_REGEX = /\sDeep link workaround for app killed state start/gm;
34
52
  export const DEFAULT_BUNDLE_VERSION = '1';
35
53
  export const DEFAULT_BUNDLE_SHORT_VERSION = '1.0';
36
54
  export const CIO_TARGET_NAME = 'CustomerIOSDK';
37
55
  export const CIO_NOTIFICATION_TARGET_NAME = 'NotificationService';
38
- export const CIO_APPDELEGATEHEADER_SNIPPET = `
39
- #import <UserNotifications/UserNotifications.h>
40
-
41
- @interface AppDelegate : EXAppDelegateWrapper <RCTBridgeDelegate, UNUserNotificationCenterDelegate>
42
- `;
43
56
 
57
+ export const CIO_APPDELEGATEHEADER_IMPORT_SNIPPET = `#import <UserNotifications/UserNotifications.h>`;
58
+ export const CIO_APPDELEGATEHEADER_USER_NOTIFICATION_CENTER_SNIPPET = 'UNUserNotificationCenterDelegate';
44
59
  export const CIO_PUSHNOTIFICATIONHANDLERDECLARATION_SNIPPET = `
45
60
  CIOAppPushNotificationsHandler* pnHandlerObj = [[CIOAppPushNotificationsHandler alloc] init];
46
61
  `;
62
+ export const CIO_RCTBRIDGE_DEEPLINK_MODIFIEDOPTIONS_SNIPPET = `
63
+ RCTBridge *bridge = [self.reactDelegate createBridgeWithDelegate:self launchOptions:modifiedLaunchOptions];
64
+ `;
65
+
66
+ export const CIO_LAUNCHOPTIONS_MODIFIEDOPTIONS_SNIPPET = `
67
+ return [super application:application didFinishLaunchingWithOptions:modifiedLaunchOptions];`
47
68
 
48
69
  export const CIO_DIDFAILTOREGISTERFORREMOTENOTIFICATIONSWITHERROR_SNIPPET = `
49
70
  [super application:application didFailToRegisterForRemoteNotificationsWithError:error];
@@ -57,7 +78,27 @@ export const CIO_DIDREGISTERFORREMOTENOTIFICATIONSWITHDEVICETOKEN_SNIPPET = `
57
78
 
58
79
  export const CIO_CONFIGURECIOSDKPUSHNOTIFICATION_SNIPPET = `
59
80
  // Register for push notifications
60
- [pnHandlerObj registerPushNotification:self];
81
+ [pnHandlerObj registerPushNotification];
82
+ `;
83
+
84
+ export const CIO_CONFIGURECIOSDKUSERNOTIFICATIONCENTER_SNIPPET = `
85
+ UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
86
+ center.delegate = self;
87
+ `;
88
+
89
+ export const CIO_CONFIGUREDEEPLINK_KILLEDSTATE_SNIPPET = `
90
+ // Deep link workaround for app killed state start
91
+ NSMutableDictionary *modifiedLaunchOptions = [NSMutableDictionary dictionaryWithDictionary:launchOptions];
92
+ if (launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]) {
93
+ NSDictionary *pushContent = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
94
+ if (pushContent[@"CIO"] && pushContent[@"CIO"][@"push"] && pushContent[@"CIO"][@"push"][@"link"]) {
95
+ NSString *initialURL = pushContent[@"CIO"][@"push"][@"link"];
96
+ if (!launchOptions[UIApplicationLaunchOptionsURLKey]) {
97
+ modifiedLaunchOptions[UIApplicationLaunchOptionsURLKey] = [NSURL URLWithString:initialURL];
98
+ }
99
+ }
100
+ }
101
+ //Deep link workaround for app killed state ends
61
102
  `;
62
103
 
63
104
  // Enable push handling - notification response
@@ -73,11 +114,10 @@ export const CIO_WILLPRESENTNOTIFICATIONHANDLER_SNIPPET = `
73
114
  completionHandler( UNNotificationPresentationOptionAlert + UNNotificationPresentationOptionSound);
74
115
  }`;
75
116
  export const CIO_REGISTER_PUSHNOTIFICATION_SNIPPET = `
76
- @objc(registerPushNotification:)
77
- public func registerPushNotification(withNotificationDelegate notificationDelegate: UNUserNotificationCenterDelegate) {
117
+ @objc(registerPushNotification)
118
+ public func registerPushNotification() {
78
119
 
79
120
  let center = UNUserNotificationCenter.current()
80
- center.delegate = notificationDelegate
81
121
  center.requestAuthorization(options: [.sound, .alert, .badge]) { (granted, error) in
82
122
  if error == nil{
83
123
  DispatchQueue.main.async {
@@ -22,6 +22,14 @@ export function injectCodeByMultiLineRegex(
22
22
  return fileContent.replace(lineRegex, `$&\n${snippet}`);
23
23
  }
24
24
 
25
+ export function injectCodeBeforeMultiLineRegex(
26
+ fileContent: string,
27
+ lineRegex: RegExp,
28
+ snippet: string
29
+ ) {
30
+ return fileContent.replace(lineRegex, `${snippet}\n$&`);
31
+ }
32
+
25
33
  export function replaceCodeByRegex(
26
34
  fileContent: string,
27
35
  lineRegex: RegExp,
@@ -30,6 +38,9 @@ export function replaceCodeByRegex(
30
38
  return fileContent.replace(lineRegex, snippet);
31
39
  }
32
40
 
41
+ export function matchRegexExists(fileContent: string, regex: RegExp) {
42
+ return regex.test(fileContent);
43
+ }
33
44
  export function injectCodeByMultiLineRegexAndReplaceLine(
34
45
  fileContent: string,
35
46
  lineRegex: RegExp,
@@ -1,4 +1,5 @@
1
1
  import type { CustomerIOPluginOptionsIOS } from '../../types/cio-types';
2
+ import { getRelativePathToRNSDK } from '../constants/ios';
2
3
  import { injectCodeByRegex } from './codeInjection';
3
4
  import { FileManagement } from './fileManagement';
4
5
 
@@ -18,7 +19,7 @@ export async function injectCIOPodfileCode(iosPath: string) {
18
19
 
19
20
  const snippetToInjectInPodfile = `
20
21
  ${blockStart}
21
- pod 'customerio-reactnative/apn', :path => '../node_modules/customerio-reactnative'
22
+ pod 'customerio-reactnative/apn', :path => '${getRelativePathToRNSDK(filename)}'
22
23
  ${blockEnd}
23
24
  `.trim();
24
25
 
@@ -52,7 +53,7 @@ export async function injectCIONotificationPodfileCode(
52
53
  ${blockStart}
53
54
  target 'NotificationService' do
54
55
  ${useFrameworks === 'static' ? 'use_frameworks! :linkage => :static' : ''}
55
- pod 'customerio-reactnative-richpush/apn', :path => '../node_modules/customerio-reactnative'
56
+ pod 'customerio-reactnative-richpush/apn', :path => '${getRelativePathToRNSDK(filename)}'
56
57
  end
57
58
  ${blockEnd}
58
59
  `.trim();
@@ -3,23 +3,34 @@ import { getAppDelegateHeaderFilePath } from '@expo/config-plugins/build/ios/Pat
3
3
 
4
4
  import {
5
5
  CIO_APPDELEGATEDECLARATION_REGEX,
6
+ CIO_APPDELEGATEHEADER_IMPORT_SNIPPET,
6
7
  CIO_APPDELEGATEHEADER_REGEX,
7
- CIO_APPDELEGATEHEADER_SNIPPET,
8
+ CIO_APPDELEGATEHEADER_USER_NOTIFICATION_CENTER_SNIPPET,
8
9
  CIO_CONFIGURECIOSDKPUSHNOTIFICATION_SNIPPET,
10
+ CIO_CONFIGURECIOSDKUSERNOTIFICATIONCENTER_SNIPPET,
11
+ CIO_CONFIGUREDEEPLINK_KILLEDSTATE_SNIPPET,
9
12
  CIO_DIDFAILTOREGISTERFORREMOTENOTIFICATIONSWITHERRORFULL_REGEX,
13
+ CIO_RCTBRIDGE_DEEPLINK_MODIFIEDOPTIONS_REGEX,
10
14
  CIO_DIDFAILTOREGISTERFORREMOTENOTIFICATIONSWITHERROR_REGEX,
11
15
  CIO_DIDFAILTOREGISTERFORREMOTENOTIFICATIONSWITHERROR_SNIPPET,
12
16
  CIO_DIDFINISHLAUNCHINGMETHOD_REGEX,
13
17
  CIO_DIDRECEIVENOTIFICATIONRESPONSEHANDLER_SNIPPET,
14
18
  CIO_DIDREGISTERFORREMOTENOTIFICATIONSWITHDEVICETOKEN_REGEX,
15
19
  CIO_DIDREGISTERFORREMOTENOTIFICATIONSWITHDEVICETOKEN_SNIPPET,
20
+ CIO_LAUNCHOPTIONS_DEEPLINK_MODIFIEDOPTIONS_REGEX,
16
21
  CIO_PUSHNOTIFICATIONHANDLERDECLARATION_SNIPPET,
17
22
  CIO_WILLPRESENTNOTIFICATIONHANDLER_SNIPPET,
23
+ CIO_LAUNCHOPTIONS_MODIFIEDOPTIONS_SNIPPET,
24
+ CIO_RCTBRIDGE_DEEPLINK_MODIFIEDOPTIONS_SNIPPET,
25
+ CIO_DEEPLINK_COMMENT_REGEX,
18
26
  } from '../helpers/constants/ios';
19
27
  import {
28
+ injectCodeBeforeMultiLineRegex,
20
29
  injectCodeByLineNumber,
21
30
  injectCodeByMultiLineRegex,
22
31
  injectCodeByMultiLineRegexAndReplaceLine,
32
+ replaceCodeByRegex,
33
+ matchRegexExists
23
34
  } from '../helpers/utils/codeInjection';
24
35
  import { FileManagement } from '../helpers/utils/fileManagement';
25
36
  import type { CustomerIOPluginOptionsIOS } from '../types/cio-types';
@@ -67,7 +78,7 @@ const addNotificationHandlerDeclaration = (stringContents: string) => {
67
78
  };
68
79
 
69
80
  const addNotificationConfiguration = (stringContents: string) => {
70
- stringContents = injectCodeByMultiLineRegex(
81
+ stringContents = injectCodeBeforeMultiLineRegex(
71
82
  stringContents,
72
83
  CIO_DIDFINISHLAUNCHINGMETHOD_REGEX,
73
84
  CIO_CONFIGURECIOSDKPUSHNOTIFICATION_SNIPPET
@@ -76,6 +87,26 @@ const addNotificationConfiguration = (stringContents: string) => {
76
87
  return stringContents;
77
88
  };
78
89
 
90
+ const addUserNotificationCenterConfiguration = (stringContents: string) => {
91
+ stringContents = injectCodeBeforeMultiLineRegex(
92
+ stringContents,
93
+ CIO_DIDFINISHLAUNCHINGMETHOD_REGEX,
94
+ CIO_CONFIGURECIOSDKUSERNOTIFICATIONCENTER_SNIPPET
95
+ );
96
+
97
+ return stringContents;
98
+ };
99
+
100
+ const addHandleDeeplinkInKilledStateConfiguration = (stringContents: string, regex: RegExp) => {
101
+ stringContents = injectCodeBeforeMultiLineRegex(
102
+ stringContents,
103
+ regex,
104
+ CIO_CONFIGUREDEEPLINK_KILLEDSTATE_SNIPPET
105
+ );
106
+
107
+ return stringContents;
108
+ };
109
+
79
110
  const addDidFailToRegisterForRemoteNotificationsWithError = (
80
111
  stringContents: string
81
112
  ) => {
@@ -111,15 +142,54 @@ const addAdditionalMethodsForPushNotifications = (stringContents: string) => {
111
142
  };
112
143
 
113
144
  const addAppdelegateHeaderModification = (stringContents: string) => {
114
- stringContents = injectCodeByMultiLineRegexAndReplaceLine(
115
- stringContents,
145
+ // Add UNUserNotificationCenterDelegate if needed
146
+ stringContents = stringContents.replace(
116
147
  CIO_APPDELEGATEHEADER_REGEX,
117
- CIO_APPDELEGATEHEADER_SNIPPET
148
+ (match, interfaceDeclaration, _groupedDelegates, existingDelegates) => {
149
+ if (existingDelegates && existingDelegates.includes(CIO_APPDELEGATEHEADER_USER_NOTIFICATION_CENTER_SNIPPET)) {
150
+ // The AppDelegate declaration already includes UNUserNotificationCenterDelegate, so we don't need to modify it
151
+ return match;
152
+ } else if (existingDelegates) {
153
+ // Other delegates exist, append ours
154
+ return `${CIO_APPDELEGATEHEADER_IMPORT_SNIPPET}
155
+ ${interfaceDeclaration}<${existingDelegates}, ${CIO_APPDELEGATEHEADER_USER_NOTIFICATION_CENTER_SNIPPET}>
156
+ `;
157
+ } else {
158
+ // No delegates exist, add ours
159
+ return `${CIO_APPDELEGATEHEADER_IMPORT_SNIPPET}
160
+ ${interfaceDeclaration.trim()} <${CIO_APPDELEGATEHEADER_USER_NOTIFICATION_CENTER_SNIPPET}>
161
+ `;
162
+ }
163
+ }
118
164
  );
119
165
 
120
166
  return stringContents;
121
167
  };
122
168
 
169
+ const addHandleDeeplinkInKilledState = (stringContents: string) => {
170
+ // Find if the deep link code snippet is already present
171
+ if (matchRegexExists(stringContents, CIO_DEEPLINK_COMMENT_REGEX)) {
172
+ return stringContents
173
+ }
174
+
175
+ // Check if the app delegate is using RCTBridge or LaunchOptions
176
+ var snippet = undefined
177
+ var regex = CIO_LAUNCHOPTIONS_DEEPLINK_MODIFIEDOPTIONS_REGEX;
178
+ if (matchRegexExists(stringContents, CIO_RCTBRIDGE_DEEPLINK_MODIFIEDOPTIONS_REGEX)) {
179
+ snippet = CIO_RCTBRIDGE_DEEPLINK_MODIFIEDOPTIONS_SNIPPET;
180
+ regex = CIO_RCTBRIDGE_DEEPLINK_MODIFIEDOPTIONS_REGEX;
181
+ }
182
+ else if (matchRegexExists(stringContents, CIO_LAUNCHOPTIONS_DEEPLINK_MODIFIEDOPTIONS_REGEX)) {
183
+ snippet = CIO_LAUNCHOPTIONS_MODIFIEDOPTIONS_SNIPPET;
184
+ }
185
+ // Add code only if the app delegate is using RCTBridge or LaunchOptions
186
+ if (snippet !== undefined) {
187
+ stringContents = addHandleDeeplinkInKilledStateConfiguration(stringContents, regex);
188
+ stringContents = replaceCodeByRegex(stringContents, regex, snippet);
189
+ }
190
+ return stringContents
191
+ }
192
+
123
193
  export const withAppDelegateModifications: ConfigPlugin<
124
194
  CustomerIOPluginOptionsIOS
125
195
  > = (configOuter, props) => {
@@ -151,6 +221,20 @@ export const withAppDelegateModifications: ConfigPlugin<
151
221
  ) {
152
222
  stringContents = addNotificationConfiguration(stringContents);
153
223
  }
224
+ if (
225
+ props.handleNotificationClick === undefined ||
226
+ props.handleNotificationClick === true
227
+ ) {
228
+ stringContents = addUserNotificationCenterConfiguration(stringContents);
229
+ }
230
+
231
+ if (
232
+ props.handleDeeplinkInKilledState !== undefined &&
233
+ props.handleDeeplinkInKilledState === true
234
+ ) {
235
+ stringContents = addHandleDeeplinkInKilledState(stringContents);
236
+ }
237
+
154
238
  stringContents = addAdditionalMethodsForPushNotifications(stringContents);
155
239
  stringContents =
156
240
  addDidFailToRegisterForRemoteNotificationsWithError(stringContents);
@@ -16,6 +16,8 @@ export type CustomerIOPluginOptionsIOS = {
16
16
  appleTeamId?: string;
17
17
  appName?: string;
18
18
  disableNotificationRegistration?: boolean;
19
+ handleNotificationClick?:boolean;
20
+ handleDeeplinkInKilledState?:boolean;
19
21
  useFrameworks?: 'static' | 'dynamic';
20
22
  pushNotification?: {
21
23
  useRichPush: boolean;