Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expo plugin to simplify installation with expo #2415

Open
wants to merge 27 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
a97fd99
Make android configuration paths dynamic
deggertsen Jan 10, 2023
e109e89
Add expo plugin
deggertsen Jan 11, 2023
85d18ec
Add @expo/config-plugins package for expo compatibility
deggertsen Jan 11, 2023
69d8571
Add expo plugin
deggertsen Jan 11, 2023
f298984
Add docs for plugin installation for expo
deggertsen Jan 11, 2023
0f74117
Shouldn't need the clean tag for expo prebuild
deggertsen Jan 11, 2023
252f718
Fix title to reflect that this is for IOS, not Android.
deggertsen Jan 12, 2023
38840a3
Merge branch 'master' into master
deggertsen Feb 2, 2023
80a96fb
Add optional CodePushPublicKey to android types for expo plugin
deggertsen Feb 2, 2023
1f81963
If CodePushPublicKey is defined in expo config, add public key as a s…
deggertsen Feb 2, 2023
3e20862
Add to docs concerning codepush public key
deggertsen Feb 2, 2023
f52c6ef
Fix for react native 71
deggertsen Jun 12, 2023
887ac2d
Apply the case for react native 71
deggertsen Jun 12, 2023
ef49c1e
Include the react-native-code-push settings at the bottom of the file
deggertsen Jun 12, 2023
8e318fa
Merge branch 'master' into master
deggertsen Jun 12, 2023
bd27f03
Merge branch 'master' into master
deggertsen Jul 16, 2023
9ac6ea9
Update buildscriptDependency.ts
deggertsen Aug 12, 2023
2c85f66
Apply suggestions from code review
deggertsen Aug 12, 2023
93a082f
Merge branch 'master' into master
deggertsen Sep 14, 2023
2e1ad0c
Update expo config plugins package
deggertsen Sep 29, 2023
f9208a7
Fix expo config plugins imports
deggertsen Sep 29, 2023
22722f7
Merge branch 'master' into master
AnatolyPristensky Oct 30, 2023
383115f
Merge branch 'master' into master
deggertsen Oct 30, 2023
3f92f71
Merge branch 'master' into master
deggertsen Nov 8, 2023
357cbea
Merge branch 'master' into master
MikhailSuendukov Jan 18, 2024
7b4c99a
Merge branch 'master' into master
deggertsen Jan 18, 2024
2639f6b
Merge branch 'master' into master
deggertsen Mar 14, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app.plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('./plugin/build');
32 changes: 30 additions & 2 deletions docs/setup-android.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## Android Setup

* [Plugin Installation for Expo (Android)](#plugin-installation-for-expo-android)
* [Plugin Installation and Configuration for React Native 0.60 version and above](#plugin-installation-and-configuration-for-react-native-060-version-and-above-android)
* [Plugin Installation for React Native lower than 0.60 (Android)](#plugin-installation-for-react-native-lower-than-060-android)
* [Plugin Installation (Android - RNPM)](#plugin-installation-android---rnpm)
Expand All @@ -17,21 +18,48 @@

In order to integrate CodePush into your Android project, please perform the following steps:

### Plugin Installation for Expo (Android)

React Native Code Push comes packaged with a plugin to automate some of the setup process:

1. Open your Expo app config file (app.config.json or app.config.js instead of app.json).

2. Insert the following into the plugins section of your config (Don't duplicate configuration, if the 'react-native-code-push' item already exists, simply add the android portion.). Create the plugins section if it doesn't already exist. CodePushPublicKey is optional, see Code Signing setup for more information.

```javascript
"plugins": [
[
'react-native-code-push',
{
android: {
CodePushDeploymentKey: 'YOUR_ANDROID_CODE_PUSH_KEY',
deggertsen marked this conversation as resolved.
Show resolved Hide resolved
CodePushPublicKey: 'YOUR_ANDROID_PUBLIC_KEY',
}
}
]
]
```

3. Replace `YOUR_ANDROID_CODE_PUSH_KEY` with the Deployment key.

4. Run `npx expo prebuild` to regenerate your native code with the codepush dependencies.

### Plugin Installation and Configuration for React Native 0.60 version and above (Android)

1. In your `android/settings.gradle` file, make the following additions at the end of the file:

```gradle
...
include ':app', ':react-native-code-push'
project(':react-native-code-push').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-code-push/android/app')
project(':react-native-code-push').projectDir = new File(["node", "--print", "require.resolve('react-native-code-push/package.json')"].execute(null, rootDir).text.trim(), "../android/app")
```

2. In your `android/app/build.gradle` file, add the `codepush.gradle` file as an additional build task definition to the end of the file:

```gradle
...
apply from: "../../node_modules/react-native-code-push/android/codepush.gradle"
apply from: new File(reactNativeRoot, "react.gradle")
apply from: new File(["node", "--print", "require.resolve('react-native-code-push/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/android/codepush.gradle"
...
```

Expand Down
25 changes: 25 additions & 0 deletions docs/setup-ios.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,31 @@
## iOS Setup

Once you've acquired the CodePush plugin, you need to integrate it into the Xcode project of your React Native app and configure it correctly. To do this, take the following steps:

### Plugin Installation for Expo (iOS)

React Native Code Push comes packaged with a plugin to automate some of the setup process:

1. Open your Expo app config file (app.config.json or app.config.js instead of app.json).

2. Insert the following into the plugins section of your config (Don't duplicate configuration, if the 'react-native-code-push' item already exists, simply add the ios portion.). Create the plugins section if it doesn't already exist.

```javascript
"plugins": [
[
'react-native-code-push',
{
ios: {
CodePushDeploymentKey: 'YOUR_IOS_CODE_PUSH_KEY',
},
}
]
]
```

3. Replace `YOUR_IOS_CODE_PUSH_KEY` with the Deployment key.

4. Run `npx expo prebuild` to regenerate your native code with the codepush dependencies.
### Plugin Installation and Configuration for React Native 0.60 version and above (iOS)

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"xcode": "3.0.1"
},
"devDependencies": {
"@expo/config-plugins": "^7.2.5",
"@types/assert": "^1.5.2",
"@types/mkdirp": "^1.0.1",
"@types/mocha": "^9.0.0",
Expand Down
44 changes: 44 additions & 0 deletions plugin/src/android/buildscriptDependency.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { ConfigPlugin, withAppBuildGradle } from 'expo/config-plugins'

import { PluginConfigType } from '../pluginConfig'

/**
* Update `<project>/build.gradle` by adding the codepush.gradle file
* as an additional build task definition underneath react.gradle
*/

function applyImplementation(appBuildGradle: string) {
const codePushImplementation = `apply from: new File(["node", "--print", "require.resolve('react-native-code-push/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/android/codepush.gradle"`;

// Make sure the project does not have the dependency already
if (!appBuildGradle.includes(codePushImplementation)) {
const reactNativeFileClassGradleInclude = `'apply from: new File(reactNativeRoot, "react.gradle")`;
if (appBuildGradle.includes(reactNativeFileClassGradleInclude)) {
return appBuildGradle.replace(
reactNativeFileClassGradleInclude,
`${reactNativeFileClassGradleInclude}\n${codePushImplementation}`
);
}
const reactNativeRawGradleInclude = `apply from: "../../node_modules/react-native/react.gradle"`;
if (appBuildGradle.includes(reactNativeRawGradleInclude)) {
return appBuildGradle.replace(
reactNativeRawGradleInclude,
`${reactNativeRawGradleInclude}\n${codePushImplementation}`
);
}
const reactNative71Include = `apply from: new File(["node", "--print", "require.resolve('@react-native-community/cli-platform-android/package.json')"].execute(null, rootDir).text.trim(), "../native_modules.gradle");`;
if (appBuildGradle.includes(reactNative71Include)) {
return appBuildGradle.replace(
reactNative71Include,
`${reactNative71Include}\n${codePushImplementation}`
);
}
}
return appBuildGradle;
}
export const withAndroidBuildscriptDependency: ConfigPlugin<PluginConfigType> = (config) => {
return withAppBuildGradle(config, (config) => {
config.modResults.contents = applyImplementation(config.modResults.contents)
return config
})
}
11 changes: 11 additions & 0 deletions plugin/src/android/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { withAndroidBuildscriptDependency } from './buildscriptDependency'
import { withAndroidMainApplicationDependency } from './mainApplicationDependency'
import { withAndroidSettingsDependency } from './settingsDependency'
import { withAndroidStringsDependency } from './stringsDependency'

export {
withAndroidBuildscriptDependency,
withAndroidSettingsDependency,
withAndroidStringsDependency,
withAndroidMainApplicationDependency,
}
59 changes: 59 additions & 0 deletions plugin/src/android/mainApplicationDependency.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { ConfigPlugin, withMainApplication } from 'expo/config-plugins'

import { PluginConfigType } from '../pluginConfig'

/**
* Update `<project>/build.gradle` by adding the codepush.gradle file
* as an additional build task definition underneath react.gradle
*/

function applyImplementation(
mainApplication: string,
find: string,
add: string,
replace?: boolean
) {
// Make sure the project does not have the settings already
if (!mainApplication.includes(add)) {
if (replace) return mainApplication.replace(find, add)
else return mainApplication.replace(find, `${find}\n${add}`)
}

return mainApplication
}

export const withAndroidMainApplicationDependency: ConfigPlugin<PluginConfigType> = (config) => {
return withMainApplication(config, (config) => {
config.modResults.contents = applyImplementation(
config.modResults.contents,
'import expo.modules.ReactNativeHostWrapper;',
`\nimport com.microsoft.codepush.react.CodePush;`
)
if (
config.modResults.contents.includes("new DefaultReactNativeHost(this) {")
) {
config.modResults.contents = applyImplementation(
config.modResults.contents,
`new DefaultReactNativeHost(this) {`,
`
@Override
protected String getJSBundleFile() {
return CodePush.getJSBundleFile();
}\n`
);
} else if (
config.modResults.contents.includes(" new ReactNativeHost(this) {")
) {
config.modResults.contents = applyImplementation(
config.modResults.contents,
`new ReactNativeHost(this) {`,
`
@Override
protected String getJSBundleFile() {
return CodePush.getJSBundleFile();
}\n`
);
}
return config;
})
}
25 changes: 25 additions & 0 deletions plugin/src/android/settingsDependency.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ConfigPlugin, withSettingsGradle } from 'expo/config-plugins'

import { PluginConfigType } from '../pluginConfig'

/**
* Update `<project>/settings.gradle` by adding react-native-code-push
*/

function applySettings(gradleSettings: string) {
const codePushSettings = `\ninclude ':react-native-code-push'\nproject(':react-native-code-push').projectDir = new File(["node", "--print", "require.resolve('react-native-code-push/package.json')"].execute(null, rootDir).text.trim(), "../android/app")`

// Make sure the project does not have the settings already
if (!gradleSettings.includes(`include ':react-native-code-push'`)) {
return gradleSettings + codePushSettings
}

return gradleSettings
}

export const withAndroidSettingsDependency: ConfigPlugin<PluginConfigType> = (config) => {
return withSettingsGradle(config, (config) => {
config.modResults.contents = applySettings(config.modResults.contents)
return config
})
}
28 changes: 28 additions & 0 deletions plugin/src/android/stringsDependency.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { AndroidConfig, ConfigPlugin, withStringsXml } from 'expo/config-plugins'
import { ResourceXML } from 'expo/config-plugins/build/android/Resources'

import { PluginConfigType } from '../pluginConfig'

/**
* Update `<project>/app/src/main/res/values/strings.xml` by adding react-native-code-push deployment key
*/

function setStrings(strings: ResourceXML, name: string, value: string) {
// Helper to add string.xml JSON items or overwrite existing items with the same name.
return AndroidConfig.Strings.setStringItem(
[
// XML represented as JSON
// <string moduleConfig="true" name="">value</string>
{ $: { name }, _: value },
],
strings
)
}

export const withAndroidStringsDependency: ConfigPlugin<PluginConfigType> = (config, props) => {
return withStringsXml(config, (config) => {
config.modResults = setStrings(config.modResults, 'CodePushDeploymentKey', props.android.CodePushDeploymentKey)
if (props.android.CodePushPublicKey) config.modResults = setStrings(config.modResults, 'CodePushPublicKey', props.android.CodePushPublicKey)
return config
})
}
36 changes: 36 additions & 0 deletions plugin/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { ConfigPlugin, createRunOncePlugin } from '@expo/config-plugins'
deggertsen marked this conversation as resolved.
Show resolved Hide resolved

import {
withAndroidBuildscriptDependency,
withAndroidMainApplicationDependency,
withAndroidSettingsDependency,
withAndroidStringsDependency,
} from './android'
import { withIosAppDelegateDependency, withIosBuildscriptDependency } from './ios'
import { PluginConfigType } from './pluginConfig'

/**
* A config plugin for configuring `react-native-code-push`
*/
const withRnCodepush: ConfigPlugin<PluginConfigType> = (config, props) => {
config = withAndroidBuildscriptDependency(config, props)
config = withAndroidSettingsDependency(config, props)
config = withAndroidStringsDependency(config, props)
config = withAndroidMainApplicationDependency(config, props)
// plugins order matter: the later one would run first
config = withIosBuildscriptDependency(config, props)
config = withIosAppDelegateDependency(config, props)

return config
}

let pkg: { name: string; version?: string } = {
name: "react-native-code-push",
// UNVERSIONED...
};
try {
const codePushPkg = require("react-native-code-push/package.json");
pkg = codePushPkg;
} catch {}

export default createRunOncePlugin(withRnCodepush, pkg.name, pkg.version);
34 changes: 34 additions & 0 deletions plugin/src/ios/appDelegateDependency.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { ConfigPlugin, withAppDelegate } from '@expo/config-plugins'

import { PluginConfigType } from '../pluginConfig'

// Use these imports in SDK 46 and lower
// import { ConfigPlugin, InfoPlist, withInfoPlist } from '@expo/config-plugins';
// import { ExpoConfig } from '@expo/config-types';

function applyImplementation(appDelegate: string, find: string, add: string, replace?: boolean) {
// Make sure the project does not have the settings already
if (!appDelegate.includes(add)) {
if (replace) return appDelegate.replace(find, add)
else return appDelegate.replace(find, `${find}\n${add}`)
}

return appDelegate
}

export const withIosAppDelegateDependency: ConfigPlugin<PluginConfigType> = (config, props) => {
return withAppDelegate(config, (config) => {
config.modResults.contents = applyImplementation(
config.modResults.contents,
`#import "AppDelegate.h"`,
`#import <CodePush/CodePush.h>`
)
config.modResults.contents = applyImplementation(
config.modResults.contents,
`return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];`,
`return [CodePush bundleURL];`,
true
)
return config
})
}
12 changes: 12 additions & 0 deletions plugin/src/ios/buildscriptDependency.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { ConfigPlugin, withInfoPlist } from '@expo/config-plugins'

import { PluginConfigType } from '../pluginConfig'

// Pass `<string>` to specify that this plugin requires a string property.
export const withIosBuildscriptDependency: ConfigPlugin<PluginConfigType> = (config, props) => {
if (!props.ios.CodePushDeploymentKey) return config
return withInfoPlist(config, (config) => {
config.modResults.CodePushDeploymentKey = props.ios.CodePushDeploymentKey
return config
})
}
4 changes: 4 additions & 0 deletions plugin/src/ios/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { withIosAppDelegateDependency } from './appDelegateDependency'
import { withIosBuildscriptDependency } from './buildscriptDependency'

export { withIosBuildscriptDependency, withIosAppDelegateDependency }
12 changes: 12 additions & 0 deletions plugin/src/pluginConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Configuration for `react-native-code-push`
*/
export interface PluginConfigType {
ios: {
CodePushDeploymentKey: string
}
android: {
CodePushDeploymentKey: string
CodePushPublicKey?: string
}
}
8 changes: 8 additions & 0 deletions plugin/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"extends": "expo-module-scripts/tsconfig.plugin",
"compilerOptions": {
"outDir": "build",
"rootDir": "src"
},
"include": ["./src"],
}
Loading