UNPKG

react-native-expo-moengage

Version:

MoEngage Expo plugin for integrating MoEngage React-Native SDK

276 lines (275 loc) 15.7 kB
"use strict"; 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;