react-native-expo-braintree
Version:
React native and expo wrapper around braintree sdk fro android and ios
212 lines (194 loc) • 10.6 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.withVenmoScheme = exports.withExpoBraintreePlist = exports.withExpoBraintreeAppDelegate = exports.withBraintreeWrapperFile = exports.modifyAppDelegateSwift = exports.modifyAppDelegateObjectiveC = void 0;
var _configPlugins = require("@expo/config-plugins");
var _eol = _interopRequireDefault(require("eol"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
/* eslint-disable no-bitwise */
/*
* Mods for Info.plist
*/
const withExpoBraintreePlist = expoConfig => {
return (0, _configPlugins.withInfoPlist)(expoConfig, config => {
const bundleIdentifier = config.ios?.bundleIdentifier ?? '';
const bundleIdentifierWithBraintreeSchema = `${bundleIdentifier}.braintree`;
const bundleUrlTypes = config.modResults.CFBundleURLTypes ?? [];
// Check if an entry with the specific Braintree URL scheme already exists
const isBraintreeEntryNotExist = !bundleUrlTypes.find(urlType => {
return urlType.CFBundleURLSchemes?.includes(bundleIdentifierWithBraintreeSchema);
});
// If Braintree entry doesn't exist, add a new one
if (isBraintreeEntryNotExist) {
bundleUrlTypes.push({
CFBundleURLSchemes: [bundleIdentifierWithBraintreeSchema]
});
}
// Assign the modified bundleUrlTypes back to the config
config.modResults.CFBundleURLTypes = bundleUrlTypes;
return config;
});
};
/*
* Add allowlist Venmo URL scheme
* @see https://developer.paypal.com/braintree/docs/guides/venmo/client-side/ios/v6#allowlist-venmo-url-scheme
*/
exports.withExpoBraintreePlist = withExpoBraintreePlist;
const withVenmoScheme = expoConfig => {
return (0, _configPlugins.withInfoPlist)(expoConfig, config => {
// Ensure LSApplicationQueriesSchemes exists in Info.plist
config.modResults.LSApplicationQueriesSchemes = config.modResults.LSApplicationQueriesSchemes || [];
// Hardcoded scheme for Venmo
const venmoScheme = 'com.venmo.touch.v2';
// Add the Venmo scheme to the LSApplicationQueriesSchemes array if not already present
if (!config.modResults.LSApplicationQueriesSchemes.includes(venmoScheme)) {
config.modResults.LSApplicationQueriesSchemes.push(venmoScheme);
}
return config;
});
};
/*
* Mods for Info.plist
*/
/**
* Mods for AppDelegate.swift React Native above 0.77.x and Expo above 53
*/
exports.withVenmoScheme = withVenmoScheme;
const modifyAppDelegateSwift = config => {
const appDelegate = config.modResults;
let contents = _eol.default.split(appDelegate.contents);
// Step 1 Add method to properly handle openUrl method in AppDelegate.m
// func application(
// _ application: UIApplication,
// open url: URL,
// options: [UIApplication.OpenURLOptionsKey : Any] = [:]
// ) -> Bool {
// if url.scheme?.localizedCaseInsensitiveCompare(
// ExpoBraintreeConfig.paymentURLScheme
// ) == .orderedSame {
// return ExpoBraintreeConfig.handleUrl(url: url)
// }
// return RCTLinkingManager.application(
// application,
// open: url,
// options: options
// )
// }
// Step 1.1
// We need to try to find if in AppDelegate.swift there is already an application method to handle url
// we also need to be sure that we do not break other plugins as well
const openUrlIdentifier = `options: [UIApplication.OpenURLOptionsKey: Any] = [:]`;
const expoBraintreeConfigHandleUrl = `return ExpoBraintreeConfig.handleUrl(url: url)`;
const expoBraintreeOpenUrlLines = [` // @generated by react-native-expo-braintree (DO NOT MODIFY)`, ` if url.scheme?.localizedCaseInsensitiveCompare(`, ` ExpoBraintreeConfig.paymentURLScheme`, ` ) == .orderedSame {`, ` ${expoBraintreeConfigHandleUrl}`, ` }`, ` // @generated by react-native-expo-braintree (DO NOT MODIFY)`];
const openUrlIdentifierElementIndex = contents.findIndex(content => content.includes(openUrlIdentifier));
// Step 2 If openUrlIdentifierElementIndex exist in AppDelegate.swift then we only need to add expoBraintreeOpenUrlLines at the top of that method
/* eslint-disable no-extra-boolean-cast */
if (!!~openUrlIdentifierElementIndex) {
contents.splice(
// We are adding +1 to the index to insert content after ') -> Bool {' block
openUrlIdentifierElementIndex + 2, 0, ...expoBraintreeOpenUrlLines);
}
// Step 3 If openUrlIdentifierElementIndex do not exist in AppDelegate.swift add a warning that something went wrong
else {
_configPlugins.WarningAggregator.addWarningIOS('withExpoBraintree', `Unable to find "options: [UIApplication.OpenURLOptionsKey: Any] = [:]" in AppDelegate.swift, automatic changes not applied expo-braintree might not working as expected`);
}
const expoBraintreeConfigHandleUrlElementIndex = contents.findIndex(content => content.includes(expoBraintreeConfigHandleUrl));
// If openUrlMethodElementIndex exist in AppDelegate.mm and expoBraintreeOpenUrlLineIndex do not exist
if (!~expoBraintreeConfigHandleUrlElementIndex) {
_configPlugins.WarningAggregator.addWarningIOS('withExpoBraintree', `Unable to find "return ExpoBraintreeConfig.handleUrl(url: url)" in AppDelegate.swift, automatic changes not applied expo-braintree might not working as expected`);
}
return contents;
};
/**
* Mods for AppDelegate.swift
*/
/**
* Mods for AppDelegate.mm / AppDelegate.m React Native below 0.77.x and Expo below 53
*/
exports.modifyAppDelegateSwift = modifyAppDelegateSwift;
const modifyAppDelegateObjectiveC = (config, xCodeProjectAppName) => {
if (!xCodeProjectAppName) {
_configPlugins.WarningAggregator.addWarningIOS('withExpoBraintree', `xCodeProjectAppName props not provided but in case of using plugin in objective c world it is required`);
}
const appDelegate = config.modResults;
let contents = _eol.default.split(appDelegate.contents);
// Step 1 Edit Import part
// Editing import part for -swift.h file to be able to use Braintree
const importSwiftHeaderFileContent = `#import "${xCodeProjectAppName}-Swift.h"`;
const importSwiftHeaderFileIndex = contents.findIndex(content => content.includes(importSwiftHeaderFileContent));
// If importSwiftHeaderFileContent do not exist in AppDelegate.mm
if (!~importSwiftHeaderFileIndex) {
contents = [importSwiftHeaderFileContent, ...contents];
}
const importExpoModulesSwiftHeader = `#import "ExpoModulesCore-Swift.h"`;
const importExpoModulesSwiftHeaderFileIndex = contents.findIndex(content => content.includes(importExpoModulesSwiftHeader));
// If importExpoModulesSwiftHeader do not exist in AppDelegate.mm
if (!~importExpoModulesSwiftHeaderFileIndex) {
contents = [importExpoModulesSwiftHeader, ...contents];
}
// Step 2 Add method to properly handle openUrl method in AppDelegate.m
const openUrlMethod = '- (BOOL)application:(UIApplication *)application openURL';
const expoBraintreeOpenUrlLines = [' if ([url.scheme localizedCaseInsensitiveCompare:[BraintreeExpoConfig getPaymentUrlScheme]] == NSOrderedSame) {', ' return [BraintreeExpoConfig handleUrl:url];', ' }'];
const openUrlMethodElementIndex = contents.findIndex(content => content.includes(openUrlMethod));
const expoBraintreeOpenUrlLineIndex = contents.findIndex(content => content.includes(expoBraintreeOpenUrlLines?.[0] ?? ''));
// If openUrlMethodElementIndex exist in AppDelegate.mm and expoBraintreeOpenUrlLineIndex do not exist
if (!~expoBraintreeOpenUrlLineIndex && !!~openUrlMethodElementIndex) {
contents.splice(
// We are adding +1 to the index to insert content after '{' block
openUrlMethodElementIndex + 1, 0, ...expoBraintreeOpenUrlLines);
}
return contents;
};
/**
* Mods for AppDelegate.mm
*/
exports.modifyAppDelegateObjectiveC = modifyAppDelegateObjectiveC;
const withExpoBraintreeAppDelegate = (expoConfig, {
xCodeProjectAppName
}) => {
let appDelegateLanguage = null;
return (0, _configPlugins.withAppDelegate)(expoConfig, config => {
appDelegateLanguage = config.modResults.language;
switch (appDelegateLanguage) {
case 'objc':
case 'objcpp':
const resultObjectiVeC = modifyAppDelegateObjectiveC(config, xCodeProjectAppName);
config.modResults.contents = resultObjectiVeC.join('\n');
return config;
case 'swift':
const resultSwift = modifyAppDelegateSwift(config);
config.modResults.contents = resultSwift.join('\n');
return config;
default:
_configPlugins.WarningAggregator.addWarningIOS('withExpoBraintree', `${appDelegateLanguage} AppDelegate file is not supported yet`);
return config;
}
});
};
// Add a new wrapper Swift file to the Xcode project for Swift compatibility.
exports.withExpoBraintreeAppDelegate = withExpoBraintreeAppDelegate;
const withBraintreeWrapperFile = (config, {
appDelegateLanguage
}) => {
const braintreeWrapperObjectiveC = ['import Braintree', 'import Foundation', '', '@objc public class BraintreeExpoConfig: NSObject {', '', '@objc(getPaymentUrlScheme)', 'public static func getPaymentUrlScheme() -> String {', ' let bundleIdentifier = Bundle.main.bundleIdentifier ?? ""', ' return bundleIdentifier + ".braintree"', '}', '', '@objc(handleUrl:)', 'public static func handleUrl(url: URL) -> Bool {', ' return BTAppContextSwitcher.sharedInstance.handleOpen(url)', '}', '}'];
const braintreeWrapperSwift = ['import Braintree', 'import Foundation', '', 'public final class ExpoBraintreeConfig {', '', ' private init() {}', '', ' public static var paymentURLScheme: String {', ' let bundleIdentifier = Bundle.main.bundleIdentifier ?? ""', ' return bundleIdentifier + ".braintree"', ' }', '', ' public static func handleUrl(url: URL) -> Bool {', ' return BTAppContextSwitcher.sharedInstance.handleOpen(url)', ' }', '}'];
switch (appDelegateLanguage) {
case 'objc':
case 'objcpp':
return _configPlugins.IOSConfig.XcodeProjectFile.withBuildSourceFile(config, {
filePath: 'ExpoBraintreeConfig.swift',
contents: braintreeWrapperObjectiveC.join('\n')
});
case 'swift':
return _configPlugins.IOSConfig.XcodeProjectFile.withBuildSourceFile(config, {
filePath: 'ExpoBraintreeConfig.swift',
contents: braintreeWrapperSwift.join('\n')
});
default:
_configPlugins.WarningAggregator.addWarningIOS('withExpoBraintree', `${appDelegateLanguage} AppDelegate file is not supported yet`);
return config;
}
};
exports.withBraintreeWrapperFile = withBraintreeWrapperFile;
//# sourceMappingURL=withExpoBraintree.ios.js.map