customerio-expo-plugin 3.4.0 → 3.5.1
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.
- package/package.json +2 -1
- package/plugin/lib/commonjs/android/withAndroidManifestUpdates.js +64 -59
- package/plugin/lib/commonjs/android/withAndroidManifestUpdates.js.map +1 -1
- package/plugin/lib/commonjs/android/withAppGoogleServices.js +10 -7
- package/plugin/lib/commonjs/android/withAppGoogleServices.js.map +1 -1
- package/plugin/lib/commonjs/android/withGoogleServicesJSON.js +18 -21
- package/plugin/lib/commonjs/android/withGoogleServicesJSON.js.map +1 -1
- package/plugin/lib/commonjs/android/withLocationGradleProperties.js +16 -12
- package/plugin/lib/commonjs/android/withLocationGradleProperties.js.map +1 -1
- package/plugin/lib/commonjs/android/withMainApplicationModifications.js +19 -12
- package/plugin/lib/commonjs/android/withMainApplicationModifications.js.map +1 -1
- package/plugin/lib/commonjs/android/withNotificationChannelMetadata.js +2 -1
- package/plugin/lib/commonjs/android/withNotificationChannelMetadata.js.map +1 -1
- package/plugin/lib/commonjs/android/withProjectBuildGradle.js +29 -25
- package/plugin/lib/commonjs/android/withProjectBuildGradle.js.map +1 -1
- package/plugin/lib/commonjs/android/withProjectGoogleServices.js +9 -5
- package/plugin/lib/commonjs/android/withProjectGoogleServices.js.map +1 -1
- package/plugin/lib/commonjs/helpers/utils/injectCIOPodfileCode.js +63 -31
- package/plugin/lib/commonjs/helpers/utils/injectCIOPodfileCode.js.map +1 -1
- package/plugin/lib/commonjs/ios/withAppDelegateModifications.js +47 -33
- package/plugin/lib/commonjs/ios/withAppDelegateModifications.js.map +1 -1
- package/plugin/lib/commonjs/ios/withCIOIosSwift.js +26 -42
- package/plugin/lib/commonjs/ios/withCIOIosSwift.js.map +1 -1
- package/plugin/lib/commonjs/ios/withGoogleServicesJsonFile.js +46 -30
- package/plugin/lib/commonjs/ios/withGoogleServicesJsonFile.js.map +1 -1
- package/plugin/lib/commonjs/ios/withNotificationsXcodeProject.js +193 -123
- package/plugin/lib/commonjs/ios/withNotificationsXcodeProject.js.map +1 -1
- package/plugin/lib/module/android/withAndroidManifestUpdates.js +61 -58
- package/plugin/lib/module/android/withAndroidManifestUpdates.js.map +1 -1
- package/plugin/lib/module/android/withAppGoogleServices.js +9 -7
- package/plugin/lib/module/android/withAppGoogleServices.js.map +1 -1
- package/plugin/lib/module/android/withGoogleServicesJSON.js +17 -21
- package/plugin/lib/module/android/withGoogleServicesJSON.js.map +1 -1
- package/plugin/lib/module/android/withLocationGradleProperties.js +15 -12
- package/plugin/lib/module/android/withLocationGradleProperties.js.map +1 -1
- package/plugin/lib/module/android/withMainApplicationModifications.js +18 -12
- package/plugin/lib/module/android/withMainApplicationModifications.js.map +1 -1
- package/plugin/lib/module/android/withNotificationChannelMetadata.js +1 -1
- package/plugin/lib/module/android/withNotificationChannelMetadata.js.map +1 -1
- package/plugin/lib/module/android/withProjectBuildGradle.js +28 -25
- package/plugin/lib/module/android/withProjectBuildGradle.js.map +1 -1
- package/plugin/lib/module/android/withProjectGoogleServices.js +8 -5
- package/plugin/lib/module/android/withProjectGoogleServices.js.map +1 -1
- package/plugin/lib/module/helpers/utils/injectCIOPodfileCode.js +61 -31
- package/plugin/lib/module/helpers/utils/injectCIOPodfileCode.js.map +1 -1
- package/plugin/lib/module/ios/withAppDelegateModifications.js +45 -33
- package/plugin/lib/module/ios/withAppDelegateModifications.js.map +1 -1
- package/plugin/lib/module/ios/withCIOIosSwift.js +24 -42
- package/plugin/lib/module/ios/withCIOIosSwift.js.map +1 -1
- package/plugin/lib/module/ios/withGoogleServicesJsonFile.js +45 -30
- package/plugin/lib/module/ios/withGoogleServicesJsonFile.js.map +1 -1
- package/plugin/lib/module/ios/withNotificationsXcodeProject.js +188 -123
- package/plugin/lib/module/ios/withNotificationsXcodeProject.js.map +1 -1
- package/plugin/lib/typescript/android/withAndroidManifestUpdates.d.ts +2 -0
- package/plugin/lib/typescript/android/withAppGoogleServices.d.ts +1 -0
- package/plugin/lib/typescript/android/withGoogleServicesJSON.d.ts +1 -0
- package/plugin/lib/typescript/android/withLocationGradleProperties.d.ts +2 -0
- package/plugin/lib/typescript/android/withMainApplicationModifications.d.ts +6 -0
- package/plugin/lib/typescript/android/withNotificationChannelMetadata.d.ts +5 -0
- package/plugin/lib/typescript/android/withProjectBuildGradle.d.ts +9 -0
- package/plugin/lib/typescript/android/withProjectGoogleServices.d.ts +1 -0
- package/plugin/lib/typescript/helpers/utils/injectCIOPodfileCode.d.ts +14 -0
- package/plugin/lib/typescript/ios/withAppDelegateModifications.d.ts +13 -0
- package/plugin/lib/typescript/ios/withCIOIosSwift.d.ts +11 -0
- package/plugin/lib/typescript/ios/withGoogleServicesJsonFile.d.ts +14 -1
- package/plugin/lib/typescript/ios/withNotificationsXcodeProject.d.ts +53 -2
- package/plugin/src/android/withAndroidManifestUpdates.ts +83 -73
- package/plugin/src/android/withAppGoogleServices.ts +13 -11
- package/plugin/src/android/withGoogleServicesJSON.ts +30 -28
- package/plugin/src/android/withLocationGradleProperties.ts +23 -17
- package/plugin/src/android/withMainApplicationModifications.ts +25 -15
- package/plugin/src/android/withNotificationChannelMetadata.ts +1 -1
- package/plugin/src/android/withProjectBuildGradle.ts +37 -27
- package/plugin/src/android/withProjectGoogleServices.ts +14 -9
- package/plugin/src/helpers/utils/injectCIOPodfileCode.ts +83 -48
- package/plugin/src/ios/withAppDelegateModifications.ts +61 -48
- package/plugin/src/ios/withCIOIosSwift.ts +33 -50
- package/plugin/src/ios/withGoogleServicesJsonFile.ts +66 -48
- package/plugin/src/ios/withNotificationsXcodeProject.ts +258 -208
|
@@ -5,38 +5,40 @@ import { logger } from '../utils/logger';
|
|
|
5
5
|
import { FileManagement } from './../helpers/utils/fileManagement';
|
|
6
6
|
import type { CustomerIOPluginOptionsAndroid } from './../types/cio-types';
|
|
7
7
|
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
);
|
|
24
|
-
} catch {
|
|
25
|
-
logger.info(
|
|
26
|
-
`There was an error copying your google-services.json file. You can copy it manually into ${androidPath}/app/google-services.json`
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
} else {
|
|
30
|
-
logger.info(
|
|
31
|
-
`The Google Services file provided in ${googleServicesFile} doesn't seem to exist. You can copy it manually into ${androidPath}/app/google-services.json`
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
} else {
|
|
8
|
+
export function copyGoogleServicesFile(
|
|
9
|
+
androidPath: string,
|
|
10
|
+
googleServicesFile: string | undefined
|
|
11
|
+
): void {
|
|
12
|
+
const destination = `${androidPath}/app/google-services.json`;
|
|
13
|
+
|
|
14
|
+
if (FileManagement.exists(destination)) {
|
|
15
|
+
logger.info(`File already exists: ${destination}. Skipping...`);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (googleServicesFile && FileManagement.exists(googleServicesFile)) {
|
|
20
|
+
try {
|
|
21
|
+
FileManagement.copyFile(googleServicesFile, destination);
|
|
22
|
+
} catch {
|
|
35
23
|
logger.info(
|
|
36
|
-
`
|
|
24
|
+
`There was an error copying your google-services.json file. You can copy it manually into ${destination}`
|
|
37
25
|
);
|
|
38
26
|
}
|
|
27
|
+
} else {
|
|
28
|
+
logger.info(
|
|
29
|
+
`The Google Services file provided in ${googleServicesFile} doesn't seem to exist. You can copy it manually into ${destination}`
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
39
33
|
|
|
34
|
+
export const withGoogleServicesJSON: ConfigPlugin<
|
|
35
|
+
CustomerIOPluginOptionsAndroid
|
|
36
|
+
> = (configOuter, cioProps) => {
|
|
37
|
+
return withProjectBuildGradle(configOuter, (props) => {
|
|
38
|
+
copyGoogleServicesFile(
|
|
39
|
+
props.modRequest.platformProjectRoot,
|
|
40
|
+
cioProps?.googleServicesFile
|
|
41
|
+
);
|
|
40
42
|
return props;
|
|
41
43
|
});
|
|
42
44
|
};
|
|
@@ -6,6 +6,28 @@ import type { CustomerIOPluginLocationOptions } from '../types/cio-types';
|
|
|
6
6
|
|
|
7
7
|
const CUSTOMERIO_LOCATION_ENABLED_KEY = 'customerio_location_enabled';
|
|
8
8
|
|
|
9
|
+
export function modifyGradleProperties(
|
|
10
|
+
items: PropertiesItem[]
|
|
11
|
+
): PropertiesItem[] {
|
|
12
|
+
const existingIndex = items.findIndex(
|
|
13
|
+
(item) => item.type === 'property' && item.key === CUSTOMERIO_LOCATION_ENABLED_KEY
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const newItem: PropertiesItem = {
|
|
17
|
+
type: 'property',
|
|
18
|
+
key: CUSTOMERIO_LOCATION_ENABLED_KEY,
|
|
19
|
+
value: 'true',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
if (existingIndex >= 0) {
|
|
23
|
+
items[existingIndex] = newItem;
|
|
24
|
+
} else {
|
|
25
|
+
items.push(newItem);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return items;
|
|
29
|
+
}
|
|
30
|
+
|
|
9
31
|
/**
|
|
10
32
|
* Adds or updates customerio_location_enabled in android/gradle.properties when location.enabled is true.
|
|
11
33
|
* The Customer.io React Native SDK reads this to enable the location native module.
|
|
@@ -19,23 +41,7 @@ export const withLocationGradleProperties: ConfigPlugin<{
|
|
|
19
41
|
|
|
20
42
|
return withGradleProperties(config, (config) => {
|
|
21
43
|
const items = config.modResults as PropertiesItem[];
|
|
22
|
-
|
|
23
|
-
(item) => item.type === 'property' && item.key === CUSTOMERIO_LOCATION_ENABLED_KEY
|
|
24
|
-
);
|
|
25
|
-
|
|
26
|
-
const newItem: PropertiesItem = {
|
|
27
|
-
type: 'property',
|
|
28
|
-
key: CUSTOMERIO_LOCATION_ENABLED_KEY,
|
|
29
|
-
value: 'true',
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
if (existingIndex >= 0) {
|
|
33
|
-
items[existingIndex] = newItem;
|
|
34
|
-
} else {
|
|
35
|
-
items.push(newItem);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
config.modResults = items;
|
|
44
|
+
config.modResults = modifyGradleProperties(items);
|
|
39
45
|
return config;
|
|
40
46
|
});
|
|
41
47
|
};
|
|
@@ -36,6 +36,30 @@ const getLocationInitOptions = (
|
|
|
36
36
|
trackingMode: sdkConfig?.location?.trackingMode,
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
+
const SDK_INITIALIZER_CLASS = 'CustomerIOSDKInitializer';
|
|
40
|
+
const SDK_INITIALIZER_PACKAGE = 'io.customer.sdk.expo';
|
|
41
|
+
const SDK_INITIALIZER_FILE = `${SDK_INITIALIZER_CLASS}.kt`;
|
|
42
|
+
const SDK_INITIALIZER_IMPORT = `import ${SDK_INITIALIZER_PACKAGE}.${SDK_INITIALIZER_CLASS}`;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Pure string transform: given the existing MainApplication contents, returns the contents
|
|
46
|
+
* with the CustomerIOSDKInitializer import and onCreate call injected (idempotent — if the
|
|
47
|
+
* initialize call is already present, the call-injection step is skipped).
|
|
48
|
+
*/
|
|
49
|
+
export function injectCustomerIOInitializerIntoMainApplication(
|
|
50
|
+
contents: string
|
|
51
|
+
): string {
|
|
52
|
+
let next = addImportToFile(contents, SDK_INITIALIZER_IMPORT);
|
|
53
|
+
if (!next.includes(CIO_NATIVE_SDK_INITIALIZE_CALL)) {
|
|
54
|
+
next = addCodeToMethod(
|
|
55
|
+
next,
|
|
56
|
+
CIO_MAINAPPLICATION_ONCREATE_REGEX,
|
|
57
|
+
CIO_NATIVE_SDK_INITIALIZE_SNIPPET
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
return next;
|
|
61
|
+
}
|
|
62
|
+
|
|
39
63
|
/**
|
|
40
64
|
* Setup CustomerIOSDKInitializer for Android auto initialization
|
|
41
65
|
*/
|
|
@@ -44,30 +68,16 @@ const setupCustomerIOSDKInitializer = (
|
|
|
44
68
|
sdkConfig: NativeSDKConfig,
|
|
45
69
|
location?: CustomerIOPluginLocationOptions,
|
|
46
70
|
): string => {
|
|
47
|
-
const SDK_INITIALIZER_CLASS = 'CustomerIOSDKInitializer';
|
|
48
|
-
const SDK_INITIALIZER_PACKAGE = 'io.customer.sdk.expo';
|
|
49
|
-
|
|
50
|
-
const SDK_INITIALIZER_FILE = `${SDK_INITIALIZER_CLASS}.kt`;
|
|
51
|
-
const SDK_INITIALIZER_IMPORT = `import ${SDK_INITIALIZER_PACKAGE}.${SDK_INITIALIZER_CLASS}`;
|
|
52
|
-
|
|
53
71
|
const locationOptions = getLocationInitOptions(location, sdkConfig);
|
|
54
|
-
let content = config.modResults.contents;
|
|
55
72
|
|
|
56
73
|
try {
|
|
57
74
|
// Always regenerate the CustomerIOSDKInitializer file to reflect config changes
|
|
58
75
|
copyTemplateFile(config, SDK_INITIALIZER_FILE, SDK_INITIALIZER_PACKAGE, (content) =>
|
|
59
76
|
patchNativeSDKInitializer(content, PLATFORM.ANDROID, sdkConfig, locationOptions)
|
|
60
77
|
);
|
|
61
|
-
|
|
62
|
-
content = addImportToFile(content, SDK_INITIALIZER_IMPORT);
|
|
63
|
-
// Add initialization code to onCreate if not already present
|
|
64
|
-
if (!content.includes(CIO_NATIVE_SDK_INITIALIZE_CALL)) {
|
|
65
|
-
content = addCodeToMethod(content, CIO_MAINAPPLICATION_ONCREATE_REGEX, CIO_NATIVE_SDK_INITIALIZE_SNIPPET);
|
|
66
|
-
}
|
|
78
|
+
return injectCustomerIOInitializerIntoMainApplication(config.modResults.contents);
|
|
67
79
|
} catch (error) {
|
|
68
80
|
logger.warn(`Could not setup ${SDK_INITIALIZER_CLASS}:`, error);
|
|
69
81
|
return config.modResults.contents;
|
|
70
82
|
}
|
|
71
|
-
|
|
72
|
-
return content;
|
|
73
83
|
};
|
|
@@ -7,7 +7,7 @@ import type { CustomerIOPluginOptionsAndroid } from '../types/cio-types';
|
|
|
7
7
|
/**
|
|
8
8
|
* Adds a metadata entry to the Android manifest if it doesn't already exist
|
|
9
9
|
*/
|
|
10
|
-
const addMetadataIfNotExists = (
|
|
10
|
+
export const addMetadataIfNotExists = (
|
|
11
11
|
application: ManifestApplication,
|
|
12
12
|
name: string,
|
|
13
13
|
value: string
|
|
@@ -23,6 +23,40 @@ function shouldDisableAndroid16Support(
|
|
|
23
23
|
return isExpoVersion53OrLower(config);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Pure string transform: injects an androidx resolution-strategy block into the
|
|
28
|
+
* project-level build.gradle's `allprojects { ... }` section when
|
|
29
|
+
* `disableAndroid16Support` is true. Idempotent — returns input unchanged if the
|
|
30
|
+
* snippet is already present, or if the flag is false.
|
|
31
|
+
*/
|
|
32
|
+
export function modifyProjectBuildGradleAndroid16Support(
|
|
33
|
+
contents: string,
|
|
34
|
+
options: { disableAndroid16Support: boolean }
|
|
35
|
+
): string {
|
|
36
|
+
if (!options.disableAndroid16Support) {
|
|
37
|
+
return contents;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (contents.includes('androidx.core:core-ktx:1.13.1')) {
|
|
41
|
+
return contents;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const resolutionStrategy = `
|
|
45
|
+
configurations.all {
|
|
46
|
+
resolutionStrategy {
|
|
47
|
+
// Disable Android 16 support by forcing older androidx versions
|
|
48
|
+
// Compatible with API 35 and AGP 8.8.2 (prevents API 36/AGP 8.9.1+ requirement)
|
|
49
|
+
force 'androidx.core:core-ktx:1.13.1'
|
|
50
|
+
force 'androidx.lifecycle:lifecycle-process:2.8.7'
|
|
51
|
+
}
|
|
52
|
+
}`;
|
|
53
|
+
|
|
54
|
+
return contents.replace(
|
|
55
|
+
/allprojects\s*\{/,
|
|
56
|
+
`allprojects {${resolutionStrategy}`
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
26
60
|
/**
|
|
27
61
|
* Adds dependency resolution strategy to force specific androidx versions.
|
|
28
62
|
* This disables Android 16 support for apps using Expo SDK 53 or older gradle versions.
|
|
@@ -38,34 +72,10 @@ export function withProjectBuildGradle(
|
|
|
38
72
|
androidOptions?: CustomerIOPluginOptionsAndroid
|
|
39
73
|
): ExpoConfig {
|
|
40
74
|
return withExpoProjectBuildGradle(config, (config) => {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
if (!shouldDisableAndroid16Support(config, androidOptions)) {
|
|
45
|
-
return config;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Skip if already applied
|
|
49
|
-
if (modResults.contents.includes('androidx.core:core-ktx:1.13.1')) {
|
|
50
|
-
return config;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const resolutionStrategy = `
|
|
54
|
-
configurations.all {
|
|
55
|
-
resolutionStrategy {
|
|
56
|
-
// Disable Android 16 support by forcing older androidx versions
|
|
57
|
-
// Compatible with API 35 and AGP 8.8.2 (prevents API 36/AGP 8.9.1+ requirement)
|
|
58
|
-
force 'androidx.core:core-ktx:1.13.1'
|
|
59
|
-
force 'androidx.lifecycle:lifecycle-process:2.8.7'
|
|
60
|
-
}
|
|
61
|
-
}`;
|
|
62
|
-
|
|
63
|
-
// Add resolution strategy inside allprojects block
|
|
64
|
-
modResults.contents = modResults.contents.replace(
|
|
65
|
-
/allprojects\s*\{/,
|
|
66
|
-
`allprojects {${resolutionStrategy}`
|
|
75
|
+
config.modResults.contents = modifyProjectBuildGradleAndroid16Support(
|
|
76
|
+
config.modResults.contents,
|
|
77
|
+
{ disableAndroid16Support: shouldDisableAndroid16Support(config, androidOptions) }
|
|
67
78
|
);
|
|
68
|
-
|
|
69
79
|
return config;
|
|
70
80
|
});
|
|
71
81
|
}
|
|
@@ -7,19 +7,24 @@ import {
|
|
|
7
7
|
} from './../helpers/constants/android';
|
|
8
8
|
import type { CustomerIOPluginOptionsAndroid } from './../types/cio-types';
|
|
9
9
|
|
|
10
|
+
export function modifyProjectBuildGradleForGoogleServices(contents: string): string {
|
|
11
|
+
const regex = new RegExp(CIO_PROJECT_GOOGLE_SNIPPET);
|
|
12
|
+
if (regex.test(contents)) {
|
|
13
|
+
return contents;
|
|
14
|
+
}
|
|
15
|
+
return contents.replace(
|
|
16
|
+
CIO_PROJECT_BUILDSCRIPTS_REGEX,
|
|
17
|
+
`$1\n${CIO_PROJECT_GOOGLE_SNIPPET}`
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
10
21
|
export const withProjectGoogleServices: ConfigPlugin<
|
|
11
22
|
CustomerIOPluginOptionsAndroid
|
|
12
23
|
> = (configOuter) => {
|
|
13
24
|
return withProjectBuildGradle(configOuter, (props) => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
props.modResults.contents = props.modResults.contents.replace(
|
|
18
|
-
CIO_PROJECT_BUILDSCRIPTS_REGEX,
|
|
19
|
-
`$1\n${CIO_PROJECT_GOOGLE_SNIPPET}`
|
|
20
|
-
);
|
|
21
|
-
}
|
|
22
|
-
|
|
25
|
+
props.modResults.contents = modifyProjectBuildGradleForGoogleServices(
|
|
26
|
+
props.modResults.contents
|
|
27
|
+
);
|
|
23
28
|
return props;
|
|
24
29
|
});
|
|
25
30
|
};
|
|
@@ -42,73 +42,108 @@ export function buildHostAppPodSnippet(
|
|
|
42
42
|
return `pod 'customerio-reactnative', :subspecs => ['${pushSubspec}', 'location'], :path => '${resolvedPath}'`;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
const HOST_APP_BLOCK_START = '# --- CustomerIO Host App START ---';
|
|
46
|
+
const HOST_APP_BLOCK_END = '# --- CustomerIO Host App END ---';
|
|
47
|
+
const NOTIFICATION_BLOCK_START = '# --- CustomerIO Notification START ---';
|
|
48
|
+
const NOTIFICATION_BLOCK_END = '# --- CustomerIO Notification END ---';
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Pure string transform: given the existing Podfile contents, returns the
|
|
52
|
+
* Podfile with the CustomerIO host-app block injected before the Expo
|
|
53
|
+
* `post_install do |installer|` anchor. Idempotent — returns input unchanged
|
|
54
|
+
* if the block is already present.
|
|
55
|
+
*/
|
|
56
|
+
export function injectHostAppPodfileCode(
|
|
57
|
+
podfileContent: string,
|
|
46
58
|
iosPath: string,
|
|
47
59
|
isFcmPushProvider: boolean,
|
|
48
60
|
options?: InjectCIOPodfileOptions
|
|
49
|
-
) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const filename = `${iosPath}/Podfile`;
|
|
54
|
-
const podfile = await FileManagement.read(filename);
|
|
55
|
-
const matches = podfile.match(new RegExp(blockStart));
|
|
56
|
-
|
|
57
|
-
if (!matches) {
|
|
58
|
-
// We need to decide what line of code in the Podfile to insert our native code.
|
|
59
|
-
// The "post_install" line is always present in an Expo project Podfile so it's reliable.
|
|
60
|
-
// Find that line in the Podfile and then we will insert our code above that line.
|
|
61
|
-
const lineInPodfileToInjectSnippetBefore = /post_install do \|installer\|/;
|
|
61
|
+
): string {
|
|
62
|
+
if (podfileContent.match(new RegExp(HOST_APP_BLOCK_START))) {
|
|
63
|
+
return podfileContent;
|
|
64
|
+
}
|
|
62
65
|
|
|
63
|
-
|
|
66
|
+
// We need to decide what line of code in the Podfile to insert our native code.
|
|
67
|
+
// The "post_install" line is always present in an Expo project Podfile so it's reliable.
|
|
68
|
+
// Find that line in the Podfile and then we will insert our code above that line.
|
|
69
|
+
const lineInPodfileToInjectSnippetBefore = /post_install do \|installer\|/;
|
|
70
|
+
const podLine = buildHostAppPodSnippet(iosPath, isFcmPushProvider, options);
|
|
64
71
|
|
|
65
|
-
|
|
66
|
-
${
|
|
72
|
+
const snippetToInjectInPodfile = `
|
|
73
|
+
${HOST_APP_BLOCK_START}
|
|
67
74
|
${podLine}
|
|
68
|
-
${
|
|
75
|
+
${HOST_APP_BLOCK_END}
|
|
69
76
|
`.trim();
|
|
70
77
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
snippetToInjectInPodfile
|
|
77
|
-
).join('\n')
|
|
78
|
-
);
|
|
79
|
-
} else {
|
|
80
|
-
logger.info('CustomerIO Podfile snippets already exists. Skipping...');
|
|
81
|
-
}
|
|
78
|
+
return injectCodeByRegex(
|
|
79
|
+
podfileContent,
|
|
80
|
+
lineInPodfileToInjectSnippetBefore,
|
|
81
|
+
snippetToInjectInPodfile,
|
|
82
|
+
).join('\n');
|
|
82
83
|
}
|
|
83
84
|
|
|
84
|
-
export async function
|
|
85
|
+
export async function injectCIOPodfileCode(
|
|
85
86
|
iosPath: string,
|
|
86
|
-
|
|
87
|
-
|
|
87
|
+
isFcmPushProvider: boolean,
|
|
88
|
+
options?: InjectCIOPodfileOptions
|
|
88
89
|
) {
|
|
89
90
|
const filename = `${iosPath}/Podfile`;
|
|
90
91
|
const podfile = await FileManagement.read(filename);
|
|
92
|
+
const next = injectHostAppPodfileCode(podfile, iosPath, isFcmPushProvider, options);
|
|
93
|
+
if (next !== podfile) {
|
|
94
|
+
FileManagement.write(filename, next);
|
|
95
|
+
} else {
|
|
96
|
+
logger.info('CustomerIO Podfile snippets already exists. Skipping...');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
91
99
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
/**
|
|
101
|
+
* Pure string transform: given the existing Podfile contents, returns the
|
|
102
|
+
* Podfile with the rich-push NotificationService target block appended at
|
|
103
|
+
* the end. Idempotent — returns input unchanged if the block is already
|
|
104
|
+
* present.
|
|
105
|
+
*/
|
|
106
|
+
export function appendNotificationTargetToPodfile(
|
|
107
|
+
podfileContent: string,
|
|
108
|
+
iosPath: string,
|
|
109
|
+
isFcmPushProvider: boolean,
|
|
110
|
+
useFrameworks: CustomerIOPluginOptionsIOS['useFrameworks'],
|
|
111
|
+
): string {
|
|
112
|
+
if (podfileContent.match(new RegExp(NOTIFICATION_BLOCK_START))) {
|
|
113
|
+
return podfileContent;
|
|
114
|
+
}
|
|
102
115
|
|
|
103
|
-
|
|
104
|
-
${
|
|
116
|
+
const snippetToAppend = `
|
|
117
|
+
${NOTIFICATION_BLOCK_START}
|
|
105
118
|
target 'NotificationService' do
|
|
106
|
-
${
|
|
107
|
-
pod 'customerio-reactnative-richpush/${
|
|
119
|
+
${useFrameworks === 'static' ? 'use_frameworks! :linkage => :static' : ''}
|
|
120
|
+
pod 'customerio-reactnative-richpush/${isFcmPushProvider ? 'fcm' : 'apn'}', :path => '${getRelativePathToRNSDK(iosPath)}'
|
|
108
121
|
end
|
|
109
|
-
${
|
|
122
|
+
${NOTIFICATION_BLOCK_END}
|
|
110
123
|
`.trim();
|
|
111
124
|
|
|
112
|
-
|
|
125
|
+
// Mirror FileManagement.append: append directly with no separator (real
|
|
126
|
+
// Podfiles end with a trailing newline, so the appended block starts on a
|
|
127
|
+
// fresh line in practice).
|
|
128
|
+
return `${podfileContent}${snippetToAppend}`;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export async function injectCIONotificationPodfileCode(
|
|
132
|
+
iosPath: string,
|
|
133
|
+
useFrameworks: CustomerIOPluginOptionsIOS['useFrameworks'],
|
|
134
|
+
isFcmPushProvider: boolean
|
|
135
|
+
) {
|
|
136
|
+
const filename = `${iosPath}/Podfile`;
|
|
137
|
+
const podfile = await FileManagement.read(filename);
|
|
138
|
+
const next = appendNotificationTargetToPodfile(
|
|
139
|
+
podfile,
|
|
140
|
+
iosPath,
|
|
141
|
+
isFcmPushProvider,
|
|
142
|
+
useFrameworks,
|
|
143
|
+
);
|
|
144
|
+
if (next !== podfile) {
|
|
145
|
+
// FileManagement.append matches what the previous direct-append did.
|
|
146
|
+
// Slice off the leading content (already on disk) and append only the new tail.
|
|
147
|
+
FileManagement.append(filename, next.slice(podfile.length));
|
|
113
148
|
}
|
|
114
149
|
}
|
|
@@ -153,9 +153,12 @@ const addFirebaseDelegateForwardDeclarationIfNeeded = (
|
|
|
153
153
|
return stringContents;
|
|
154
154
|
};
|
|
155
155
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
156
|
+
/**
|
|
157
|
+
* Pure string transform: ensures the AppDelegate header (Objective-C path) declares
|
|
158
|
+
* `UNUserNotificationCenterDelegate` and imports `UserNotifications`. Idempotent.
|
|
159
|
+
*/
|
|
160
|
+
export function modifyAppDelegateHeader(headerContent: string): string {
|
|
161
|
+
return headerContent.replace(
|
|
159
162
|
CIO_APPDELEGATEHEADER_REGEX,
|
|
160
163
|
(match, interfaceDeclaration, _groupedDelegates, existingDelegates) => {
|
|
161
164
|
if (
|
|
@@ -179,9 +182,7 @@ ${interfaceDeclaration.trim()} <${CIO_APPDELEGATEHEADER_USER_NOTIFICATION_CENTER
|
|
|
179
182
|
}
|
|
180
183
|
}
|
|
181
184
|
);
|
|
182
|
-
|
|
183
|
-
return stringContents;
|
|
184
|
-
};
|
|
185
|
+
}
|
|
185
186
|
|
|
186
187
|
const addHandleDeeplinkInKilledState = (stringContents: string) => {
|
|
187
188
|
// Find if the deep link code snippet is already present
|
|
@@ -219,59 +220,71 @@ const addHandleDeeplinkInKilledState = (stringContents: string) => {
|
|
|
219
220
|
return stringContents;
|
|
220
221
|
};
|
|
221
222
|
|
|
223
|
+
/**
|
|
224
|
+
* Pure string transform: produces the modified Objective-C AppDelegate.m / AppDelegate.mm
|
|
225
|
+
* contents wired with the Customer.io push pipeline (imports, declarations, notification
|
|
226
|
+
* configuration, registration callbacks, optional killed-state deep-link, FCM forward decl,
|
|
227
|
+
* Expo notifications header). The caller is responsible for the AppDelegate header file
|
|
228
|
+
* (.h) — see `modifyAppDelegateHeader`.
|
|
229
|
+
*/
|
|
230
|
+
export function modifyAppDelegateContents(
|
|
231
|
+
contents: string,
|
|
232
|
+
projectName: string,
|
|
233
|
+
props: CustomerIOPluginOptionsIOS
|
|
234
|
+
): string {
|
|
235
|
+
let next = addImport(contents, projectName);
|
|
236
|
+
next = addNotificationHandlerDeclaration(next);
|
|
237
|
+
|
|
238
|
+
// unless this property is explicity set to true, push notification
|
|
239
|
+
// registration will be added to the AppDelegate
|
|
240
|
+
if (props.pushNotification?.disableNotificationRegistration !== true) {
|
|
241
|
+
next = addNotificationConfiguration(next);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
next = addInitializeNativeCioSdk(next);
|
|
245
|
+
|
|
246
|
+
if (props.pushNotification?.handleDeeplinkInKilledState === true) {
|
|
247
|
+
next = addHandleDeeplinkInKilledState(next);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
next = addDidFailToRegisterForRemoteNotificationsWithError(next);
|
|
251
|
+
next = addDidRegisterForRemoteNotificationsWithDeviceToken(next);
|
|
252
|
+
|
|
253
|
+
if (isFcmPushProvider(props)) {
|
|
254
|
+
next = addFirebaseDelegateForwardDeclarationIfNeeded(next);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
next = addExpoNotificationsHeaderModification(next);
|
|
258
|
+
|
|
259
|
+
return next;
|
|
260
|
+
}
|
|
261
|
+
|
|
222
262
|
export const withAppDelegateModifications: ConfigPlugin<
|
|
223
263
|
CustomerIOPluginOptionsIOS
|
|
224
264
|
> = (configOuter, props) => {
|
|
225
265
|
return withAppDelegate(configOuter, async (config) => {
|
|
226
|
-
|
|
266
|
+
const stringContents = config.modResults.contents;
|
|
227
267
|
const regex = new RegExp(
|
|
228
268
|
`#import <${config.modRequest.projectName}-Swift.h>`
|
|
229
269
|
);
|
|
230
|
-
const match = stringContents.match(regex);
|
|
231
|
-
|
|
232
|
-
if (!match) {
|
|
233
|
-
const headerPath = getAppDelegateHeaderFilePath(
|
|
234
|
-
config.modRequest.projectRoot
|
|
235
|
-
);
|
|
236
|
-
let headerContent = await FileManagement.read(headerPath);
|
|
237
|
-
headerContent = addAppdelegateHeaderModification(headerContent);
|
|
238
|
-
FileManagement.write(headerPath, headerContent);
|
|
239
|
-
|
|
240
|
-
stringContents = addImport(
|
|
241
|
-
stringContents,
|
|
242
|
-
config.modRequest.projectName as string
|
|
243
|
-
);
|
|
244
|
-
stringContents = addNotificationHandlerDeclaration(stringContents);
|
|
245
|
-
|
|
246
|
-
// unless this property is explicity set to true, push notification
|
|
247
|
-
// registration will be added to the AppDelegate
|
|
248
|
-
if (props.pushNotification?.disableNotificationRegistration !== true) {
|
|
249
|
-
stringContents = addNotificationConfiguration(stringContents);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
stringContents = addInitializeNativeCioSdk(stringContents);
|
|
253
|
-
|
|
254
|
-
if (props.pushNotification?.handleDeeplinkInKilledState === true) {
|
|
255
|
-
stringContents = addHandleDeeplinkInKilledState(stringContents);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
stringContents =
|
|
259
|
-
addDidFailToRegisterForRemoteNotificationsWithError(stringContents);
|
|
260
|
-
stringContents =
|
|
261
|
-
addDidRegisterForRemoteNotificationsWithDeviceToken(stringContents);
|
|
262
|
-
|
|
263
|
-
if (isFcmPushProvider(props)) {
|
|
264
|
-
stringContents =
|
|
265
|
-
addFirebaseDelegateForwardDeclarationIfNeeded(stringContents);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
stringContents = addExpoNotificationsHeaderModification(stringContents);
|
|
269
270
|
|
|
270
|
-
|
|
271
|
-
} else {
|
|
271
|
+
if (stringContents.match(regex)) {
|
|
272
272
|
logger.info('Customerio AppDelegate changes already exist. Skipping...');
|
|
273
|
+
return config;
|
|
273
274
|
}
|
|
274
275
|
|
|
276
|
+
const headerPath = getAppDelegateHeaderFilePath(
|
|
277
|
+
config.modRequest.projectRoot
|
|
278
|
+
);
|
|
279
|
+
const headerContent = await FileManagement.read(headerPath);
|
|
280
|
+
FileManagement.write(headerPath, modifyAppDelegateHeader(headerContent));
|
|
281
|
+
|
|
282
|
+
config.modResults.contents = modifyAppDelegateContents(
|
|
283
|
+
stringContents,
|
|
284
|
+
config.modRequest.projectName as string,
|
|
285
|
+
props
|
|
286
|
+
);
|
|
287
|
+
|
|
275
288
|
return config;
|
|
276
289
|
});
|
|
277
290
|
};
|