react-native-expo-braintree
Version:
React native and expo wrapper around braintree sdk fro android and ios
103 lines (96 loc) • 5.78 kB
JavaScript
/* eslint-disable no-bitwise */
import { IOSConfig, withAppDelegate, withInfoPlist } from '@expo/config-plugins';
import eol from 'eol';
export const withExpoBraintreeAppDelegate = (expoConfig, {
xCodeProjectAppName
}) => {
return withAppDelegate(expoConfig, config => {
const appDelegate = config.modResults;
let contents = eol.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 configure method in didFinishLaunchingWithOptions
const didFinishLaunchingWithOptions = 'didFinishLaunchingWithOptions';
const expoBraintreeConfigureLine = ' [BraintreeExpoConfig configure];';
let didFinishLaunchingWithOptionsElementIndex = contents.findIndex(content => content.includes(didFinishLaunchingWithOptions));
const expoBraintreeConfigureLineIndex = contents.findIndex(content => content.includes(expoBraintreeConfigureLine));
// If didFinishLaunchingWithOptions exist in AppDelegate.mm and expoBraintreeConfigureLine do not exist
if (!~expoBraintreeConfigureLineIndex && !!~didFinishLaunchingWithOptionsElementIndex) {
contents.splice(
// We are adding +2 to the index to insert content after '{' block
didFinishLaunchingWithOptionsElementIndex + 2, 0, expoBraintreeConfigureLine);
}
// Step 3 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);
}
config.modResults.contents = contents.join('\n');
return config;
});
};
/**
* Add a new wrapper Swift file to the Xcode project for Swift compatibility.
*/
export const withSwiftBraintreeWrapperFile = config => {
return IOSConfig.XcodeProjectFile.withBuildSourceFile(config, {
filePath: 'BraintreeExpoConfig.swift',
contents: ['import Braintree', 'import Foundation', '', '@objc public class BraintreeExpoConfig: NSObject {', '', '@objc(configure)', 'public static func configure() {', ' BTAppContextSwitcher.sharedInstance.returnURLScheme = self.getPaymentUrlScheme()', '}', '', '@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)', '}', '}'].join('\n')
});
};
export const withExpoBraintreePlist = expoConfig => {
return 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
*/
export const withVenmoScheme = expoConfig => {
return 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;
});
};
//# sourceMappingURL=withExpoBraintree.ios.js.map