feat: create react-native-branch plugin

This commit is contained in:
Giau. Tran Minh committed 2022-01-20 15:42:35 +07:00
1 parent 1de3e74d19
commit 0b06baa2b4
21 files changed
+957

No files matched your search

+4
View File
@@ -57,6 +57,10 @@
],
"@config-plugins/detox",
"@config-plugins/react-native-adjust",
["@config-plugins/react-native-branch", {
"apiKey": "key_live_f9f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8",
"appDomain": "https://bacon.app.link"
}],
"@config-plugins/react-native-google-cast",
"@config-plugins/react-native-ble-plx",
"@config-plugins/react-native-webrtc",
+2
View File
@@ -28,6 +28,7 @@
"@config-plugins/react-native-adjust": "^0.0.1",
"@config-plugins/react-native-ble-plx": "^0.0.2",
"@config-plugins/react-native-blob-util": "^0.0.0",
"@config-plugins/react-native-branch": "^0.0.1",
"@config-plugins/react-native-callkeep": "^0.0.1",
"@config-plugins/react-native-dynamic-app-icon": "^0.0.2",
"@config-plugins/react-native-google-cast": "^0.1.0",
@@ -49,6 +50,7 @@
"react-native-adjust": "^4.29.5",
"react-native-blob-util": "^0.13.17",
"react-native-callkeep": "^4.2.0",
"react-native-branch": "^5.2.1",
"react-native-dynamic-app-icon": "^1.1.0",
"react-native-google-cast": "^4.1.2",
"react-native-pdf": "^6.4.0",
@@ -0,0 +1,2 @@
// @generated by expo-module-scripts
module.exports = require('expo-module-scripts/eslintrc.base.js');
+51
View File
@@ -0,0 +1,51 @@
# config-plugins/react-native-branch
Config plugin to auto configure `react-native-branch` when the native code is generated (`expo prebuild`).
## Expo installation
> Tested against Expo SDK 43
> This package cannot be used in the "Expo Go" app because [it requires custom native code](https://docs.expo.io/workflow/customizing/).
> First install the package with yarn, npm, or [`expo install`](https://docs.expo.io/workflow/expo-cli/#expo-install).
```sh
expo install react-native-branch @config-plugins/react-native-branch
```
After installing this npm package, add the [config plugin](https://docs.expo.io/guides/config-plugins/) to the [`plugins`](https://docs.expo.io/versions/latest/config/app/#plugins) array of your `app.json` or `app.config.js`:
```json
{
"expo": {
"plugins": ["@config-plugins/react-native-branch"]
}
}
```
Next, rebuild your app as described in the ["Adding custom native code"](https://docs.expo.io/workflow/customizing/) guide.
## API
The plugin provides props for extra customization. Every time you change the props or plugins, you'll need to rebuild (and `prebuild`) the native app. If no extra properties are added, defaults will be used.
- `apiKey` (_string_): Branch API key. Required.
- `appDomain` (_string_): App Domain for iOS. Required.
#### Example
```json
{
"expo": {
"plugins": [
[
"@config-plugins/react-native-branch",
{
"apiKey": "xxxxxx",
"appDomain": "xxxxxx",
}
]
]
}
}
```
@@ -0,0 +1 @@
module.exports = require("./build/withBranch");
+4
View File
@@ -0,0 +1,4 @@
export declare type ConfigData = {
apiKey: string;
appDomain: string;
};
+2
View File
@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
+4
View File
@@ -0,0 +1,4 @@
import type { ConfigPlugin } from "@expo/config-plugins";
import type { ConfigData } from "./types";
declare const _default: ConfigPlugin<ConfigData>;
export default _default;
+20
View File
@@ -0,0 +1,20 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const config_plugins_1 = require("@expo/config-plugins");
const withBranchAndroid_1 = require("./withBranchAndroid");
const withBranchIOS_1 = require("./withBranchIOS");
const withBranch = (config, { apiKey, appDomain }) => {
return config_plugins_1.withPlugins(config, [
[withBranchIOS_1.withBranchIos, { apiKey, appDomain }],
[withBranchAndroid_1.withBranchAndroid, { apiKey }],
]);
};
let pkg = {
name: "branch",
};
try {
const branchPkg = require("react-native-branch/package.json");
pkg = branchPkg;
}
catch (_a) { }
exports.default = config_plugins_1.createRunOncePlugin(withBranch, pkg.name, pkg.version);
+14
View File
@@ -0,0 +1,14 @@
import { AndroidConfig } from "@expo/config-plugins";
import type { ConfigPlugin, ExportedConfigWithProps } from "@expo/config-plugins";
import type { MergeResults } from "@expo/config-plugins/build/utils/generateCode";
export declare function editMainApplication(config: ExportedConfigWithProps, action: (mainApplication: string) => string): Promise<void>;
export declare function editProguardRules(config: ExportedConfigWithProps, action: (mainApplication: string) => string): Promise<void>;
export declare function addBranchMainApplicationImport(src: string, packageId: string): MergeResults;
export declare function addBranchGetAutoInstance(src: string): MergeResults;
export declare function addBranchMainActivityImport(src: string, packageId: string): MergeResults;
export declare function addBranchInitSession(src: string): MergeResults;
export declare function addBranchOnNewIntent(src: string): MergeResults;
export declare function setCustomConfigAsync(config: ExportedConfigWithProps<AndroidConfig.Manifest.AndroidManifest>, androidManifest: AndroidConfig.Manifest.AndroidManifest, branchApiKey: string): Promise<AndroidConfig.Manifest.AndroidManifest>;
export declare const withBranchAndroid: ConfigPlugin<{
apiKey: string;
}>;
+238
View File
@@ -0,0 +1,238 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.withBranchAndroid = exports.setCustomConfigAsync = exports.addBranchOnNewIntent = exports.addBranchInitSession = exports.addBranchMainActivityImport = exports.addBranchGetAutoInstance = exports.addBranchMainApplicationImport = exports.editProguardRules = exports.editMainApplication = void 0;
const config_plugins_1 = require("@expo/config-plugins");
const generateCode_1 = require("@expo/config-plugins/build/utils/generateCode");
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const assert_1 = __importDefault(require("assert"));
async function readFileAsync(path) {
return fs_1.default.promises.readFile(path, "utf8");
}
async function saveFileAsync(path, content) {
return fs_1.default.promises.writeFile(path, content, "utf8");
}
// Fork of config-plugins mergeContents, but appends the contents to the end of the file.
function appendContents({ src, newSrc, tag, comment, }) {
const header = generateCode_1.createGeneratedHeaderComment(newSrc, tag, comment);
if (!src.includes(header)) {
// Ensure the old generated contents are removed.
const sanitizedTarget = generateCode_1.removeGeneratedContents(src, tag);
const contentsToAdd = [
// @something
header,
// contents
newSrc,
// @end
`${comment} @generated end ${tag}`,
].join("\n");
return {
contents: (sanitizedTarget !== null && sanitizedTarget !== void 0 ? sanitizedTarget : src) + contentsToAdd,
didMerge: true,
didClear: !!sanitizedTarget,
};
}
return { contents: src, didClear: false, didMerge: false };
}
async function editMainApplication(config, action) {
var _a;
const packageName = (_a = config.android) === null || _a === void 0 ? void 0 : _a.package;
assert_1.default(packageName, "android.package must be defined");
const mainApplicationPath = path_1.default.join(config.modRequest.platformProjectRoot, "app", "src", "main", "java", ...packageName.split("."), "MainApplication.java");
try {
const mainApplication = action(await readFileAsync(mainApplicationPath));
return await saveFileAsync(mainApplicationPath, mainApplication);
}
catch (e) {
config_plugins_1.WarningAggregator.addWarningAndroid("rn-branch-plugin", `Couldn't modify MainApplication.java - ${e}.`);
}
}
exports.editMainApplication = editMainApplication;
async function editProguardRules(config, action) {
const proguardRulesPath = path_1.default.join(config.modRequest.platformProjectRoot, "app", "proguard-rules.pro");
try {
const proguardRules = action(await readFileAsync(proguardRulesPath));
return await saveFileAsync(proguardRulesPath, proguardRules);
}
catch (e) {
config_plugins_1.WarningAggregator.addWarningAndroid("rn-branch-plugin", `Couldn't modify proguard-rules.pro - ${e}.`);
}
}
exports.editProguardRules = editProguardRules;
function addBranchMainApplicationImport(src, packageId) {
const newSrc = ["import io.branch.rnbranch.RNBranchModule;"];
return generateCode_1.mergeContents({
tag: "rn-branch-import",
src,
newSrc: newSrc.join("\n"),
anchor: `package ${packageId};`,
offset: 1,
comment: "//",
});
}
exports.addBranchMainApplicationImport = addBranchMainApplicationImport;
function addBranchGetAutoInstance(src) {
const newSrc = [" RNBranchModule.getAutoInstance(this);"];
return generateCode_1.mergeContents({
tag: "rn-branch-auto-instance",
src,
newSrc: newSrc.join("\n"),
anchor: /super\.onCreate\(\);/,
offset: 1,
comment: "//",
});
}
exports.addBranchGetAutoInstance = addBranchGetAutoInstance;
function addBranchMainActivityImport(src, packageId) {
const newSrc = [
"import android.content.Intent;",
"import io.branch.rnbranch.*;",
];
return generateCode_1.mergeContents({
tag: "rn-branch-import",
src,
newSrc: newSrc.join("\n"),
anchor: `package ${packageId};`,
offset: 1,
comment: "//",
});
}
exports.addBranchMainActivityImport = addBranchMainActivityImport;
function addBranchInitSession(src) {
const tag = "rn-branch-init-session";
try {
const newSrc = [
" RNBranchModule.initSession(getIntent().getData(), this);",
];
return generateCode_1.mergeContents({
tag,
src,
newSrc: newSrc.join("\n"),
anchor: /super\.onStart\(\);/,
offset: 1,
comment: "//",
});
}
catch (err) {
if (err.code !== "ERR_NO_MATCH") {
throw err;
}
}
const newSrc = [
" @Override",
" protected void onStart() {",
" super.onStart();",
" RNBranchModule.initSession(getIntent().getData(), this);",
" }",
];
return generateCode_1.mergeContents({
tag,
src,
newSrc: newSrc.join("\n"),
anchor: `getMainComponentName`,
offset: 3,
comment: "//",
});
}
exports.addBranchInitSession = addBranchInitSession;
function addBranchOnNewIntent(src) {
const tag = "rn-branch-on-new-intent";
try {
const newSrc = [" RNBranchModule.onNewIntent(intent);"];
return generateCode_1.mergeContents({
tag,
src,
newSrc: newSrc.join("\n"),
anchor: /super\.onNewIntent\(intent\);/,
offset: 1,
comment: "//",
});
}
catch (err) {
if (err.code !== "ERR_NO_MATCH") {
throw err;
}
}
const newSrc = [
" @Override",
" public void onNewIntent(Intent intent) {",
" super.onNewIntent(intent);",
" RNBranchModule.onNewIntent(intent);",
" }",
];
return generateCode_1.mergeContents({
tag,
src,
newSrc: newSrc.join("\n"),
anchor: `getMainComponentName`,
offset: 3,
comment: "//",
});
}
exports.addBranchOnNewIntent = addBranchOnNewIntent;
const { addMetaDataItemToMainApplication, getMainApplicationOrThrow } = config_plugins_1.AndroidConfig.Manifest;
// Splitting this function out of the mod makes it easier to test.
async function setCustomConfigAsync(config, androidManifest, branchApiKey) {
// Get the <application /> tag and assert if it doesn't exist.
const mainApplication = getMainApplicationOrThrow(androidManifest);
addMetaDataItemToMainApplication(mainApplication,
// value for `android:name`
"io.branch.sdk.BranchKey",
// value for `android:value`
branchApiKey);
return androidManifest;
}
exports.setCustomConfigAsync = setCustomConfigAsync;
const withBranchAndroid = (config, data) => {
// Insert the branch_key into the AndroidManifest
config = config_plugins_1.withAndroidManifest(config, async (config) => {
// Modifiers can be async, but try to keep them fast.
config.modResults = await setCustomConfigAsync(config, config.modResults, data.apiKey);
return config;
});
// Directly edit MainApplication.java
config = config_plugins_1.withDangerousMod(config, [
"android",
async (config) => {
var _a;
const packageName = (_a = config.android) === null || _a === void 0 ? void 0 : _a.package;
assert_1.default(packageName, "android.package must be defined");
await editMainApplication(config, (mainApplication) => {
mainApplication = addBranchMainApplicationImport(mainApplication, packageName).contents;
mainApplication = addBranchGetAutoInstance(mainApplication).contents;
return mainApplication;
});
return config;
},
]);
// Update proguard rules directly
config = config_plugins_1.withDangerousMod(config, [
"android",
async (config) => {
await editProguardRules(config, (proguardRules) => {
return appendContents({
tag: "rn-branch-dont-warn",
src: proguardRules,
newSrc: ["-dontwarn io.branch.**"].join("\n"),
comment: "#",
}).contents;
});
return config;
},
]);
// Insert the required Branch code into MainActivity.java
config = config_plugins_1.withMainActivity(config, (config) => {
var _a;
const packageName = (_a = config.android) === null || _a === void 0 ? void 0 : _a.package;
assert_1.default(packageName, "android.package must be defined");
config.modResults.contents = addBranchMainActivityImport(config.modResults.contents, packageName).contents;
config.modResults.contents = addBranchInitSession(config.modResults.contents).contents;
config.modResults.contents = addBranchOnNewIntent(config.modResults.contents).contents;
return config;
});
return config;
};
exports.withBranchAndroid = withBranchAndroid;
+8
View File
@@ -0,0 +1,8 @@
import { MergeResults } from "@expo/config-plugins/build/utils/generateCode";
import type { ConfigPlugin } from "@expo/config-plugins";
import type { ConfigData } from "./types";
export declare function addBranchAppDelegateImport(src: string): MergeResults;
export declare function addBranchAppDelegateInit(src: string): MergeResults;
export declare function addBranchAppDelegateOpenURL(src: string): MergeResults;
export declare function addBranchAppDelegateContinueUserActivity(src: string): MergeResults;
export declare const withBranchIos: ConfigPlugin<ConfigData>;
+88
View File
@@ -0,0 +1,88 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.withBranchIos = exports.addBranchAppDelegateContinueUserActivity = exports.addBranchAppDelegateOpenURL = exports.addBranchAppDelegateInit = exports.addBranchAppDelegateImport = void 0;
const config_plugins_1 = require("@expo/config-plugins");
const generateCode_1 = require("@expo/config-plugins/build/utils/generateCode");
function addBranchAppDelegateImport(src) {
const newSrc = ["#import <RNBranch/RNBranch.h>"];
return generateCode_1.mergeContents({
tag: "rn-branch-import",
src,
newSrc: newSrc.join("\n"),
anchor: /#import "AppDelegate\.h"/,
offset: 1,
comment: "//",
});
}
exports.addBranchAppDelegateImport = addBranchAppDelegateImport;
// Match against `UMModuleRegistryAdapter` (unimodules), and React Native without unimodules (Expo Modules).
const MATCH_INIT = /(?:(self\.|_)(\w+)\s?=\s?\[\[UMModuleRegistryAdapter alloc\])|(?:RCTBridge\s?\*\s?(\w+)\s?=\s?\[\[RCTBridge alloc\])/g;
function addBranchAppDelegateInit(src) {
const newSrc = [];
newSrc.push(" [RNBranch initSessionWithLaunchOptions:launchOptions isReferrable:YES];");
return generateCode_1.mergeContents({
tag: "rn-branch-init",
src,
newSrc: newSrc.join("\n"),
anchor: MATCH_INIT,
offset: 0,
comment: "//",
});
}
exports.addBranchAppDelegateInit = addBranchAppDelegateInit;
function addBranchAppDelegateOpenURL(src) {
const newSrc = [
" if ([RNBranch application:application openURL:url options:options]) {",
" // do other deep link routing for the Facebook SDK, Pinterest SDK, etc",
" }",
];
return generateCode_1.mergeContents({
tag: "rn-branch-open-url",
src,
newSrc: newSrc.join("\n"),
anchor: /\(UIApplication \*\)application openURL:/,
offset: 1,
comment: "//",
});
}
exports.addBranchAppDelegateOpenURL = addBranchAppDelegateOpenURL;
function addBranchAppDelegateContinueUserActivity(src) {
const newSrc = [
" if ([RNBranch continueUserActivity:userActivity]) {",
" return YES;",
" }",
];
return generateCode_1.mergeContents({
tag: "rn-branch-continue-user-activity",
src,
newSrc: newSrc.join("\n"),
anchor: /\(UIApplication \*\)application continueUserActivity:/,
offset: 1,
comment: "//",
});
}
exports.addBranchAppDelegateContinueUserActivity = addBranchAppDelegateContinueUserActivity;
const withBranchIos = (config, data) => {
// Ensure object exist
if (!config.ios) {
config.ios = {};
}
// Update the infoPlist with the branch key and branch domain
config = config_plugins_1.withInfoPlist(config, (config) => {
config.modResults.branch_app_domain = data.appDomain;
config.modResults.branch_key = {
live: data.apiKey,
};
return config;
});
// Update the AppDelegate.m
config = config_plugins_1.withAppDelegate(config, (config) => {
config.modResults.contents = addBranchAppDelegateImport(config.modResults.contents).contents;
config.modResults.contents = addBranchAppDelegateInit(config.modResults.contents).contents;
config.modResults.contents = addBranchAppDelegateOpenURL(config.modResults.contents).contents;
config.modResults.contents = addBranchAppDelegateContinueUserActivity(config.modResults.contents).contents;
return config;
});
return config;
};
exports.withBranchIos = withBranchIos;
@@ -0,0 +1 @@
module.exports = require('expo-module-scripts/jest-preset-plugin');
+35
View File
@@ -0,0 +1,35 @@
{
"name": "@config-plugins/react-native-branch",
"version": "0.0.1",
"description": "Config plugin to auto configure react-native-branch on prebuild",
"main": "build/withBranch.js",
"types": "build/withBranch.d.ts",
"sideEffects": false,
"files": [
"build",
"app.plugin.js"
],
"scripts": {
"build": "expo-module build",
"clean": "expo-module clean",
"lint": "expo-module lint",
"test": "expo-module test",
"prepare": "expo-module prepare",
"prepublishOnly": "expo-module prepublishOnly",
"expo-module": "expo-module"
},
"keywords": [
"expo",
"react-native",
"react-native-branch"
],
"jest": {
"preset": "expo-module-scripts"
},
"dependencies": {
"@expo/config-plugins": "^4.0.7"
},
"devDependencies": {
"expo-module-scripts": "^2.0.0"
}
}
@@ -0,0 +1,4 @@
export type ConfigData = {
apiKey: string;
appDomain: string;
}
@@ -0,0 +1,26 @@
import { createRunOncePlugin, withPlugins } from "@expo/config-plugins";
import type { ConfigPlugin } from "@expo/config-plugins";
import { withBranchAndroid } from "./withBranchAndroid";
import { withBranchIos } from "./withBranchIOS";
import type { ConfigData } from "./types";
const withBranch: ConfigPlugin<ConfigData> = (config, { apiKey, appDomain }) => {
return withPlugins(
config,
[
[withBranchIos, { apiKey, appDomain }],
[withBranchAndroid, { apiKey }],
]
);
};
let pkg: { name: string; version?: string } = {
name: "branch",
};
try {
const branchPkg = require("react-native-branch/package.json");
pkg = branchPkg;
} catch {}
export default createRunOncePlugin(withBranch, pkg.name, pkg.version);
@@ -0,0 +1,333 @@
import {
AndroidConfig,
WarningAggregator,
withAndroidManifest,
withDangerousMod,
withMainActivity,
} from "@expo/config-plugins";
import type {
ConfigPlugin,
ExportedConfigWithProps,
} from "@expo/config-plugins";
import {
createGeneratedHeaderComment,
mergeContents,
removeGeneratedContents,
} from "@expo/config-plugins/build/utils/generateCode";
import type { MergeResults } from "@expo/config-plugins/build/utils/generateCode";
import fs from "fs";
import path from "path";
import assert from "assert";
async function readFileAsync(path: string) {
return fs.promises.readFile(path, "utf8");
}
async function saveFileAsync(path: string, content: string) {
return fs.promises.writeFile(path, content, "utf8");
}
// Fork of config-plugins mergeContents, but appends the contents to the end of the file.
function appendContents({
src,
newSrc,
tag,
comment,
}: {
src: string;
newSrc: string;
tag: string;
comment: string;
}): MergeResults {
const header = createGeneratedHeaderComment(newSrc, tag, comment);
if (!src.includes(header)) {
// Ensure the old generated contents are removed.
const sanitizedTarget = removeGeneratedContents(src, tag);
const contentsToAdd = [
// @something
header,
// contents
newSrc,
// @end
`${comment} @generated end ${tag}`,
].join("\n");
return {
contents: (sanitizedTarget ?? src) + contentsToAdd,
didMerge: true,
didClear: !!sanitizedTarget,
};
}
return { contents: src, didClear: false, didMerge: false };
}
export async function editMainApplication(
config: ExportedConfigWithProps,
action: (mainApplication: string) => string
) {
const packageName = config.android?.package;
assert(packageName, "android.package must be defined");
const mainApplicationPath = path.join(
config.modRequest.platformProjectRoot,
"app",
"src",
"main",
"java",
...packageName.split("."),
"MainApplication.java"
);
try {
const mainApplication = action(await readFileAsync(mainApplicationPath));
return await saveFileAsync(mainApplicationPath, mainApplication);
} catch (e) {
WarningAggregator.addWarningAndroid(
"rn-branch-plugin",
`Couldn't modify MainApplication.java - ${e}.`
);
}
}
export async function editProguardRules(
config: ExportedConfigWithProps,
action: (mainApplication: string) => string
) {
const proguardRulesPath = path.join(
config.modRequest.platformProjectRoot,
"app",
"proguard-rules.pro"
);
try {
const proguardRules = action(await readFileAsync(proguardRulesPath));
return await saveFileAsync(proguardRulesPath, proguardRules);
} catch (e) {
WarningAggregator.addWarningAndroid(
"rn-branch-plugin",
`Couldn't modify proguard-rules.pro - ${e}.`
);
}
}
export function addBranchMainApplicationImport(
src: string,
packageId: string
): MergeResults {
const newSrc = ["import io.branch.rnbranch.RNBranchModule;"];
return mergeContents({
tag: "rn-branch-import",
src,
newSrc: newSrc.join("\n"),
anchor: `package ${packageId};`,
offset: 1,
comment: "//",
});
}
export function addBranchGetAutoInstance(src: string): MergeResults {
const newSrc = [" RNBranchModule.getAutoInstance(this);"];
return mergeContents({
tag: "rn-branch-auto-instance",
src,
newSrc: newSrc.join("\n"),
anchor: /super\.onCreate\(\);/,
offset: 1,
comment: "//",
});
}
export function addBranchMainActivityImport(
src: string,
packageId: string
): MergeResults {
const newSrc = [
"import android.content.Intent;",
"import io.branch.rnbranch.*;",
];
return mergeContents({
tag: "rn-branch-import",
src,
newSrc: newSrc.join("\n"),
anchor: `package ${packageId};`,
offset: 1,
comment: "//",
});
}
export function addBranchInitSession(src: string): MergeResults {
const tag = "rn-branch-init-session";
try {
const newSrc = [
" RNBranchModule.initSession(getIntent().getData(), this);",
];
return mergeContents({
tag,
src,
newSrc: newSrc.join("\n"),
anchor: /super\.onStart\(\);/,
offset: 1,
comment: "//",
});
} catch (err) {
if (err.code !== "ERR_NO_MATCH") {
throw err;
}
}
const newSrc = [
" @Override",
" protected void onStart() {",
" super.onStart();",
" RNBranchModule.initSession(getIntent().getData(), this);",
" }",
];
return mergeContents({
tag,
src,
newSrc: newSrc.join("\n"),
anchor: `getMainComponentName`,
offset: 3,
comment: "//",
});
}
export function addBranchOnNewIntent(src: string): MergeResults {
const tag = "rn-branch-on-new-intent";
try {
const newSrc = [" RNBranchModule.onNewIntent(intent);"];
return mergeContents({
tag,
src,
newSrc: newSrc.join("\n"),
anchor: /super\.onNewIntent\(intent\);/,
offset: 1,
comment: "//",
});
} catch (err) {
if (err.code !== "ERR_NO_MATCH") {
throw err;
}
}
const newSrc = [
" @Override",
" public void onNewIntent(Intent intent) {",
" super.onNewIntent(intent);",
" RNBranchModule.onNewIntent(intent);",
" }",
];
return mergeContents({
tag,
src,
newSrc: newSrc.join("\n"),
anchor: `getMainComponentName`,
offset: 3,
comment: "//",
});
}
const { addMetaDataItemToMainApplication, getMainApplicationOrThrow } =
AndroidConfig.Manifest;
// Splitting this function out of the mod makes it easier to test.
export async function setCustomConfigAsync(
config: ExportedConfigWithProps<AndroidConfig.Manifest.AndroidManifest>,
androidManifest: AndroidConfig.Manifest.AndroidManifest,
branchApiKey: string
) {
// Get the <application /> tag and assert if it doesn't exist.
const mainApplication = getMainApplicationOrThrow(androidManifest);
addMetaDataItemToMainApplication(
mainApplication,
// value for `android:name`
"io.branch.sdk.BranchKey",
// value for `android:value`
branchApiKey
);
return androidManifest;
}
export const withBranchAndroid: ConfigPlugin<{ apiKey: string }> = (
config,
data
) => {
// Insert the branch_key into the AndroidManifest
config = withAndroidManifest(config, async (config) => {
// Modifiers can be async, but try to keep them fast.
config.modResults = await setCustomConfigAsync(
config,
config.modResults,
data.apiKey
);
return config;
});
// Directly edit MainApplication.java
config = withDangerousMod(config, [
"android",
async (config) => {
const packageName = config.android?.package;
assert(packageName, "android.package must be defined");
await editMainApplication(config, (mainApplication) => {
mainApplication = addBranchMainApplicationImport(
mainApplication,
packageName
).contents;
mainApplication = addBranchGetAutoInstance(mainApplication).contents;
return mainApplication;
});
return config;
},
]);
// Update proguard rules directly
config = withDangerousMod(config, [
"android",
async (config) => {
await editProguardRules(config, (proguardRules) => {
return appendContents({
tag: "rn-branch-dont-warn",
src: proguardRules,
newSrc: ["-dontwarn io.branch.**"].join("\n"),
comment: "#",
}).contents;
});
return config;
},
]);
// Insert the required Branch code into MainActivity.java
config = withMainActivity(config, (config) => {
const packageName = config.android?.package;
assert(packageName, "android.package must be defined");
config.modResults.contents = addBranchMainActivityImport(
config.modResults.contents,
packageName
).contents;
config.modResults.contents = addBranchInitSession(
config.modResults.contents
).contents;
config.modResults.contents = addBranchOnNewIntent(
config.modResults.contents
).contents;
return config;
});
return config;
};
@@ -0,0 +1,106 @@
import { withAppDelegate, withInfoPlist } from "@expo/config-plugins";
import { mergeContents, MergeResults } from "@expo/config-plugins/build/utils/generateCode";
import type { ConfigPlugin } from "@expo/config-plugins";
import type { ConfigData } from "./types";
export function addBranchAppDelegateImport(src: string): MergeResults {
const newSrc = ["#import <RNBranch/RNBranch.h>"];
return mergeContents({
tag: "rn-branch-import",
src,
newSrc: newSrc.join("\n"),
anchor: /#import "AppDelegate\.h"/,
offset: 1,
comment: "//",
});
}
// Match against `UMModuleRegistryAdapter` (unimodules), and React Native without unimodules (Expo Modules).
const MATCH_INIT =
/(?:(self\.|_)(\w+)\s?=\s?\[\[UMModuleRegistryAdapter alloc\])|(?:RCTBridge\s?\*\s?(\w+)\s?=\s?\[\[RCTBridge alloc\])/g;
export function addBranchAppDelegateInit(src: string): MergeResults {
const newSrc = [];
newSrc.push(
" [RNBranch initSessionWithLaunchOptions:launchOptions isReferrable:YES];"
);
return mergeContents({
tag: "rn-branch-init",
src,
newSrc: newSrc.join("\n"),
anchor: MATCH_INIT,
offset: 0,
comment: "//",
});
}
export function addBranchAppDelegateOpenURL(src: string): MergeResults {
const newSrc = [
" if ([RNBranch application:application openURL:url options:options]) {",
" // do other deep link routing for the Facebook SDK, Pinterest SDK, etc",
" }",
];
return mergeContents({
tag: "rn-branch-open-url",
src,
newSrc: newSrc.join("\n"),
anchor: /\(UIApplication \*\)application openURL:/,
offset: 1,
comment: "//",
});
}
export function addBranchAppDelegateContinueUserActivity(src: string): MergeResults {
const newSrc = [
" if ([RNBranch continueUserActivity:userActivity]) {",
" return YES;",
" }",
];
return mergeContents({
tag: "rn-branch-continue-user-activity",
src,
newSrc: newSrc.join("\n"),
anchor: /\(UIApplication \*\)application continueUserActivity:/,
offset: 1,
comment: "//",
});
}
export const withBranchIos: ConfigPlugin<ConfigData> = (config, data) =>{
// Ensure object exist
if (!config.ios) {
config.ios = {};
}
// Update the infoPlist with the branch key and branch domain
config = withInfoPlist(config, (config) => {
config.modResults.branch_app_domain = data.appDomain;
config.modResults.branch_key = {
live: data.apiKey,
}
return config;
});
// Update the AppDelegate.m
config = withAppDelegate(config, (config) => {
config.modResults.contents = addBranchAppDelegateImport(
config.modResults.contents
).contents;
config.modResults.contents = addBranchAppDelegateInit(
config.modResults.contents
).contents;
config.modResults.contents = addBranchAppDelegateOpenURL(
config.modResults.contents
).contents;
config.modResults.contents = addBranchAppDelegateContinueUserActivity(
config.modResults.contents
).contents;
return config;
});
return config;
}
@@ -0,0 +1,9 @@
{
"extends": "expo-module-scripts/tsconfig.plugin",
"compilerOptions": {
"outDir": "build",
"rootDir": "src"
},
"include": ["./src"],
"exclude": ["**/__mocks__/*", "**/__tests__/*"]
}
+5
View File
@@ -14749,6 +14749,11 @@ react-native-blob-util@^0.13.17:
base-64 "0.1.0"
glob "^7.1.6"
react-native-branch@^5.2.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/react-native-branch/-/react-native-branch-5.2.1.tgz#72329419365822708533ae93b31de0547e301b59"
integrity sha512-SDGbIh+axaHscoZ+vXAQQ+POcwf8tF2jQDtdxRE7JHJ/B9pqvChqTicllxwgyrXhZ0cj4TtpvLaD9jQ2ktnNyw==
react-native-callkeep@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/react-native-callkeep/-/react-native-callkeep-4.2.0.tgz#84f20eaf07a4b3494502bf9156dbdbf4b7617122"