UNPKG

smartech-base-expo-plugin

Version:

Smartech Base Expo SDK's React Native Plugin For React Native Projects.

444 lines (443 loc) 23.9 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.withNetcoreiOS = void 0; const config_plugins_1 = require("@expo/config-plugins"); const fs_1 = require("fs"); const path_1 = __importDefault(require("path")); const iosConstants_1 = require("./iosConstants"); const helper_1 = require("./helper"); const addURLSchemeEntry = (existingURLTypes, urlName, urlSchema) => { const newEntry = { CFBundleTypeRole: "Editor", CFBundleURLName: urlName, CFBundleURLSchemes: [urlSchema], }; const isNewEntryAlreadyAdded = existingURLTypes.some((entry) => (entry.CFBundleURLName === newEntry.CFBundleURLName && entry.CFBundleURLSchemes.includes(newEntry.CFBundleURLSchemes[0]))); if (!isNewEntryAlreadyAdded) { existingURLTypes.push(newEntry); } return existingURLTypes; }; // Set custom entitlements plist const modifyEntitlementsPlist = (config, props) => { return (0, config_plugins_1.withEntitlementsPlist)(config, async (newConfig) => { newConfig.modResults = { ...newConfig.modResults, "com.apple.security.application-groups": [`group.${props.ios.groupIdentifier}`], }; return newConfig; }); }; // Set custom info plist const modifyInfoPlist = (config, props) => { return (0, config_plugins_1.withInfoPlist)(config, (newConfig) => { newConfig.modResults = { ...newConfig.modResults, SmartechKeys: { SmartechAppGroup: `group.${props.ios.groupIdentifier}`, SmartechAppId: props.ios.appId, SmartechAutoFetchLocation: props.ios.autoFetchLocation, SmartechUseAdvId: props.ios.useAdvID, }, ...(props.ios.smartechNudges.smartechNudgesEnabled && { HanselKeys: { HanselAppId: props.ios.smartechNudges.appId, HanselAppKey: props.ios.smartechNudges.appKey, } }) }; return newConfig; }); }; const addSmartechURLScheme = (config, props) => { return (0, config_plugins_1.withInfoPlist)(config, (newConfig) => { if (!props.ios.addTestDevice) { return newConfig; } if (props.ios.testDeviceURLName == undefined || props.ios.testDeviceURLSchema == undefined) { console.log(`Smartech Url scheme ${props.ios.testDeviceURLName} and ${props.ios.testDeviceURLSchema}`); return newConfig; } const modResults = newConfig.modResults; const existingURLTypes = (modResults.CFBundleURLTypes || []); modResults.CFBundleURLTypes = addURLSchemeEntry(existingURLTypes, props.ios.testDeviceURLName, props.ios.testDeviceURLSchema); return newConfig; }); }; const addHanselURLScheme = (config, props) => { return (0, config_plugins_1.withInfoPlist)(config, (newConfig) => { if (!props.ios.smartechNudges.smartechNudgesEnabled || !props.ios.smartechNudges.addTestDevice) { return newConfig; } if (props.ios.smartechNudges?.testDeviceURLName == undefined || props.ios.smartechNudges?.testDeviceURLSchema == undefined) { console.log(`Smartech Nudge Url scheme ${props.ios.smartechNudges?.testDeviceURLName} and ${props.ios.smartechNudges?.testDeviceURLSchema}`); return newConfig; } const modResults = newConfig.modResults; const existingURLTypes = (modResults.CFBundleURLTypes || []); modResults.CFBundleURLTypes = addURLSchemeEntry(existingURLTypes, props.ios.smartechNudges.testDeviceURLName, props.ios.smartechNudges.testDeviceURLSchema); return newConfig; }); }; const addSmartechHeaderMod = (config, props) => { return (0, config_plugins_1.withDangerousMod)(config, [ 'ios', async (config) => { const { platformProjectRoot, projectName } = config.modRequest; if (projectName && platformProjectRoot) { const isSwift = await helper_1.Helper.Ios.isSwiftProject(platformProjectRoot, projectName); if (isSwift) { // For Swift projects, add imports to AppDelegate.swift try { const appDelegateSwiftPath = path_1.default.join(platformProjectRoot, projectName, 'AppDelegate.swift'); const appDelegateSwiftContents = await fs_1.promises.readFile(appDelegateSwiftPath, 'utf-8'); //console.log(`appdelegate content before is ${appDelegateSwiftContents}`); console.log(`appdelegate path is ${appDelegateSwiftPath}`); // Add Smartech import if not already present (check for exact import line) const lines = appDelegateSwiftContents.split('\n'); const hasSmartechImport = lines.some(line => line.trim() === iosConstants_1.swiftImports.smartechImport); if (!hasSmartechImport) { let insertIndex = 0; // Find the last import statement for (let i = 0; i < lines.length; i++) { if (lines[i].trim().startsWith('import ')) { insertIndex = i + 1; } else if (lines[i].trim() !== '' && !lines[i].trim().startsWith('import ')) { break; } } // Insert the Smartech import lines.splice(insertIndex, 0, iosConstants_1.swiftImports.smartechImport); const modifiedContents = lines.join('\n'); //console.log(`appdelegate content is ${modifiedContents}`); await fs_1.promises.writeFile(appDelegateSwiftPath, modifiedContents, 'utf-8'); console.log(`${iosConstants_1.swiftImports.smartechImport} added to AppDelegate.swift at line ${insertIndex + 1}`); } else { console.log(`${iosConstants_1.swiftImports.smartechImport} is already added to AppDelegate.swift`); } } catch (error) { console.error('Error modifying AppDelegate.swift:', error); } } else { // For Objective-C projects (existing logic) try { const appDelegateHeaderPath = path_1.default.join(platformProjectRoot, projectName, iosConstants_1.appdelegateSnippet.fileName); const appDelegateHeaderContents = await fs_1.promises.readFile(appDelegateHeaderPath, 'utf-8'); // Add Smartech header snipper if not already present. if (!appDelegateHeaderContents.includes(iosConstants_1.appdelegateSnippet.smartechHeaderSnippet)) { const modifiedAppDelegateHeaderContents = `${appDelegateHeaderContents}\n${iosConstants_1.appdelegateSnippet.smartechHeaderSnippet}`; await fs_1.promises.writeFile(appDelegateHeaderPath, modifiedAppDelegateHeaderContents, 'utf-8'); } else { console.log(`${iosConstants_1.appdelegateSnippet.smartechHeaderSnippet} is already added.`); } } catch (error) { console.error('Error modifying AppDelegate.h:', error); } } } return config; } ]); }; const addSmartechHanseleHeaderMod = (config, props) => { return (0, config_plugins_1.withDangerousMod)(config, [ 'ios', async (config) => { const { platformProjectRoot, projectName } = config.modRequest; if (platformProjectRoot && projectName) { const isSwift = await helper_1.Helper.Ios.isSwiftProject(platformProjectRoot, projectName); if (isSwift) { // For Swift projects, add imports to AppDelegate.swift try { const appDelegateSwiftPath = path_1.default.join(platformProjectRoot, projectName, 'AppDelegate.swift'); // Re-read the file to get the current state (including any changes from previous functions) const appDelegateSwiftContents = await fs_1.promises.readFile(appDelegateSwiftPath, 'utf-8'); // console.log(`appdelegate content hansel before is ${appDelegateSwiftContents}`); // Add Hansel import if Hansel is enabled and not already present (check for exact import line) const lines = appDelegateSwiftContents.split('\n'); const hasHanselImport = lines.some(line => line.trim() === iosConstants_1.swiftImports.hanselImport); if (props.ios.smartechNudges.smartechNudgesEnabled && !hasHanselImport) { let insertIndex = 0; // Find the last import statement for (let i = 0; i < lines.length; i++) { if (lines[i].trim().startsWith('import ')) { insertIndex = i + 1; } else if (lines[i].trim() !== '' && !lines[i].trim().startsWith('import ')) { break; } } // Insert the Hansel import lines.splice(insertIndex, 0, iosConstants_1.swiftImports.hanselImport); const modifiedContents = lines.join('\n'); // console.log(`appdelegate content hansel after is ${modifiedContents}`); await fs_1.promises.writeFile(appDelegateSwiftPath, modifiedContents, 'utf-8'); console.log(`${iosConstants_1.swiftImports.hanselImport} added to AppDelegate.swift at line ${insertIndex + 1}`); } else { console.log(`${iosConstants_1.swiftImports.hanselImport} is already added or SmartechNudges disabled: ${props.ios.smartechNudges.smartechNudgesEnabled}`); } } catch (error) { console.error('Error modifying AppDelegate.swift:', error); } } else { // For Objective-C projects (existing logic) try { const appDelegateHeaderPath = path_1.default.join(platformProjectRoot, projectName, iosConstants_1.appdelegateSnippet.fileName); const appDelegateHeaderContents = await fs_1.promises.readFile(appDelegateHeaderPath, 'utf-8'); // Add Hansel header snippet if Hansel is enabled and not already present if (props.ios.smartechNudges.smartechNudgesEnabled && !appDelegateHeaderContents.includes(iosConstants_1.appdelegateSnippet.hanselHeaderSnippet)) { const modifiedAppDelegateHeaderContents = `${appDelegateHeaderContents}\n${iosConstants_1.appdelegateSnippet.hanselHeaderSnippet}`; await fs_1.promises.writeFile(appDelegateHeaderPath, modifiedAppDelegateHeaderContents, 'utf-8'); } else { console.log(`${iosConstants_1.appdelegateSnippet.hanselHeaderSnippet} is already added or status of SmartechNudge: ${props.ios.smartechNudges.smartechNudgesEnabled}`); } } catch (error) { console.error('Error modifying AppDelegate.h:', error); } } } return config; } ]); }; // Set custom AppDelegate contents modifications const setCustomAppDelegateContentsMod = (config, props) => { return (0, config_plugins_1.withDangerousMod)(config, [ 'ios', async (config) => { const { platformProjectRoot, projectName } = config.modRequest; if (platformProjectRoot && projectName) { const isSwift = await helper_1.Helper.Ios.isSwiftProject(platformProjectRoot, projectName); if (isSwift) { // Handle Swift AppDelegate const appDelegateSwiftPath = path_1.default.join(platformProjectRoot, projectName, 'AppDelegate.swift'); try { let contents = await fs_1.promises.readFile(appDelegateSwiftPath, 'utf-8'); contents = addSmartechInitCodeSwift(contents, props); contents = addDeepLinkHandlerSwift(contents, props); await fs_1.promises.writeFile(appDelegateSwiftPath, contents, 'utf-8'); console.log('Swift AppDelegate contents updated successfully'); } catch (error) { console.error('Error modifying AppDelegate.swift:', error); } } else { // Handle Objective-C AppDelegate (existing logic) const appDelegatePath = await helper_1.Helper.Ios.getObjCAppDelegatePath(platformProjectRoot, projectName); try { let contents = await fs_1.promises.readFile(appDelegatePath, 'utf-8'); const smartechInitContent = addSmartechInitCode(contents, props); const updatedContent = helper_1.Helper.Ios.addDeepLinkHandler(smartechInitContent, props); await fs_1.promises.writeFile(appDelegatePath, updatedContent, 'utf-8'); console.log(`Objective-C AppDelegate contents updated successfully: ${appDelegatePath}`); } catch (error) { console.error(`Error modifying AppDelegate file (${appDelegatePath}):`, error); } } } return config; } ]); }; // Helper function to modify AppDelegate contents const addSmartechInitCode = (contents, props) => { const lines = contents.split("\n"); const didLaunchIndex = lines.findIndex(line => iosConstants_1.smartechBaseSnippet.regexPattern.test(line)); if (didLaunchIndex === -1) { return contents; } // Prepare the lines to insert const sdkLines = [ iosConstants_1.smartechBaseSnippet.smartechInit, ...(props.ios.isLogEnabled ? [iosConstants_1.smartechBaseSnippet.debugLevel] : []), iosConstants_1.smartechBaseSnippet.trackAppInstallUpdateBySmartech, ...((props.ios.smartechNudges.smartechNudgesEnabled && props.ios.smartechNudges.isLogEnabled) ? [iosConstants_1.smartechBaseSnippet.hanselLogSnippet] : []), ]; // Add the snippets before and after the `didFinishLaunching` index const resultLines = [ ...lines.slice(0, didLaunchIndex + 2), ...sdkLines, ...lines.slice(didLaunchIndex + 2), ]; return resultLines.join("\n"); }; // Helper function to modify Swift AppDelegate contents const addSmartechInitCodeSwift = (contents, props) => { const lines = contents.split("\n"); // Find didFinishLaunchingWithOptions method by looking for the specific method name let didLaunchIndex = -1; for (let i = 0; i < lines.length; i++) { const line = lines[i]; // Look for func application and then check if didFinishLaunchingWithOptions appears in the next few lines if (iosConstants_1.smartechBaseSwiftSnippet.didFinishLaunchingRegex.test(line)) { // Check the next 5 lines for didFinishLaunchingWithOptions for (let j = i; j < Math.min(i + 5, lines.length); j++) { if (lines[j].includes('didFinishLaunchingWithOptions')) { didLaunchIndex = i; break; } } if (didLaunchIndex !== -1) break; } } if (didLaunchIndex === -1) { console.log('Could not find didFinishLaunchingWithOptions method in Swift AppDelegate'); return contents; } // Find the opening brace of the method let methodStartIndex = didLaunchIndex; for (let i = didLaunchIndex; i < lines.length; i++) { if (lines[i].includes('{')) { methodStartIndex = i; break; } } // Prepare the lines to insert const sdkLines = [ ` ${iosConstants_1.smartechBaseSwiftSnippet.smartechInit}`, ...(props.ios.isLogEnabled ? [` ${iosConstants_1.smartechBaseSwiftSnippet.debugLevel}`] : []), ` ${iosConstants_1.smartechBaseSwiftSnippet.trackAppInstallUpdateBySmartech}`, ...((props.ios.smartechNudges.smartechNudgesEnabled && props.ios.smartechNudges.isLogEnabled) ? [` ${iosConstants_1.smartechBaseSwiftSnippet.hanselLogSnippet}`] : []), ]; // Add the snippets after the method opening brace const resultLines = [ ...lines.slice(0, methodStartIndex + 1), ...sdkLines, ...lines.slice(methodStartIndex + 1), ]; console.log('Swift SDK initialization code added to didFinishLaunchingWithOptions'); return resultLines.join("\n"); }; // Helper function to add Swift deeplink handler const addDeepLinkHandlerSwift = (contents, props) => { const lines = contents.split("\n"); // Find the end of the main AppDelegate class (before the last closing brace, but not the ReactNativeDelegate class) let classEndIndex = -1; let insideAppDelegate = false; let braceCount = 0; for (let i = 0; i < lines.length; i++) { const line = lines[i].trim(); // Look for the AppDelegate class declaration if (line.includes('class') && line.includes('AppDelegate') && line.includes(':')) { insideAppDelegate = true; braceCount = 0; } if (insideAppDelegate) { // Count braces to find the end of the AppDelegate class const openBraces = (line.match(/\{/g) || []).length; const closeBraces = (line.match(/\}/g) || []).length; braceCount += openBraces - closeBraces; // When we reach the closing brace of AppDelegate class if (braceCount === 0 && line.includes('}') && openBraces === 0) { classEndIndex = i; break; } } } if (classEndIndex === -1) { console.log('Could not find AppDelegate class end in Swift file'); return contents; } const deepLinkHandlerLines = (0, iosConstants_1.deepLinkHandlerSwiftSnippet)(props.deepLinkDelay); const resultLines = [ ...lines.slice(0, classEndIndex), deepLinkHandlerLines, ...lines.slice(classEndIndex), ]; return resultLines.join("\n"); }; // Helper function to modify Swift openURL method const modifySwiftOpenURLMethod = (contents) => { const swiftCodeBlock = `let handleBySmartech = Smartech.sharedInstance().application(app, open: url, options: options) if !handleBySmartech { return super.application(app, open: url, options: options) || RCTLinkingManager.application(app, open: url, options: options) } return true`; if (contents.includes("open url:")) { const modifiedContents = contents.replace("return super.application(app, open: url, options: options) || RCTLinkingManager.application(app, open: url, options: options)", swiftCodeBlock); return modifiedContents; } console.log('Could not find Swift openURL method to modify'); return contents; }; // Set custom AppDelegate contents modifications const modifyAppDelegateWithLinker = (config, props) => { const addTestDeviceForSmartech = props.ios.addTestDevice ?? false; const addTestDeviceForHansel = props.ios.smartechNudges.smartechNudgesEnabled && (props.ios.smartechNudges.addTestDevice ?? false); if (!addTestDeviceForSmartech && !addTestDeviceForHansel) { return config; } return (0, config_plugins_1.withDangerousMod)(config, [ 'ios', async (config) => { const { platformProjectRoot, projectName } = config.modRequest; if (platformProjectRoot && projectName) { const isSwift = await helper_1.Helper.Ios.isSwiftProject(platformProjectRoot, projectName); if (isSwift) { // Handle Swift AppDelegate const appDelegateSwiftPath = path_1.default.join(platformProjectRoot, projectName, 'AppDelegate.swift'); try { let contents = await fs_1.promises.readFile(appDelegateSwiftPath, 'utf-8'); contents = modifySwiftOpenURLMethod(contents); await fs_1.promises.writeFile(appDelegateSwiftPath, contents, 'utf-8'); console.log('Swift AppDelegate openURL method updated successfully'); } catch (error) { console.error('Error modifying Swift AppDelegate openURL method:', error); } } else { // Handle Objective-C AppDelegate (existing logic) const appDelegatePath = await helper_1.Helper.Ios.getObjCAppDelegatePath(platformProjectRoot, projectName); try { let contents = await fs_1.promises.readFile(appDelegatePath, 'utf-8'); const codeBlock = `BOOL handleBySmartech = [[Smartech sharedInstance] application:application openURL:url options:options]; if(!handleBySmartech) { return [super application:application openURL:url options:options] || [RCTLinkingManager application:application openURL:url options:options]; } return YES;`; if (contents.includes("openURL:")) { const modifiedContents = contents.replace("return [super application:application openURL:url options:options] || [RCTLinkingManager application:application openURL:url options:options];", `${codeBlock}`); await fs_1.promises.writeFile(appDelegatePath, modifiedContents, 'utf-8'); } } catch (error) { console.error('Error modifying Objective-C AppDelegate openURL method:', error); } } } return config; } ]); }; const withNetcoreiOS = (config, props) => { config = modifyEntitlementsPlist(config, props); config = modifyInfoPlist(config, props); config = addSmartechURLScheme(config, props); config = addHanselURLScheme(config, props); config = addSmartechHeaderMod(config, props); config = addSmartechHanseleHeaderMod(config, props); config = setCustomAppDelegateContentsMod(config, props); config = modifyAppDelegateWithLinker(config, props); return config; }; exports.withNetcoreiOS = withNetcoreiOS;