react-native-expo-moengage
Version:
MoEngage Expo plugin for integrating MoEngage React-Native SDK
276 lines (275 loc) • 15.7 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.withMoEngageDangerousMod = void 0;
const config_plugins_1 = require("@expo/config-plugins");
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const constants_1 = require("./constants");
const plist = require('plist');
/**
* MoEngage Expo plugin for iOS - Dangerous modifications
*
* This plugin handles file operations that are not directly exposed by the Expo Config Plugin API,
* including copying Rich Push, Push Templates, and LiveActivity files, and updating the Podfile
* to include the necessary MoEngage pods for each target.
*
* @param config - The Expo config object
* @param props - The MoEngage plugin properties
* @returns The updated config object
*/
const withMoEngageDangerousMod = (config, props) => {
return (0, config_plugins_1.withDangerousMod)(config, [
'ios',
(config) => {
var _a, _b, _c, _d, _e, _f, _g;
if (process.env['EXPO_TV']) {
// Skip modifications for tvOS
console.log(`Skipping extension targets copy for tvOS`);
return config;
}
const applicationPods = [];
const projectRoot = config.modRequest.projectRoot;
const { apple } = props;
const shouldAddRichPushExtension = apple.richPushNotificationEnabled || apple.pushNotificationImpressionTrackingEnabled || apple.pushTemplatesEnabled;
/**
* Extract the KeychainGroupName from the MoEngage configuration file
*
* The keychain group is needed for secure data sharing between the main app
* and extension targets. If present, it's added to the entitlements files
* for the extension targets.
*/
let keychainGroupValue = '';
try {
const configFilePath = path.join(config.modRequest.projectRoot, props.apple.configFilePath);
if (fs.existsSync(configFilePath)) {
const configPlist = plist.parse(fs.readFileSync(configFilePath, 'utf8'));
keychainGroupValue = configPlist['KeychainGroupName'];
}
else {
const message = `MoEngage configuration does not exist`;
console.error(message);
throw new Error(message);
}
}
catch (e) {
const message = `Could not import MoEngage configuration: ${e}`;
console.error(message);
throw new Error(message);
}
/**
* Set up the Rich Push Notification Service Extension
*
* This extension handles rich push notifications with media attachments and
* custom notification UI. We:
* 1. Create the extension directory if it doesn't exist
* 2. Copy template files from the plugin's resources
* 3. Update entitlements with keychain access if configured
* 4. Modify the Podfile to include the MoEngage rich notification dependency
*/
if (shouldAddRichPushExtension) {
// Copy Rich Push files to project path.
const absoluteSource = require.resolve('react-native-expo-moengage/apple/RichPush/NotificationService.swift');
const sourcePath = path.dirname(absoluteSource);
const destinationPath = `${projectRoot}/ios/${constants_1.MOENGAGE_IOS_RICH_PUSH_TARGET}`;
if (!fs.existsSync(`${destinationPath}`)) {
fs.mkdirSync(`${destinationPath}`, { recursive: true });
}
for (const file of constants_1.MOENGAGE_IOS_RICH_PUSH_FILES) {
// Copy keychain group to extension entitlements
if (keychainGroupValue && keychainGroupValue.length > 0 && file.endsWith('.entitlements')) {
const entitlements = plist.parse(fs.readFileSync(`${sourcePath}/${file}`, 'utf8'));
entitlements['keychain-access-groups'] = [keychainGroupValue];
fs.writeFileSync(`${destinationPath}/${file}`, plist.build(entitlements));
continue;
}
fs.copyFileSync(`${sourcePath}/${file}`, `${destinationPath}/${file}`);
}
// Modify Podfile to include `MoEngageRichNotification`.
const podfilePath = `${projectRoot}/ios/Podfile`;
if (fs.existsSync(podfilePath)) {
const podfile = fs.readFileSync(podfilePath, 'utf8');
if (!podfile.includes(constants_1.MOENGAGE_IOS_RICH_PUSH_TARGET) || !podfile.includes(constants_1.MOENGAGE_IOS_NOTIFICATION_SERVICE_POD)) {
const notificationServiceTarget = [
``,
`target '${constants_1.MOENGAGE_IOS_RICH_PUSH_TARGET}' do`,
` use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks']`,
` use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS']`,
` pod '${constants_1.MOENGAGE_IOS_NOTIFICATION_SERVICE_POD}'`,
`end`
].join('\n');
fs.appendFileSync(podfilePath, notificationServiceTarget);
}
}
}
/**
* Set up the Push Templates Notification Content Extension
*
* This extension enables custom notification UI templates for push notifications.
* The setup process includes:
* 1. Creating the extension directory if it doesn't exist
* 2. Copying template files from the plugin's resources
* 3. Updating entitlements with keychain access if configured
* 4. Modifying the Podfile to include the MoEngage push template dependency
*/
if (apple.pushTemplatesEnabled) {
// Copy Push Template files to project path.
const absoluteSource = require.resolve('react-native-expo-moengage/apple/PushTemplates/NotificationViewController.swift');
const sourcePath = path.dirname(absoluteSource);
const destinationPath = `${projectRoot}/ios/${constants_1.MOENGAGE_IOS_PUSH_TEMPLATE_TARGET}`;
if (!fs.existsSync(`${destinationPath}`)) {
fs.mkdirSync(`${destinationPath}`, { recursive: true });
}
for (const file of constants_1.MOENGAGE_IOS_PUSH_TEMPLATE_FILES) {
// Copy keychain group to extension entitlements
if (keychainGroupValue && keychainGroupValue.length > 0 && file.endsWith('.entitlements')) {
const entitlements = plist.parse(fs.readFileSync(`${sourcePath}/${file}`, 'utf8'));
entitlements['keychain-access-groups'] = [keychainGroupValue];
fs.writeFileSync(`${destinationPath}/${file}`, plist.build(entitlements));
continue;
}
fs.copyFileSync(`${sourcePath}/${file}`, `${destinationPath}/${file}`);
}
// Modify Podfile to include `MoEngageRichNotification`.
const podfilePath = `${projectRoot}/ios/Podfile`;
if (fs.existsSync(podfilePath)) {
const podfile = fs.readFileSync(podfilePath, 'utf8');
if (!podfile.includes(constants_1.MOENGAGE_IOS_PUSH_TEMPLATE_TARGET) || !podfile.includes(constants_1.MOENGAGE_IOS_PUSH_TEMPLATE_POD)) {
const pushTemplateTarget = [
``,
`target '${constants_1.MOENGAGE_IOS_PUSH_TEMPLATE_TARGET}' do`,
` use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks']`,
` use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS']`,
` pod '${constants_1.MOENGAGE_IOS_PUSH_TEMPLATE_POD}'`,
`end`
].join('\n');
fs.appendFileSync(podfilePath, pushTemplateTarget);
}
}
}
/**
* Set up Live Activity support
*
* Live Activities allow apps to display persistent, interactive notifications
* on the lock screen and in the Dynamic Island on supported devices.
*
* This section:
* 1. Adds the MoEngageLiveActivity pod to the main app target
* 2. Creates a LiveActivity extension target in the Podfile if needed
* 3. Configures the pod dependencies for the LiveActivity extension
*/
if (apple.liveActivityTargetPath && apple.liveActivityTargetPath.length) {
// Add MoEngageLiveActivity pod to the Podfile
applicationPods.push(` pod '${constants_1.MOENGAGE_IOS_LIVE_ACTIVITY_POD}'`);
const podfilePath = path.join(projectRoot, 'ios', 'Podfile');
if (fs.existsSync(podfilePath)) {
const podfile = fs.readFileSync(podfilePath, 'utf8');
if (!podfile.includes(constants_1.MOENGAGE_IOS_LIVE_ACTIVITY_TARGET)) {
const liveActivityTarget = [
``,
`target '${constants_1.MOENGAGE_IOS_LIVE_ACTIVITY_TARGET}' do`,
` use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks']`,
` use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS']`,
` pod '${constants_1.MOENGAGE_IOS_LIVE_ACTIVITY_POD}'`,
`end`
].join('\n');
fs.appendFileSync(podfilePath, liveActivityTarget);
console.log(`Added MoEngageLiveActivity pod to Podfile for ${constants_1.MOENGAGE_IOS_LIVE_ACTIVITY_TARGET} target`);
}
}
}
/**
* Set up Device Trigger support
*
* Device Triggers allow campaigns to be triggered based on device events,
* without requiring server communication. This is useful for real-time
* engagement based on user actions within the app.
*
* If enabled, we add the MoEngage RealTimeTrigger pod to the main application.
*/
if (apple.deviceTriggerEnabled) {
applicationPods.push(` pod '${constants_1.MOENGAGE_IOS_DEVICE_TRIGGER_POD}'`);
}
/**
* Update the main application's Podfile with MoEngage dependencies
*
* This section adds any accumulated MoEngage pods to the main application target
* in the Podfile. It:
* 1. Finds the main application target section in the Podfile
* 2. Locates a suitable insertion point before existing dependencies
* 3. Inserts the MoEngage pod declarations
*/
if (applicationPods && applicationPods.length) {
const podfilePath = `${projectRoot}/ios/Podfile`;
if (fs.existsSync(podfilePath)) {
const podfile = fs.readFileSync(podfilePath, 'utf8');
if (!podfile.includes(constants_1.MOENGAGE_IOS_DEVICE_TRIGGER_POD)) {
// Find the main target and add the pod there
const lines = podfile.split('\n');
let mainTargetIndex = -1;
// Find the main application target
for (let i = 0; i < lines.length; i++) {
if (((_a = lines[i]) === null || _a === void 0 ? void 0 : _a.includes('target')) && ((_b = lines[i]) === null || _b === void 0 ? void 0 : _b.includes('do')) &&
!((_c = lines[i]) === null || _c === void 0 ? void 0 : _c.includes(constants_1.MOENGAGE_IOS_RICH_PUSH_TARGET)) &&
!((_d = lines[i]) === null || _d === void 0 ? void 0 : _d.includes(constants_1.MOENGAGE_IOS_PUSH_TEMPLATE_TARGET))) {
mainTargetIndex = i;
break;
}
}
if (mainTargetIndex !== -1) {
// Find the end of the dependency section
let dependenciesEndIndex = -1;
for (let i = mainTargetIndex; i < lines.length; i++) {
if (((_e = lines[i]) === null || _e === void 0 ? void 0 : _e.includes('use_native_modules')) ||
((_f = lines[i]) === null || _f === void 0 ? void 0 : _f.includes('use_react_native')) ||
((_g = lines[i]) === null || _g === void 0 ? void 0 : _g.includes('post_install'))) {
dependenciesEndIndex = i;
break;
}
}
if (dependenciesEndIndex !== -1) {
// Insert the application pods before the end of the target
lines.splice(dependenciesEndIndex, 0, ...applicationPods);
fs.writeFileSync(podfilePath, lines.join('\n'));
}
}
}
}
}
return config;
},
]);
};
exports.withMoEngageDangerousMod = withMoEngageDangerousMod;