customerio-expo-plugin 1.0.0-beta.1 → 1.0.0-beta.10

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 (62) hide show
  1. package/README.md +5 -166
  2. package/lib/commonjs/android/withAndroidManifestUpdates.js +37 -0
  3. package/lib/commonjs/android/withAndroidManifestUpdates.js.map +1 -0
  4. package/lib/commonjs/android/withAppGoogleServices.js.map +1 -1
  5. package/lib/commonjs/android/withCIOAndroid.js +4 -0
  6. package/lib/commonjs/android/withCIOAndroid.js.map +1 -1
  7. package/lib/commonjs/android/withGistMavenRepository.js.map +1 -1
  8. package/lib/commonjs/android/withGoogleServicesJSON.js.map +1 -1
  9. package/lib/commonjs/android/withProjectGoogleServices.js.map +1 -1
  10. package/lib/commonjs/helpers/constants/android.js.map +1 -1
  11. package/lib/commonjs/helpers/constants/ios.js +27 -4
  12. package/lib/commonjs/helpers/constants/ios.js.map +1 -1
  13. package/lib/commonjs/helpers/native-files/ios/PushService.swift +1 -13
  14. package/lib/commonjs/helpers/utils/codeInjection.js.map +1 -1
  15. package/lib/commonjs/helpers/utils/fileManagement.js.map +1 -1
  16. package/lib/commonjs/helpers/utils/injectCIOPodfileCode.js.map +1 -1
  17. package/lib/commonjs/index.js.map +1 -1
  18. package/lib/commonjs/ios/withAppDelegateModifications.js +8 -4
  19. package/lib/commonjs/ios/withAppDelegateModifications.js.map +1 -1
  20. package/lib/commonjs/ios/withCIOIos.js.map +1 -1
  21. package/lib/commonjs/ios/withNotificationsXcodeProject.js +48 -27
  22. package/lib/commonjs/ios/withNotificationsXcodeProject.js.map +1 -1
  23. package/lib/commonjs/ios/withXcodeProject.js.map +1 -1
  24. package/lib/commonjs/postInstall.js.map +1 -1
  25. package/lib/commonjs/postInstallHelper.js.map +1 -1
  26. package/lib/commonjs/types/cio-types.js.map +1 -1
  27. package/lib/module/android/withAndroidManifestUpdates.js +30 -0
  28. package/lib/module/android/withAndroidManifestUpdates.js.map +1 -0
  29. package/lib/module/android/withAppGoogleServices.js.map +1 -1
  30. package/lib/module/android/withCIOAndroid.js +4 -0
  31. package/lib/module/android/withCIOAndroid.js.map +1 -1
  32. package/lib/module/android/withGistMavenRepository.js.map +1 -1
  33. package/lib/module/android/withGoogleServicesJSON.js.map +1 -1
  34. package/lib/module/android/withProjectGoogleServices.js.map +1 -1
  35. package/lib/module/helpers/constants/ios.js +25 -3
  36. package/lib/module/helpers/constants/ios.js.map +1 -1
  37. package/lib/module/helpers/native-files/ios/PushService.swift +1 -13
  38. package/lib/module/helpers/utils/codeInjection.js.map +1 -1
  39. package/lib/module/helpers/utils/fileManagement.js.map +1 -1
  40. package/lib/module/helpers/utils/injectCIOPodfileCode.js.map +1 -1
  41. package/lib/module/index.js.map +1 -1
  42. package/lib/module/ios/withAppDelegateModifications.js +8 -4
  43. package/lib/module/ios/withAppDelegateModifications.js.map +1 -1
  44. package/lib/module/ios/withCIOIos.js.map +1 -1
  45. package/lib/module/ios/withNotificationsXcodeProject.js +49 -27
  46. package/lib/module/ios/withNotificationsXcodeProject.js.map +1 -1
  47. package/lib/module/ios/withXcodeProject.js.map +1 -1
  48. package/lib/module/postInstall.js.map +1 -1
  49. package/lib/module/postInstallHelper.js.map +1 -1
  50. package/lib/module/types/cio-types.js.map +1 -1
  51. package/lib/typescript/android/withAndroidManifestUpdates.d.ts +3 -0
  52. package/lib/typescript/helpers/constants/ios.d.ts +5 -4
  53. package/lib/typescript/ios/withAppDelegateModifications.d.ts +2 -1
  54. package/lib/typescript/types/cio-types.d.ts +2 -0
  55. package/package.json +5 -2
  56. package/src/android/withAndroidManifestUpdates.ts +49 -0
  57. package/src/android/withCIOAndroid.ts +4 -0
  58. package/src/helpers/constants/ios.ts +31 -3
  59. package/src/helpers/native-files/ios/PushService.swift +1 -13
  60. package/src/ios/withAppDelegateModifications.ts +14 -6
  61. package/src/ios/withNotificationsXcodeProject.ts +76 -28
  62. package/src/types/cio-types.ts +2 -0
@@ -1,6 +1,7 @@
1
1
  import type { ExpoConfig } from '@expo/config-types';
2
2
 
3
3
  import type { CustomerIOPluginOptionsAndroid } from '../types/cio-types';
4
+ import { withAndroidManifestUpdates } from './withAndroidManifestUpdates';
4
5
  import { withAppGoogleServices } from './withAppGoogleServices';
5
6
  import { withGistMavenRepository } from './withGistMavenRepository';
6
7
  import { withGoogleServicesJSON } from './withGoogleServicesJSON';
@@ -14,6 +15,9 @@ export function withCIOAndroid(
14
15
  config = withProjectGoogleServices(config, props);
15
16
  config = withAppGoogleServices(config, props);
16
17
  config = withGoogleServicesJSON(config, props);
18
+ if (props.setHighPriorityPushHandler) {
19
+ config = withAndroidManifestUpdates(config, props);
20
+ }
17
21
 
18
22
  return config;
19
23
  }
@@ -1,6 +1,17 @@
1
- export const LOCAL_PATH_TO_CIO_NSE_FILES = `node_modules/customerio-expo-plugin/src/helpers/native-files/ios`;
1
+ const finder = require('find-package-json');
2
+ const path = require('path');
3
+
4
+ const f = finder(__dirname);
5
+ let pluginPackageRoot = f.next().filename;
6
+ // This is the path to the root of the customerio-expo-plugin package
7
+ pluginPackageRoot = path.dirname(pluginPackageRoot);
8
+
9
+ export const LOCAL_PATH_TO_CIO_NSE_FILES = path.join(
10
+ pluginPackageRoot,
11
+ 'src/helpers/native-files/ios'
12
+ );
2
13
  export const IOS_DEPLOYMENT_TARGET = '13.0';
3
- export const CIO_SDK_VERSION = '~> 2.0.0';
14
+ export const CIO_SDK_VERSION = "'~> 2.0'";
4
15
  export const CIO_PODFILE_REGEX = /pod 'CustomerIO\/MessagingPushAPN'/;
5
16
  export const CIO_CIO_TARGET_REGEX = /cio_target_names/;
6
17
  export const CIO_PODFILE_NOTIFICATION_REGEX = /target 'NotificationService' do/;
@@ -40,10 +51,12 @@ CIOAppPushNotificationsHandler* pnHandlerObj = [[CIOAppPushNotificationsHandler
40
51
  `;
41
52
 
42
53
  export const CIO_DIDFAILTOREGISTERFORREMOTENOTIFICATIONSWITHERROR_SNIPPET = `
54
+ [super application:application didFailToRegisterForRemoteNotificationsWithError:error];
43
55
  [pnHandlerObj application:application error:error];
44
56
  `;
45
57
 
46
58
  export const CIO_DIDREGISTERFORREMOTENOTIFICATIONSWITHDEVICETOKEN_SNIPPET = `
59
+ [super application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
47
60
  return [pnHandlerObj application:application deviceToken:deviceToken];
48
61
  `;
49
62
 
@@ -64,7 +77,7 @@ export const CIO_WILLPRESENTNOTIFICATIONHANDLER_SNIPPET = `
64
77
  - (void)userNotificationCenter:(UNUserNotificationCenter* )center willPresentNotification:(UNNotification* )notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler {
65
78
  completionHandler( UNNotificationPresentationOptionAlert + UNNotificationPresentationOptionSound);
66
79
  }`;
67
- export const CIO_PODFILE_SNIPPET = ` pod 'CustomerIO/MessagingPushAPN', '${CIO_SDK_VERSION}'`;
80
+ export const CIO_PODFILE_SNIPPET = ` pod 'CustomerIO/MessagingPushAPN', ${CIO_SDK_VERSION}`;
68
81
  export const CIO_PODFILE_NOTIFICATION_SNIPPET = `
69
82
  target '${CIO_NOTIFICATION_TARGET_NAME}' do
70
83
  ${CIO_PODFILE_SNIPPET}
@@ -74,3 +87,18 @@ target '${CIO_NOTIFICATION_TARGET_NAME}' do
74
87
  use_frameworks! :linkage => :static
75
88
  ${CIO_PODFILE_SNIPPET}
76
89
  end`;
90
+
91
+ export const CIO_REGISTER_PUSHNOTIFICATION_SNIPPET = `
92
+ @objc(registerPushNotification:)
93
+ public func registerPushNotification(withNotificationDelegate notificationDelegate: UNUserNotificationCenterDelegate) {
94
+
95
+ let center = UNUserNotificationCenter.current()
96
+ center.delegate = notificationDelegate
97
+ center.requestAuthorization(options: [.sound, .alert, .badge]) { (granted, error) in
98
+ if error == nil{
99
+ DispatchQueue.main.async {
100
+ UIApplication.shared.registerForRemoteNotifications()
101
+ }
102
+ }
103
+ }
104
+ }`;
@@ -9,19 +9,7 @@ public class CIOAppPushNotificationsHandler : NSObject {
9
9
 
10
10
  public override init() {}
11
11
 
12
- @objc(registerPushNotification:)
13
- public func registerPushNotification(withNotificationDelegate notificationDelegate: UNUserNotificationCenterDelegate) {
14
-
15
- let center = UNUserNotificationCenter.current()
16
- center.delegate = notificationDelegate
17
- center.requestAuthorization(options: [.sound, .alert, .badge]) { (granted, error) in
18
- if error == nil{
19
- DispatchQueue.main.async {
20
- UIApplication.shared.registerForRemoteNotifications()
21
- }
22
- }
23
- }
24
- }
12
+ {{REGISTER_SNIPPET}}
25
13
 
26
14
  @objc(application:deviceToken:)
27
15
  public func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
@@ -22,6 +22,7 @@ import {
22
22
  injectCodeByMultiLineRegexAndReplaceLine,
23
23
  } from '../helpers/utils/codeInjection';
24
24
  import { FileManagement } from '../helpers/utils/fileManagement';
25
+ import type { CustomerIOPluginOptionsIOS } from '../types/cio-types';
25
26
 
26
27
  const pushCodeSnippets = [
27
28
  CIO_DIDRECEIVENOTIFICATIONRESPONSEHANDLER_SNIPPET,
@@ -87,7 +88,7 @@ const addDidFailToRegisterForRemoteNotificationsWithError = (
87
88
  return stringContents;
88
89
  };
89
90
 
90
- const AddDidRegisterForRemoteNotificationsWithDeviceToken = (
91
+ const addDidRegisterForRemoteNotificationsWithDeviceToken = (
91
92
  stringContents: string
92
93
  ) => {
93
94
  stringContents = injectCodeByMultiLineRegexAndReplaceLine(
@@ -119,9 +120,9 @@ const addAppdelegateHeaderModification = (stringContents: string) => {
119
120
  return stringContents;
120
121
  };
121
122
 
122
- export const withAppDelegateModifications: ConfigPlugin<any> = (
123
- configOuter
124
- ) => {
123
+ export const withAppDelegateModifications: ConfigPlugin<
124
+ CustomerIOPluginOptionsIOS
125
+ > = (configOuter, props) => {
125
126
  return withAppDelegate(configOuter, async (config) => {
126
127
  let stringContents = config.modResults.contents;
127
128
  const regex = new RegExp(
@@ -142,12 +143,19 @@ export const withAppDelegateModifications: ConfigPlugin<any> = (
142
143
  config.modRequest.projectName as string
143
144
  );
144
145
  stringContents = addNotificationHandlerDeclaration(stringContents);
145
- stringContents = addNotificationConfiguration(stringContents);
146
+
147
+ // any other value would be treated as true, it has to be explicitly false to disable
148
+ if (
149
+ props.disableNotificationRegistration !== undefined &&
150
+ props.disableNotificationRegistration === false
151
+ ) {
152
+ stringContents = addNotificationConfiguration(stringContents);
153
+ }
146
154
  stringContents = addAdditionalMethodsForPushNotifications(stringContents);
147
155
  stringContents =
148
156
  addDidFailToRegisterForRemoteNotificationsWithError(stringContents);
149
157
  stringContents =
150
- AddDidRegisterForRemoteNotificationsWithDeviceToken(stringContents);
158
+ addDidRegisterForRemoteNotificationsWithDeviceToken(stringContents);
151
159
 
152
160
  config.modResults.contents = stringContents;
153
161
  } else {
@@ -1,8 +1,12 @@
1
- import { ConfigPlugin, withXcodeProject } from '@expo/config-plugins';
2
- import xcode from 'xcode';
1
+ import {
2
+ ConfigPlugin,
3
+ XcodeProject,
4
+ withXcodeProject,
5
+ } from '@expo/config-plugins';
3
6
 
4
7
  import {
5
8
  CIO_NOTIFICATION_TARGET_NAME,
9
+ CIO_REGISTER_PUSHNOTIFICATION_SNIPPET,
6
10
  DEFAULT_BUNDLE_VERSION,
7
11
  LOCAL_PATH_TO_CIO_NSE_FILES,
8
12
  } from '../helpers/constants/ios';
@@ -17,21 +21,10 @@ const ENV_FILENAME = 'Env.swift';
17
21
  const TARGETED_DEVICE_FAMILY = `"1,2"`;
18
22
 
19
23
  const addNotificationServiceExtension = async (
20
- options: CustomerIOPluginOptionsIOS
24
+ options: CustomerIOPluginOptionsIOS,
25
+ xcodeProject: XcodeProject
21
26
  ) => {
22
- // iosPath and appName are predefined from Expo config.
23
- // See function withCioNotificationsXcodeProject to get where the variabes are pulled from.
24
- const { iosPath, appName } = options;
25
-
26
- const projPath = `${iosPath}/${appName}.xcodeproj/project.pbxproj`;
27
-
28
- const xcodeProject = xcode.project(projPath);
29
-
30
- xcodeProject.parse(async function (err: Error) {
31
- if (err) {
32
- throw new Error(`Error parsing iOS project: ${JSON.stringify(err)}`);
33
- }
34
-
27
+ try {
35
28
  if (options.pushNotification) {
36
29
  await addPushNotificationFile(options, xcodeProject);
37
30
  }
@@ -39,9 +32,11 @@ const addNotificationServiceExtension = async (
39
32
  if (options.pushNotification?.useRichPush) {
40
33
  await addRichPushXcodeProj(options, xcodeProject);
41
34
  }
42
-
43
- FileManagement.writeFile(projPath, xcodeProject.writeSync());
44
- });
35
+ return xcodeProject;
36
+ } catch (error: any) {
37
+ console.error(error);
38
+ return null;
39
+ }
45
40
  };
46
41
 
47
42
  export const withCioNotificationsXcodeProject: ConfigPlugin<
@@ -84,6 +79,7 @@ export const withCioNotificationsXcodeProject: ConfigPlugin<
84
79
  }
85
80
 
86
81
  const options = {
82
+ ...props,
87
83
  appleTeamId,
88
84
  bundleIdentifier,
89
85
  bundleShortVersion,
@@ -95,7 +91,14 @@ export const withCioNotificationsXcodeProject: ConfigPlugin<
95
91
  pushNotification,
96
92
  };
97
93
 
98
- await addNotificationServiceExtension(options);
94
+ const modifiedProjectFile = await addNotificationServiceExtension(
95
+ options,
96
+ config.modResults
97
+ );
98
+
99
+ if (modifiedProjectFile) {
100
+ config.modResults = modifiedProjectFile;
101
+ }
99
102
 
100
103
  return config;
101
104
  });
@@ -117,6 +120,15 @@ const addRichPushXcodeProj = async (
117
120
 
118
121
  await injectCIONotificationPodfileCode(iosPath, useFrameworks);
119
122
 
123
+ // Check if `CIO_NOTIFICATION_TARGET_NAME` group already exist in the project
124
+ // If true then skip creating a new group to avoid duplicate folders
125
+ if (xcodeProject.pbxTargetByName(CIO_NOTIFICATION_TARGET_NAME)) {
126
+ console.warn(
127
+ `${CIO_NOTIFICATION_TARGET_NAME} already exists in project. Skipping...`
128
+ );
129
+ return;
130
+ }
131
+
120
132
  const nsePath = `${iosPath}/${CIO_NOTIFICATION_TARGET_NAME}`;
121
133
  FileManagement.mkdir(nsePath, {
122
134
  recursive: true,
@@ -160,7 +172,7 @@ const addRichPushXcodeProj = async (
160
172
  // files / folder appear in the file explorer in Xcode.
161
173
  const groups = xcodeProject.hash.project.objects['PBXGroup'];
162
174
  Object.keys(groups).forEach((key) => {
163
- if (groups[key].name === undefined) {
175
+ if (groups[key].name === undefined && groups[key].path === undefined) {
164
176
  xcodeProject.addToPbxGroup(extGroup.uuid, key);
165
177
  }
166
178
  });
@@ -290,13 +302,23 @@ const updateNseEnv = (
290
302
  }
291
303
 
292
304
  if (options.pushNotification?.env?.region) {
293
- let region = '';
294
- if (options.pushNotification?.env?.region === 'us') {
295
- region = 'Region.US';
296
- } else if (options.pushNotification?.env?.region === 'eu') {
297
- region = 'Region.EU';
305
+ const regionMap = {
306
+ us: 'Region.US',
307
+ eu: 'Region.EU',
308
+ };
309
+ const region = options.pushNotification?.env?.region?.toLowerCase();
310
+ const mappedRegion = (regionMap as any)[region] || '';
311
+ if (!mappedRegion) {
312
+ console.warn(
313
+ `${options.pushNotification?.env?.region} is an invalid region. Please use the values from the docs: https://customer.io/docs/sdk/expo/getting-started/#configure-the-plugin`
314
+ );
315
+ } else {
316
+ envFileContent = replaceCodeByRegex(
317
+ envFileContent,
318
+ REGION_RE,
319
+ mappedRegion
320
+ );
298
321
  }
299
- envFileContent = replaceCodeByRegex(envFileContent, REGION_RE, region);
300
322
  }
301
323
 
302
324
  FileManagement.writeFile(envFileName, envFileContent);
@@ -310,24 +332,50 @@ async function addPushNotificationFile(
310
332
  const file = 'PushService.swift';
311
333
  const appPath = `${iosPath}/${appName}`;
312
334
  const getTargetFile = (filename: string) => `${appPath}/${filename}`;
335
+ const targetFile = getTargetFile(file);
313
336
 
337
+ // Check whether {file} exists in the project. If false, then add the file
338
+ // If {file} exists then skip and return
314
339
  if (!FileManagement.exists(getTargetFile(file))) {
315
340
  FileManagement.mkdir(appPath, {
316
341
  recursive: true,
317
342
  });
318
343
 
319
- const targetFile = getTargetFile(file);
320
344
  FileManagement.copyFile(
321
345
  `${LOCAL_PATH_TO_CIO_NSE_FILES}/${file}`,
322
346
  targetFile
323
347
  );
324
348
  } else {
325
349
  console.log(`${getTargetFile(file)} already exists. Skipping...`);
350
+ return;
326
351
  }
327
352
 
353
+ updatePushFile(options, targetFile);
354
+
328
355
  const group = xcodeProject.pbxCreateGroup('CustomerIONotifications');
329
356
  const classesKey = xcodeProject.findPBXGroupKey({ name: `${appName}` });
330
357
  xcodeProject.addToPbxGroup(group, classesKey);
331
358
 
332
359
  xcodeProject.addSourceFile(`${appName}/${file}`, null, group);
333
360
  }
361
+
362
+ const updatePushFile = (
363
+ options: CustomerIOPluginOptionsIOS,
364
+ envFileName: string
365
+ ) => {
366
+ const REGISTER_RE = /\{\{REGISTER_SNIPPET\}\}/;
367
+
368
+ let envFileContent = FileManagement.readFile(envFileName);
369
+
370
+ let snippet = '';
371
+ if (
372
+ options.disableNotificationRegistration !== undefined &&
373
+ options.disableNotificationRegistration === false
374
+ ) {
375
+ snippet = CIO_REGISTER_PUSHNOTIFICATION_SNIPPET;
376
+ }
377
+
378
+ envFileContent = replaceCodeByRegex(envFileContent, REGISTER_RE, snippet);
379
+
380
+ FileManagement.writeFile(envFileName, envFileContent);
381
+ };
@@ -15,6 +15,7 @@ export type CustomerIOPluginOptionsIOS = {
15
15
  iosDeploymentTarget?: string;
16
16
  appleTeamId?: string;
17
17
  appName?: string;
18
+ disableNotificationRegistration?: boolean;
18
19
  useFrameworks?: 'static' | 'dynamic';
19
20
  pushNotification?: {
20
21
  useRichPush: boolean;
@@ -29,6 +30,7 @@ export type CustomerIOPluginOptionsIOS = {
29
30
  export type CustomerIOPluginOptionsAndroid = {
30
31
  androidPath: string;
31
32
  googleServicesFile?: string;
33
+ setHighPriorityPushHandler?: boolean;
32
34
  };
33
35
 
34
36
  export type CustomerIOPluginOptions = {