1
0
mirror of https://gitcode.com/gh_mirrors/re/react-native-pushy.git synced 2025-09-18 01:56:11 +08:00
Code Issues Packages Projects Releases Wiki Activity GitHub Gitee

Compare commits

..

119 Commits

Author SHA1 Message Date
sunnylqm
7c8e75587b v5.11.0-beta1 2021-01-20 23:38:42 +08:00
sunnylqm
84fee71df6 Fix cleanup target dir 2021-01-20 23:38:21 +08:00
sunnylqm
db548d03dd v5.11.0-beta0 2021-01-20 23:15:05 +08:00
sunnylqm
fc82addd75 Check diff and patch existence 2021-01-20 23:05:35 +08:00
sunnylqm
90e7c00cf6 Clean up files if task failed 2021-01-20 18:34:58 +08:00
sunnylqm
eea7ff26f2 v5.10.0 2020-12-18 11:17:07 +08:00
sunnylqm
684dc8267d Back compatibility 2020-12-18 11:16:49 +08:00
sunnylqm
5786bf8132 v5.10.0-beta0 2020-12-17 00:15:48 +08:00
sunnylqm
1bd5fbbc94 Refactor ios rollback 2020-12-17 00:12:30 +08:00
sunnylqm
cd2eb9417a Update gitignore 2020-11-03 16:17:24 +08:00
sunnylqm
7d8730f590 v5.9.3 2020-11-03 16:17:03 +08:00
sunnylqm
e3081a02db Rename file_paths.xml to avoid overwrite 2020-11-03 15:52:59 +08:00
sunnylqm
ec8c475c54 Add webview test 2020-10-21 15:51:51 +08:00
sunnylqm
ac18785e50 Add webview 2020-10-21 08:26:55 +08:00
sunnylqm
e80317415c Upgrade 0.63.3 2020-10-21 08:12:52 +08:00
sunnylqm
f5c307deca v5.9.1 2020-10-06 23:41:08 +08:00
sunnylqm
b4feae292d Fix index.d.ts 2020-10-06 23:40:51 +08:00
sunnylqm
a1c2679427 v5.9.0 2020-09-27 22:32:30 +08:00
sunnylqm
17ff366f51 Implement download and install apk 2020-09-27 22:16:27 +08:00
sunnylqm
3c5792423e v5.8.3 2020-09-24 22:45:12 +08:00
sunnylqm
d071fbfc2b Fix ios bridge reload 2020-09-24 22:44:50 +08:00
sunnylqm
17dffa1eb5 Rename hashname -> hash 2020-09-24 22:44:37 +08:00
sunnylqm
bcd61315e9 v5.8.2 2020-09-24 19:36:09 +08:00
sunnylqm
f626cc1933 Fix download event and remove unzip event 2020-09-24 19:31:27 +08:00
sunnylqm
4ba3f25972 v5.8.1 2020-09-17 23:20:32 +08:00
sunnylqm
cd695b1ffb Revert rctreloadcommand 2020-09-17 23:18:17 +08:00
sunnylqm
37a1a5a18b Merge branch 'master' of github.com:reactnativecn/react-native-pushy
# Conflicts:
#	package.json
2020-09-16 17:35:16 +08:00
sunnylqm
5a2ebf8df7 Fix example setState 2020-09-16 14:42:45 +08:00
sunnylqm
95ba5f364b v5.8.0 2020-09-16 14:37:32 +08:00
sunnylqm
841228c341 Fix example 2020-09-16 14:36:37 +08:00
sunnylqm
93049f1e54 v5.8.0-beta1 2020-09-16 13:02:53 +08:00
sunnylqm
a966655faf Implement download progress 2020-09-16 13:01:14 +08:00
sunnylqm
a4052091e0 Update example to 0.63 2020-09-01 15:29:13 +08:00
sunnylqm
ebb5defb10 v5.7.2 2020-09-01 10:51:43 +08:00
sunnylqm
a509ff8e30 Cleanup 2020-09-01 10:51:15 +08:00
sunnylqm
59fcba15ee Implement blockupdate on android 2020-08-31 18:39:03 +08:00
sunnylqm
c6f9bb20a1 Add client info and uuid 2020-08-31 11:47:08 +08:00
sunnylqm
9e6c7ea769 setBlockUpdate 2020-08-31 01:17:28 +08:00
sunnylqm
f461c8ddd2 Update endpoint and example 2020-08-26 00:44:34 +08:00
sunnylqm
4cbeb7bec4 v5.7.0 2020-08-13 00:32:52 +08:00
sunnylqm
ed7f5ac606 Detect android bundle url 2020-08-13 00:32:07 +08:00
sunnylqm
6abb2c7a5d Unify download progress event 2020-08-09 00:08:30 +08:00
sunnylqm
a4a372f17e Revert "Exclude backup"
This reverts commit 2d805fb38e.
2020-08-08 21:09:15 +08:00
sunnylqm
48693e3b45 v5.7.0-beta1 2020-08-04 13:05:14 +08:00
sunnylqm
2d805fb38e Exclude backup 2020-08-04 13:03:53 +08:00
sunnylqm
92c421b30f Fix typo 2020-08-04 13:03:37 +08:00
sunnylqm
dbd0880295 Fix custom endpoints 2020-07-28 23:34:06 +08:00
sunnylqm
f110df1206 Add custom endpoints 2020-07-28 23:15:42 +08:00
sunnylqm
d17eac48f0 v5.6.0 2020-05-26 23:29:14 +08:00
sunnylqm
8e1a639dda Merge branch 'fix-app-json' 2020-05-25 15:00:21 +08:00
sunnylqm
5999670917 Redirect doc 2020-05-24 17:17:07 +08:00
sunnylqm
0f6875d8d5 v5.6.0-beta.1 2020-05-23 00:34:47 +08:00
sunnylqm
3752ad4e5d Try to fix app.json issue 2020-05-23 00:17:30 +08:00
Sunny Luo
a2f01b6213 Update README.md 2020-05-21 22:21:27 +08:00
Sunny Luo
c2499f799b Update README.md 2020-05-21 22:21:10 +08:00
sunnylqm
680f77a8d8 v5.5.10 2020-04-30 18:09:05 +08:00
sunnylqm
eaae1286f4 Add link error hint 2020-04-30 17:59:28 +08:00
sunnylqm
20a21ae894 v5.5.9 2020-04-14 22:59:19 +08:00
sunnylqm
5abe7b181b Fix ios script path 2020-04-14 22:58:53 +08:00
Sunny Luo
e859238d97 Update changelog.md 2020-04-07 16:38:06 +08:00
sunnylqm
7796090e72 v5.5.8 2020-04-02 12:09:38 +08:00
sunnylqm
f07be8cf0b Merge branch 'customInstanceManager' 2020-04-02 12:09:13 +08:00
sunnylqm
7c8f2d493f import instancemanager 2020-04-02 12:06:32 +08:00
sunnylqm
437eb4f10f v5.5.7 2020-04-02 00:48:46 +08:00
sunnylqm
5053a89cfa backup domain 2020-04-02 00:48:19 +08:00
Sunny Luo
4a84622c04 Update domains.json 2020-04-02 00:44:18 +08:00
Sunny Luo
cf812eacda Update domains.json 2020-04-02 00:43:21 +08:00
sunnylqm
6a031f598f Allow custom instance manager 2020-03-13 13:13:37 +08:00
sunnylqm
7ecf879ac9 Merge branch 'master' of github.com:reactnativecn/react-native-pushy 2020-03-13 11:28:09 +08:00
Sunny Luo
c779befa86 Update README.md 2020-03-13 09:36:47 +08:00
Sunny Luo
19f060abf5 Update react-native-update.podspec 2020-03-03 14:55:32 +08:00
sunnylqm
b9c655312e Fix typo in patch extension 2020-02-27 00:00:49 +08:00
sunnylqm
2ad4635bc6 Allow http for android example 2020-02-27 00:00:05 +08:00
sunnylqm
0d3f95d2bd Move cli to separated repo 2020-02-19 00:34:36 +08:00
sunnylqm
36568356dc v5.5.6 2020-02-11 11:56:59 +08:00
sunnylqm
1a3681f308 Update changelog 2020-02-11 11:56:38 +08:00
sunnylqm
81f6e57413 Check ppk extension when publish 2020-02-11 11:53:30 +08:00
sunnylqm
46c7aa88dd Clarify packageVersion and currentVersion 2020-02-11 11:32:18 +08:00
Sunny Luo
22a2902f71 Fix download buffer overflow 2020-02-07 22:28:17 +08:00
sunnylqm
e9755392ec Force reset cache when bundle 2020-01-20 15:05:12 +08:00
sunnylqm
c28eec436d immediate reject 2020-01-19 00:13:50 +08:00
sunnylqm
c46fb1be3a v5.5.5 2020-01-18 23:57:20 +08:00
sunnylqm
1d0fb7d260 Backup domains 2020-01-18 23:55:10 +08:00
sunnylqm
bdd97ac6ac domains.json 2020-01-18 21:46:55 +08:00
sunnylqm
ba3e92e3b8 5.5.4 changelog 2020-01-13 22:25:59 +08:00
sunnylqm
3939607f49 v5.5.4 2020-01-13 22:23:10 +08:00
sunnylqm
8d06235ea3 Check android bundle file before reload 2020-01-13 21:58:33 +08:00
sunnylqm
fa754ef8a2 Check bundlejs before switch 2020-01-09 17:45:44 +08:00
sunnylqm
5a5884c8fa Fix doc link 2019-12-29 12:42:05 +08:00
sunnylqm
8bd92f2c0e Fix doc home page 2019-12-29 12:39:45 +08:00
sunnylqm
3ac2e3ef05 文档优化 2019-12-29 12:36:08 +08:00
Sunny Luo
523cbdd7e1 Update README.md 2019-12-23 10:28:02 +08:00
sunnylqm
dece044a54 Bump 5.5.3 2019-12-18 18:06:08 +08:00
sunnylqm
a35c98ed4d v5.5.3 2019-12-18 18:05:47 +08:00
sunnylqm
dcea576fff Ignore hermes output to avoid buffer issue 2019-12-18 18:04:08 +08:00
Sunny Luo
201f11e770 Update guide.md 2019-12-14 12:26:04 +08:00
Sunny Luo
3a86218a48 Update guide.md 2019-12-14 12:24:39 +08:00
sunnylqm
5a5e27037c Update yauzl 2019-12-13 11:01:04 +08:00
sunnylqm
9b4016ba0a Update changelog 2019-12-06 15:27:26 +08:00
sunnylqm
5202cbec96 v5.5.2 2019-12-06 15:25:46 +08:00
sunnylqm
d7c7e27eaa 修复use_frameworks时无法获取编译时间戳的问题 2019-12-06 15:24:49 +08:00
Sunny Luo
e9e60bc5c6 Merge pull request #299 from nfq6612/progress
android 下载进度发送事件发出和发送事件频率优化
2019-12-04 11:12:22 +08:00
nonghuaqiang
a93875884c android 下载进度发送事件发出和发送事件频率优化 2019-12-03 12:34:02 +08:00
sunnylqm
f20263b827 v5.5.1 2019-11-25 16:09:34 +08:00
sunnylqm
8a74678b9c Add missing files 2019-11-25 16:09:05 +08:00
Sunny Luo
17b7920100 Merge pull request #296 from reactnativecn/dependabot/npm_and_yarn/brace-expansion-1.1.11
Bump brace-expansion from 1.1.6 to 1.1.11
2019-11-24 22:30:18 +08:00
sunnylqm
4d44eb858f Remove unused fs-promise 2019-11-24 22:30:04 +08:00
sunnylqm
4d502a4e73 v5.5.0 2019-11-24 22:01:48 +08:00
sunnylqm
7488a7fb9f Update changelog 2019-11-24 22:01:00 +08:00
dependabot[bot]
74ef45056e Bump brace-expansion from 1.1.6 to 1.1.11
Bumps [brace-expansion](https://github.com/juliangruber/brace-expansion) from 1.1.6 to 1.1.11.
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/v1.1.6...1.1.11)

Signed-off-by: dependabot[bot] <support@github.com>
2019-11-24 13:58:51 +00:00
sunnylqm
6b254582b6 Read build time for ipa 2019-11-24 21:58:12 +08:00
sunnylqm
737060e962 Read build time in local-cli for apk 2019-11-21 00:02:00 +08:00
sunnylqm
b848259905 v5.4.0 2019-11-16 00:34:14 +08:00
sunnylqm
6c087b473b Update readme 2019-11-16 00:33:34 +08:00
sunnylqm
ddc12186b0 Rename 2019-11-16 00:31:30 +08:00
sunnylqm
b489199572 v5.3.2 2019-10-25 19:05:22 +08:00
sunnylqm
1cb974ca61 修复ios禁用时报错 2019-10-25 19:05:01 +08:00
sunnylqm
b44225f9bd Fix readme format 2019-10-25 12:12:56 +08:00
sunnylqm
7586a5854d Update readme 2019-10-25 12:11:00 +08:00
118 changed files with 4492 additions and 8047 deletions

13
.gitignore vendored
View File

@@ -1,10 +1,6 @@
/.idea .vscode
/node_modules android/build
/local-cli/lib android/obj
/react-native-pushy-cli/node_modules
/react-native-pushy-cli/lib
/android/build
/android/obj
*.iml *.iml
# OSX # OSX
@@ -45,4 +41,5 @@ local.properties
node_modules/ node_modules/
npm-debug.log npm-debug.log
Example/**/update.json Example/**/update.json
yarn-error.log

View File

@@ -1,13 +1,11 @@
/.idea .babelrc
/.babelrc .npmignore
/.npmignore .eslintrc
/.eslintrc .nvmrc
/.nvmrc .travis.yml
/.travis.yml Example
/local-cli/src android/build
/react-native-pushy-cli .vscode
/Example
/android/build
# OSX # OSX
# #
@@ -45,4 +43,7 @@ node_modules/
npm-debug.log npm-debug.log
Example Example
yarn.lock yarn.lock
android/jni android/jni
domains.json
endpoints.json

View File

@@ -1,5 +1,4 @@
module.exports = { module.exports = {
printWidth: 120,
trailingComma: 'all', trailingComma: 'all',
singleQuote: true, singleQuote: true,
}; };

View File

@@ -1,53 +0,0 @@
### 最近更新
## 5.3.0 (2019-09-19)
1. 替换 apk reader 以避免某些环境读取 apk 版本号报错的问题
## 5.2.9 (2019-09-18)
1. 添加 proguard 混淆规则以解决开启混淆后闪退的问题
## 5.2.8
1. 解决某些情形下 Android 调用 switchVersion 不能重启的问题
## 5.2.7
1. 改进 windows 端打更新包的兼容性(部分 windows 机器上会产生空 ppk 文件)
## 5.2.4
1. 支持 RN 0.61 的 hermes路径变化
2. iOS 端使用第三方的 SSZipArchive 以减少重名冲突
## 5.2.2
1. 修复一处导致 iOS 回滚的问题
## 5.2.1
1. 检测如果开启了 hermes则自动编译为 hermes 字节码格式
## 5.2.0
1. 添加 typescript 声明
2. 支持 cocoapods
## 5.1.9
1. 重写 bundle 命令以提升版本兼容性
2. 改进命令行的输出样式
## 5.1.8
1. 服务器迁移到 https
2. android 支持 64 位
## 5.1.6
解决 Android 热更新后部分图片丢失问题:
同一个项目中放置了多个完全相同的文件,在 5.1.0 至 5.1.5 之间的版本中,更新后有时会出现其中的部分无法显示。此问题在 5.1.6 版本修复。
修复此问题涉及原生部分,需要重新打包。

View File

@@ -1,75 +0,0 @@
[ignore]
; We fork some components by platform
.*/*[.]android.js
; Ignore "BUCK" generated dirs
<PROJECT_ROOT>/\.buckd/
; Ignore polyfills
node_modules/react-native/Libraries/polyfills/.*
; These should not be required directly
; require from fbjs/lib instead: require('fbjs/lib/warning')
node_modules/warning/.*
; Flow doesn't support platforms
.*/Libraries/Utilities/LoadingView.js
[untyped]
.*/node_modules/@react-native-community/cli/.*/.*
[include]
[libs]
node_modules/react-native/Libraries/react-native/react-native-interface.js
node_modules/react-native/flow/
[options]
emoji=true
esproposal.optional_chaining=enable
esproposal.nullish_coalescing=enable
module.file_ext=.js
module.file_ext=.json
module.file_ext=.ios.js
munge_underscores=true
module.name_mapper='^react-native$' -> '<PROJECT_ROOT>/node_modules/react-native/Libraries/react-native/react-native-implementation'
module.name_mapper='^react-native/\(.*\)$' -> '<PROJECT_ROOT>/node_modules/react-native/\1'
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '<PROJECT_ROOT>/node_modules/react-native/Libraries/Image/RelativeImageStub'
suppress_type=$FlowIssue
suppress_type=$FlowFixMe
suppress_type=$FlowFixMeProps
suppress_type=$FlowFixMeState
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
[lints]
sketchy-null-number=warn
sketchy-null-mixed=warn
sketchy-number=warn
untyped-type-import=warn
nonstrict-import=warn
deprecated-type=warn
unsafe-getters-setters=warn
inexact-spread=warn
unnecessary-invariant=warn
signature-verification-failure=warn
deprecated-utility=error
[strict]
deprecated-type
nonstrict-import
sketchy-null
unclear-type
unsafe-getters-setters
untyped-import
untyped-type-import
[version]
^0.105.0

View File

@@ -15,10 +15,12 @@ import com.android.build.OutputFile
* // the name of the generated asset file containing your JS bundle * // the name of the generated asset file containing your JS bundle
* bundleAssetName: "index.android.bundle", * bundleAssetName: "index.android.bundle",
* *
* // the entry file for bundle generation * // the entry file for bundle generation. If none specified and
* // "index.android.js" exists, it will be used. Otherwise "index.js" is
* // default. Can be overridden with ENTRY_FILE environment variable.
* entryFile: "index.android.js", * entryFile: "index.android.js",
* *
* // https://facebook.github.io/react-native/docs/performance#enable-the-ram-format * // https://reactnative.dev/docs/performance#enable-the-ram-format
* bundleCommand: "ram-bundle", * bundleCommand: "ram-bundle",
* *
* // whether to bundle JS and assets in debug mode * // whether to bundle JS and assets in debug mode
@@ -76,7 +78,6 @@ import com.android.build.OutputFile
*/ */
project.ext.react = [ project.ext.react = [
entryFile: "index.js",
enableHermes: false, // clean and rebuild if changing enableHermes: false, // clean and rebuild if changing
] ]
@@ -155,13 +156,15 @@ android {
signingConfig signingConfigs.debug signingConfig signingConfigs.debug
} }
release { release {
crunchPngs false
// Caution! In production, you need to generate your own keystore file. // Caution! In production, you need to generate your own keystore file.
// see https://facebook.github.io/react-native/docs/signed-apk-android. // see https://reactnative.dev/docs/signed-apk-android.
signingConfig signingConfigs.debug signingConfig signingConfigs.debug
minifyEnabled enableProguardInReleaseBuilds minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
} }
} }
// applicationVariants are e.g. debug, release // applicationVariants are e.g. debug, release
applicationVariants.all { variant -> applicationVariants.all { variant ->
variant.outputs.each { output -> variant.outputs.each { output ->
@@ -180,8 +183,24 @@ android {
dependencies { dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"]) implementation fileTree(dir: "libs", include: ["*.jar"])
//noinspection GradleDynamicVersion
implementation "com.facebook.react:react-native:+" // From node_modules implementation "com.facebook.react:react-native:+" // From node_modules
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
exclude group:'com.facebook.fbjni'
}
debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
exclude group:'com.facebook.flipper'
exclude group:'com.squareup.okhttp3', module:'okhttp'
}
debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
exclude group:'com.facebook.flipper'
}
if (enableHermes) { if (enableHermes) {
def hermesPath = "../../node_modules/hermes-engine/android/"; def hermesPath = "../../node_modules/hermes-engine/android/";
debugImplementation files(hermesPath + "hermes-debug.aar") debugImplementation files(hermesPath + "hermes-debug.aar")
@@ -198,4 +217,4 @@ task copyDownloadableDepsToLibs(type: Copy) {
into 'libs' into 'libs'
} }
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)

View File

@@ -0,0 +1,72 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
* directory of this source tree.
*/
package com.testhotupdate;
import android.content.Context;
import com.facebook.flipper.android.AndroidFlipperClient;
import com.facebook.flipper.android.utils.FlipperUtils;
import com.facebook.flipper.core.FlipperClient;
import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
import com.facebook.flipper.plugins.inspector.DescriptorMapping;
import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.modules.network.NetworkingModule;
import okhttp3.OkHttpClient;
public class ReactNativeFlipper {
public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
if (FlipperUtils.shouldEnableFlipper(context)) {
final FlipperClient client = AndroidFlipperClient.getInstance(context);
client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
client.addPlugin(new ReactFlipperPlugin());
client.addPlugin(new DatabasesFlipperPlugin(context));
client.addPlugin(new SharedPreferencesFlipperPlugin(context));
client.addPlugin(CrashReporterPlugin.getInstance());
NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
NetworkingModule.setCustomClientBuilder(
new NetworkingModule.CustomClientBuilder() {
@Override
public void apply(OkHttpClient.Builder builder) {
builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
}
});
client.addPlugin(networkFlipperPlugin);
client.start();
// Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
// Hence we run if after all native modules have been initialized
ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
if (reactContext == null) {
reactInstanceManager.addReactInstanceEventListener(
new ReactInstanceManager.ReactInstanceEventListener() {
@Override
public void onReactContextInitialized(ReactContext reactContext) {
reactInstanceManager.removeReactInstanceEventListener(this);
reactContext.runOnNativeModulesQueueThread(
new Runnable() {
@Override
public void run() {
client.addPlugin(new FrescoFlipperPlugin());
}
});
}
});
} else {
client.addPlugin(new FrescoFlipperPlugin());
}
}
}
}

View File

@@ -4,6 +4,7 @@
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<application <application
android:usesCleartextTraffic="true"
android:name=".MainApplication" android:name=".MainApplication"
android:label="@string/app_name" android:label="@string/app_name"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
@@ -13,7 +14,8 @@
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:label="@string/app_name" android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />

View File

@@ -4,6 +4,7 @@ import android.app.Application;
import android.content.Context; import android.content.Context;
import com.facebook.react.PackageList; import com.facebook.react.PackageList;
import com.facebook.react.ReactApplication; import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage; import com.facebook.react.ReactPackage;
import com.facebook.soloader.SoLoader; import com.facebook.soloader.SoLoader;
@@ -50,23 +51,28 @@ public class MainApplication extends Application implements ReactApplication {
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
SoLoader.init(this, /* native exopackage */ false); SoLoader.init(this, /* native exopackage */ false);
initializeFlipper(this); // Remove this line if you don't want Flipper enabled initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
} }
/** /**
* Loads Flipper in React Native templates. * Loads Flipper in React Native templates. Call this in the onCreate method with something like
* initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
* *
* @param context * @param context
* @param reactInstanceManager
*/ */
private static void initializeFlipper(Context context) { private static void initializeFlipper(
Context context, ReactInstanceManager reactInstanceManager) {
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
try { try {
/* /*
We use reflection here to pick up the class that initializes Flipper, We use reflection here to pick up the class that initializes Flipper,
since Flipper library is not available in release mode since Flipper library is not available in release mode
*/ */
Class<?> aClass = Class.forName("com.facebook.flipper.ReactNativeFlipper"); Class<?> aClass = Class.forName("com.testhotupdate.ReactNativeFlipper");
aClass.getMethod("initializeFlipper", Context.class).invoke(null, context); aClass
.getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
.invoke(null, context, reactInstanceManager);
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
e.printStackTrace(); e.printStackTrace();
} catch (NoSuchMethodException e) { } catch (NoSuchMethodException e) {

View File

@@ -2,17 +2,17 @@
buildscript { buildscript {
ext { ext {
buildToolsVersion = "28.0.3" buildToolsVersion = "29.0.2"
minSdkVersion = 16 minSdkVersion = 21
compileSdkVersion = 28 compileSdkVersion = 29
targetSdkVersion = 28 targetSdkVersion = 29
} }
repositories { repositories {
google() google()
jcenter() jcenter()
} }
dependencies { dependencies {
classpath('com.android.tools.build:gradle:3.5.1') classpath('com.android.tools.build:gradle:3.5.3')
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files
@@ -33,6 +33,6 @@ allprojects {
google() google()
jcenter() jcenter()
maven { url 'https://jitpack.io' } maven { url 'https://www.jitpack.io' }
} }
} }

View File

@@ -17,5 +17,12 @@
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true # org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true android.enableJetifier=true
# Version of flipper SDK to use with React Native
FLIPPER_VERSION=0.54.0

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-all.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@@ -7,7 +7,7 @@
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
# You may obtain a copy of the License at # You may obtain a copy of the License at
# #
# http://www.apache.org/licenses/LICENSE-2.0 # https://www.apache.org/licenses/LICENSE-2.0
# #
# Unless required by applicable law or agreed to in writing, software # Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, # distributed under the License is distributed on an "AS IS" BASIS,
@@ -125,8 +125,8 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi fi
# For Cygwin, switch paths to Windows format before running java # For Cygwin or MSYS, switch paths to Windows format before running java
if $cygwin ; then if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"` APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"` JAVACMD=`cygpath --unix "$JAVACMD"`
@@ -154,19 +154,19 @@ if $cygwin ; then
else else
eval `echo args$i`="\"$arg\"" eval `echo args$i`="\"$arg\""
fi fi
i=$((i+1)) i=`expr $i + 1`
done done
case $i in case $i in
(0) set -- ;; 0) set -- ;;
(1) set -- "$args0" ;; 1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;; 2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;; 3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;; 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac esac
fi fi
@@ -175,14 +175,9 @@ save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " " echo " "
} }
APP_ARGS=$(save "$@") APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules # Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong exec "$JAVACMD" "$@"
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

View File

@@ -5,7 +5,7 @@
@rem you may not use this file except in compliance with the License. @rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at @rem You may obtain a copy of the License at
@rem @rem
@rem http://www.apache.org/licenses/LICENSE-2.0 @rem https://www.apache.org/licenses/LICENSE-2.0
@rem @rem
@rem Unless required by applicable law or agreed to in writing, software @rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS, @rem distributed under the License is distributed on an "AS IS" BASIS,
@@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@@ -97,4 +100,4 @@ exit /b 1
:mainEnd :mainEnd
if "%OS%"=="Windows_NT" endlocal if "%OS%"=="Windows_NT" endlocal
:omega :omega

View File

@@ -1,39 +1,20 @@
platform :ios, '9.0' require_relative '../node_modules/react-native/scripts/react_native_pods'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
platform :ios, '10.0'
target 'testHotUpdate' do target 'testHotUpdate' do
# Pods for testHotUpdate config = use_native_modules!
pod 'FBLazyVector', :path => "../node_modules/react-native/Libraries/FBLazyVector"
pod 'FBReactNativeSpec', :path => "../node_modules/react-native/Libraries/FBReactNativeSpec"
pod 'RCTRequired', :path => "../node_modules/react-native/Libraries/RCTRequired"
pod 'RCTTypeSafety', :path => "../node_modules/react-native/Libraries/TypeSafety"
pod 'React', :path => '../node_modules/react-native/'
pod 'React-Core', :path => '../node_modules/react-native/'
pod 'React-CoreModules', :path => '../node_modules/react-native/React/CoreModules'
pod 'React-Core/DevSupport', :path => '../node_modules/react-native/'
pod 'React-RCTActionSheet', :path => '../node_modules/react-native/Libraries/ActionSheetIOS'
pod 'React-RCTAnimation', :path => '../node_modules/react-native/Libraries/NativeAnimation'
pod 'React-RCTBlob', :path => '../node_modules/react-native/Libraries/Blob'
pod 'React-RCTImage', :path => '../node_modules/react-native/Libraries/Image'
pod 'React-RCTLinking', :path => '../node_modules/react-native/Libraries/LinkingIOS'
pod 'React-RCTNetwork', :path => '../node_modules/react-native/Libraries/Network'
pod 'React-RCTSettings', :path => '../node_modules/react-native/Libraries/Settings'
pod 'React-RCTText', :path => '../node_modules/react-native/Libraries/Text'
pod 'React-RCTVibration', :path => '../node_modules/react-native/Libraries/Vibration'
pod 'React-Core/RCTWebSocket', :path => '../node_modules/react-native/'
pod 'React-cxxreact', :path => '../node_modules/react-native/ReactCommon/cxxreact' use_react_native!(:path => config["reactNativePath"])
pod 'React-jsi', :path => '../node_modules/react-native/ReactCommon/jsi'
pod 'React-jsiexecutor', :path => '../node_modules/react-native/ReactCommon/jsiexecutor'
pod 'React-jsinspector', :path => '../node_modules/react-native/ReactCommon/jsinspector'
pod 'ReactCommon/jscallinvoker', :path => "../node_modules/react-native/ReactCommon"
pod 'ReactCommon/turbomodule/core', :path => "../node_modules/react-native/ReactCommon"
pod 'Yoga', :path => '../node_modules/react-native/ReactCommon/yoga'
pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec' # Enables Flipper.
pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec' #
pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec' # Note that if you have use_frameworks! enabled, Flipper will not work and
# you should disable these next few lines.
use_native_modules! use_flipper!('Flipper' => '0.54.0')
post_install do |installer|
flipper_post_install(installer)
end
end end

View File

@@ -1,245 +1,348 @@
PODS: PODS:
- boost-for-react-native (1.63.0) - boost-for-react-native (1.63.0)
- CocoaAsyncSocket (7.6.4)
- CocoaLibEvent (1.0.0)
- DoubleConversion (1.1.6) - DoubleConversion (1.1.6)
- FBLazyVector (0.61.2) - FBLazyVector (0.63.3)
- FBReactNativeSpec (0.61.2): - FBReactNativeSpec (0.63.3):
- Folly (= 2018.10.22.00) - Folly (= 2020.01.13.00)
- RCTRequired (= 0.61.2) - RCTRequired (= 0.63.3)
- RCTTypeSafety (= 0.61.2) - RCTTypeSafety (= 0.63.3)
- React-Core (= 0.61.2) - React-Core (= 0.63.3)
- React-jsi (= 0.61.2) - React-jsi (= 0.63.3)
- ReactCommon/turbomodule/core (= 0.61.2) - ReactCommon/turbomodule/core (= 0.63.3)
- Folly (2018.10.22.00): - Flipper (0.54.0):
- Flipper-Folly (~> 2.2)
- Flipper-RSocket (~> 1.1)
- Flipper-DoubleConversion (1.1.7)
- Flipper-Folly (2.2.0):
- boost-for-react-native
- CocoaLibEvent (~> 1.0)
- Flipper-DoubleConversion
- Flipper-Glog
- OpenSSL-Universal (= 1.0.2.19)
- Flipper-Glog (0.3.6)
- Flipper-PeerTalk (0.0.4)
- Flipper-RSocket (1.1.0):
- Flipper-Folly (~> 2.2)
- FlipperKit (0.54.0):
- FlipperKit/Core (= 0.54.0)
- FlipperKit/Core (0.54.0):
- Flipper (~> 0.54.0)
- FlipperKit/CppBridge
- FlipperKit/FBCxxFollyDynamicConvert
- FlipperKit/FBDefines
- FlipperKit/FKPortForwarding
- FlipperKit/CppBridge (0.54.0):
- Flipper (~> 0.54.0)
- FlipperKit/FBCxxFollyDynamicConvert (0.54.0):
- Flipper-Folly (~> 2.2)
- FlipperKit/FBDefines (0.54.0)
- FlipperKit/FKPortForwarding (0.54.0):
- CocoaAsyncSocket (~> 7.6)
- Flipper-PeerTalk (~> 0.0.4)
- FlipperKit/FlipperKitHighlightOverlay (0.54.0)
- FlipperKit/FlipperKitLayoutPlugin (0.54.0):
- FlipperKit/Core
- FlipperKit/FlipperKitHighlightOverlay
- FlipperKit/FlipperKitLayoutTextSearchable
- YogaKit (~> 1.18)
- FlipperKit/FlipperKitLayoutTextSearchable (0.54.0)
- FlipperKit/FlipperKitNetworkPlugin (0.54.0):
- FlipperKit/Core
- FlipperKit/FlipperKitReactPlugin (0.54.0):
- FlipperKit/Core
- FlipperKit/FlipperKitUserDefaultsPlugin (0.54.0):
- FlipperKit/Core
- FlipperKit/SKIOSNetworkPlugin (0.54.0):
- FlipperKit/Core
- FlipperKit/FlipperKitNetworkPlugin
- Folly (2020.01.13.00):
- boost-for-react-native - boost-for-react-native
- DoubleConversion - DoubleConversion
- Folly/Default (= 2018.10.22.00) - Folly/Default (= 2020.01.13.00)
- glog - glog
- Folly/Default (2018.10.22.00): - Folly/Default (2020.01.13.00):
- boost-for-react-native - boost-for-react-native
- DoubleConversion - DoubleConversion
- glog - glog
- glog (0.3.5) - glog (0.3.5)
- RCTRequired (0.61.2) - OpenSSL-Universal (1.0.2.19):
- RCTTypeSafety (0.61.2): - OpenSSL-Universal/Static (= 1.0.2.19)
- FBLazyVector (= 0.61.2) - OpenSSL-Universal/Static (1.0.2.19)
- Folly (= 2018.10.22.00) - RCTRequired (0.63.3)
- RCTRequired (= 0.61.2) - RCTTypeSafety (0.63.3):
- React-Core (= 0.61.2) - FBLazyVector (= 0.63.3)
- React (0.61.2): - Folly (= 2020.01.13.00)
- React-Core (= 0.61.2) - RCTRequired (= 0.63.3)
- React-Core/DevSupport (= 0.61.2) - React-Core (= 0.63.3)
- React-Core/RCTWebSocket (= 0.61.2) - React (0.63.3):
- React-RCTActionSheet (= 0.61.2) - React-Core (= 0.63.3)
- React-RCTAnimation (= 0.61.2) - React-Core/DevSupport (= 0.63.3)
- React-RCTBlob (= 0.61.2) - React-Core/RCTWebSocket (= 0.63.3)
- React-RCTImage (= 0.61.2) - React-RCTActionSheet (= 0.63.3)
- React-RCTLinking (= 0.61.2) - React-RCTAnimation (= 0.63.3)
- React-RCTNetwork (= 0.61.2) - React-RCTBlob (= 0.63.3)
- React-RCTSettings (= 0.61.2) - React-RCTImage (= 0.63.3)
- React-RCTText (= 0.61.2) - React-RCTLinking (= 0.63.3)
- React-RCTVibration (= 0.61.2) - React-RCTNetwork (= 0.63.3)
- React-Core (0.61.2): - React-RCTSettings (= 0.63.3)
- Folly (= 2018.10.22.00) - React-RCTText (= 0.63.3)
- React-RCTVibration (= 0.63.3)
- React-callinvoker (0.63.3)
- React-Core (0.63.3):
- Folly (= 2020.01.13.00)
- glog - glog
- React-Core/Default (= 0.61.2) - React-Core/Default (= 0.63.3)
- React-cxxreact (= 0.61.2) - React-cxxreact (= 0.63.3)
- React-jsi (= 0.61.2) - React-jsi (= 0.63.3)
- React-jsiexecutor (= 0.61.2) - React-jsiexecutor (= 0.63.3)
- Yoga - Yoga
- React-Core/CoreModulesHeaders (0.61.2): - React-Core/CoreModulesHeaders (0.63.3):
- Folly (= 2018.10.22.00) - Folly (= 2020.01.13.00)
- glog - glog
- React-Core/Default - React-Core/Default
- React-cxxreact (= 0.61.2) - React-cxxreact (= 0.63.3)
- React-jsi (= 0.61.2) - React-jsi (= 0.63.3)
- React-jsiexecutor (= 0.61.2) - React-jsiexecutor (= 0.63.3)
- Yoga - Yoga
- React-Core/Default (0.61.2): - React-Core/Default (0.63.3):
- Folly (= 2018.10.22.00) - Folly (= 2020.01.13.00)
- glog - glog
- React-cxxreact (= 0.61.2) - React-cxxreact (= 0.63.3)
- React-jsi (= 0.61.2) - React-jsi (= 0.63.3)
- React-jsiexecutor (= 0.61.2) - React-jsiexecutor (= 0.63.3)
- Yoga - Yoga
- React-Core/DevSupport (0.61.2): - React-Core/DevSupport (0.63.3):
- Folly (= 2018.10.22.00) - Folly (= 2020.01.13.00)
- glog - glog
- React-Core/Default (= 0.61.2) - React-Core/Default (= 0.63.3)
- React-Core/RCTWebSocket (= 0.61.2) - React-Core/RCTWebSocket (= 0.63.3)
- React-cxxreact (= 0.61.2) - React-cxxreact (= 0.63.3)
- React-jsi (= 0.61.2) - React-jsi (= 0.63.3)
- React-jsiexecutor (= 0.61.2) - React-jsiexecutor (= 0.63.3)
- React-jsinspector (= 0.61.2) - React-jsinspector (= 0.63.3)
- Yoga - Yoga
- React-Core/RCTActionSheetHeaders (0.61.2): - React-Core/RCTActionSheetHeaders (0.63.3):
- Folly (= 2018.10.22.00) - Folly (= 2020.01.13.00)
- glog - glog
- React-Core/Default - React-Core/Default
- React-cxxreact (= 0.61.2) - React-cxxreact (= 0.63.3)
- React-jsi (= 0.61.2) - React-jsi (= 0.63.3)
- React-jsiexecutor (= 0.61.2) - React-jsiexecutor (= 0.63.3)
- Yoga - Yoga
- React-Core/RCTAnimationHeaders (0.61.2): - React-Core/RCTAnimationHeaders (0.63.3):
- Folly (= 2018.10.22.00) - Folly (= 2020.01.13.00)
- glog - glog
- React-Core/Default - React-Core/Default
- React-cxxreact (= 0.61.2) - React-cxxreact (= 0.63.3)
- React-jsi (= 0.61.2) - React-jsi (= 0.63.3)
- React-jsiexecutor (= 0.61.2) - React-jsiexecutor (= 0.63.3)
- Yoga - Yoga
- React-Core/RCTBlobHeaders (0.61.2): - React-Core/RCTBlobHeaders (0.63.3):
- Folly (= 2018.10.22.00) - Folly (= 2020.01.13.00)
- glog - glog
- React-Core/Default - React-Core/Default
- React-cxxreact (= 0.61.2) - React-cxxreact (= 0.63.3)
- React-jsi (= 0.61.2) - React-jsi (= 0.63.3)
- React-jsiexecutor (= 0.61.2) - React-jsiexecutor (= 0.63.3)
- Yoga - Yoga
- React-Core/RCTImageHeaders (0.61.2): - React-Core/RCTImageHeaders (0.63.3):
- Folly (= 2018.10.22.00) - Folly (= 2020.01.13.00)
- glog - glog
- React-Core/Default - React-Core/Default
- React-cxxreact (= 0.61.2) - React-cxxreact (= 0.63.3)
- React-jsi (= 0.61.2) - React-jsi (= 0.63.3)
- React-jsiexecutor (= 0.61.2) - React-jsiexecutor (= 0.63.3)
- Yoga - Yoga
- React-Core/RCTLinkingHeaders (0.61.2): - React-Core/RCTLinkingHeaders (0.63.3):
- Folly (= 2018.10.22.00) - Folly (= 2020.01.13.00)
- glog - glog
- React-Core/Default - React-Core/Default
- React-cxxreact (= 0.61.2) - React-cxxreact (= 0.63.3)
- React-jsi (= 0.61.2) - React-jsi (= 0.63.3)
- React-jsiexecutor (= 0.61.2) - React-jsiexecutor (= 0.63.3)
- Yoga - Yoga
- React-Core/RCTNetworkHeaders (0.61.2): - React-Core/RCTNetworkHeaders (0.63.3):
- Folly (= 2018.10.22.00) - Folly (= 2020.01.13.00)
- glog - glog
- React-Core/Default - React-Core/Default
- React-cxxreact (= 0.61.2) - React-cxxreact (= 0.63.3)
- React-jsi (= 0.61.2) - React-jsi (= 0.63.3)
- React-jsiexecutor (= 0.61.2) - React-jsiexecutor (= 0.63.3)
- Yoga - Yoga
- React-Core/RCTSettingsHeaders (0.61.2): - React-Core/RCTSettingsHeaders (0.63.3):
- Folly (= 2018.10.22.00) - Folly (= 2020.01.13.00)
- glog - glog
- React-Core/Default - React-Core/Default
- React-cxxreact (= 0.61.2) - React-cxxreact (= 0.63.3)
- React-jsi (= 0.61.2) - React-jsi (= 0.63.3)
- React-jsiexecutor (= 0.61.2) - React-jsiexecutor (= 0.63.3)
- Yoga - Yoga
- React-Core/RCTTextHeaders (0.61.2): - React-Core/RCTTextHeaders (0.63.3):
- Folly (= 2018.10.22.00) - Folly (= 2020.01.13.00)
- glog - glog
- React-Core/Default - React-Core/Default
- React-cxxreact (= 0.61.2) - React-cxxreact (= 0.63.3)
- React-jsi (= 0.61.2) - React-jsi (= 0.63.3)
- React-jsiexecutor (= 0.61.2) - React-jsiexecutor (= 0.63.3)
- Yoga - Yoga
- React-Core/RCTVibrationHeaders (0.61.2): - React-Core/RCTVibrationHeaders (0.63.3):
- Folly (= 2018.10.22.00) - Folly (= 2020.01.13.00)
- glog - glog
- React-Core/Default - React-Core/Default
- React-cxxreact (= 0.61.2) - React-cxxreact (= 0.63.3)
- React-jsi (= 0.61.2) - React-jsi (= 0.63.3)
- React-jsiexecutor (= 0.61.2) - React-jsiexecutor (= 0.63.3)
- Yoga - Yoga
- React-Core/RCTWebSocket (0.61.2): - React-Core/RCTWebSocket (0.63.3):
- Folly (= 2018.10.22.00) - Folly (= 2020.01.13.00)
- glog - glog
- React-Core/Default (= 0.61.2) - React-Core/Default (= 0.63.3)
- React-cxxreact (= 0.61.2) - React-cxxreact (= 0.63.3)
- React-jsi (= 0.61.2) - React-jsi (= 0.63.3)
- React-jsiexecutor (= 0.61.2) - React-jsiexecutor (= 0.63.3)
- Yoga - Yoga
- React-CoreModules (0.61.2): - React-CoreModules (0.63.3):
- FBReactNativeSpec (= 0.61.2) - FBReactNativeSpec (= 0.63.3)
- Folly (= 2018.10.22.00) - Folly (= 2020.01.13.00)
- RCTTypeSafety (= 0.61.2) - RCTTypeSafety (= 0.63.3)
- React-Core/CoreModulesHeaders (= 0.61.2) - React-Core/CoreModulesHeaders (= 0.63.3)
- React-RCTImage (= 0.61.2) - React-jsi (= 0.63.3)
- ReactCommon/turbomodule/core (= 0.61.2) - React-RCTImage (= 0.63.3)
- React-cxxreact (0.61.2): - ReactCommon/turbomodule/core (= 0.63.3)
- React-cxxreact (0.63.3):
- boost-for-react-native (= 1.63.0) - boost-for-react-native (= 1.63.0)
- DoubleConversion - DoubleConversion
- Folly (= 2018.10.22.00) - Folly (= 2020.01.13.00)
- glog - glog
- React-jsinspector (= 0.61.2) - React-callinvoker (= 0.63.3)
- React-jsi (0.61.2): - React-jsinspector (= 0.63.3)
- React-jsi (0.63.3):
- boost-for-react-native (= 1.63.0) - boost-for-react-native (= 1.63.0)
- DoubleConversion - DoubleConversion
- Folly (= 2018.10.22.00) - Folly (= 2020.01.13.00)
- glog - glog
- React-jsi/Default (= 0.61.2) - React-jsi/Default (= 0.63.3)
- React-jsi/Default (0.61.2): - React-jsi/Default (0.63.3):
- boost-for-react-native (= 1.63.0) - boost-for-react-native (= 1.63.0)
- DoubleConversion - DoubleConversion
- Folly (= 2018.10.22.00) - Folly (= 2020.01.13.00)
- glog - glog
- React-jsiexecutor (0.61.2): - React-jsiexecutor (0.63.3):
- DoubleConversion - DoubleConversion
- Folly (= 2018.10.22.00) - Folly (= 2020.01.13.00)
- glog - glog
- React-cxxreact (= 0.61.2) - React-cxxreact (= 0.63.3)
- React-jsi (= 0.61.2) - React-jsi (= 0.63.3)
- React-jsinspector (0.61.2) - React-jsinspector (0.63.3)
- react-native-update (5.3.0): - react-native-update (5.9.1):
- React - React
- react-native-update/BSDiff (= 5.3.0) - react-native-update/BSDiff (= 5.9.1)
- react-native-update/RCTHotUpdate (= 5.3.0) - react-native-update/RCTPushy (= 5.9.1)
- SSZipArchive - SSZipArchive
- react-native-update/BSDiff (5.3.0): - react-native-update/BSDiff (5.9.1):
- React - React
- SSZipArchive - SSZipArchive
- react-native-update/RCTHotUpdate (5.3.0): - react-native-update/RCTPushy (5.9.1):
- React - React
- SSZipArchive - SSZipArchive
- React-RCTActionSheet (0.61.2): - react-native-webview (10.9.2):
- React-Core/RCTActionSheetHeaders (= 0.61.2) - React-Core
- React-RCTAnimation (0.61.2): - React-RCTActionSheet (0.63.3):
- React-Core/RCTAnimationHeaders (= 0.61.2) - React-Core/RCTActionSheetHeaders (= 0.63.3)
- React-RCTBlob (0.61.2): - React-RCTAnimation (0.63.3):
- React-Core/RCTBlobHeaders (= 0.61.2) - FBReactNativeSpec (= 0.63.3)
- React-Core/RCTWebSocket (= 0.61.2) - Folly (= 2020.01.13.00)
- React-jsi (= 0.61.2) - RCTTypeSafety (= 0.63.3)
- React-RCTNetwork (= 0.61.2) - React-Core/RCTAnimationHeaders (= 0.63.3)
- React-RCTImage (0.61.2): - React-jsi (= 0.63.3)
- React-Core/RCTImageHeaders (= 0.61.2) - ReactCommon/turbomodule/core (= 0.63.3)
- React-RCTNetwork (= 0.61.2) - React-RCTBlob (0.63.3):
- React-RCTLinking (0.61.2): - FBReactNativeSpec (= 0.63.3)
- React-Core/RCTLinkingHeaders (= 0.61.2) - Folly (= 2020.01.13.00)
- React-RCTNetwork (0.61.2): - React-Core/RCTBlobHeaders (= 0.63.3)
- React-Core/RCTNetworkHeaders (= 0.61.2) - React-Core/RCTWebSocket (= 0.63.3)
- React-RCTSettings (0.61.2): - React-jsi (= 0.63.3)
- React-Core/RCTSettingsHeaders (= 0.61.2) - React-RCTNetwork (= 0.63.3)
- React-RCTText (0.61.2): - ReactCommon/turbomodule/core (= 0.63.3)
- React-Core/RCTTextHeaders (= 0.61.2) - React-RCTImage (0.63.3):
- React-RCTVibration (0.61.2): - FBReactNativeSpec (= 0.63.3)
- React-Core/RCTVibrationHeaders (= 0.61.2) - Folly (= 2020.01.13.00)
- ReactCommon/jscallinvoker (0.61.2): - RCTTypeSafety (= 0.63.3)
- React-Core/RCTImageHeaders (= 0.63.3)
- React-jsi (= 0.63.3)
- React-RCTNetwork (= 0.63.3)
- ReactCommon/turbomodule/core (= 0.63.3)
- React-RCTLinking (0.63.3):
- FBReactNativeSpec (= 0.63.3)
- React-Core/RCTLinkingHeaders (= 0.63.3)
- React-jsi (= 0.63.3)
- ReactCommon/turbomodule/core (= 0.63.3)
- React-RCTNetwork (0.63.3):
- FBReactNativeSpec (= 0.63.3)
- Folly (= 2020.01.13.00)
- RCTTypeSafety (= 0.63.3)
- React-Core/RCTNetworkHeaders (= 0.63.3)
- React-jsi (= 0.63.3)
- ReactCommon/turbomodule/core (= 0.63.3)
- React-RCTSettings (0.63.3):
- FBReactNativeSpec (= 0.63.3)
- Folly (= 2020.01.13.00)
- RCTTypeSafety (= 0.63.3)
- React-Core/RCTSettingsHeaders (= 0.63.3)
- React-jsi (= 0.63.3)
- ReactCommon/turbomodule/core (= 0.63.3)
- React-RCTText (0.63.3):
- React-Core/RCTTextHeaders (= 0.63.3)
- React-RCTVibration (0.63.3):
- FBReactNativeSpec (= 0.63.3)
- Folly (= 2020.01.13.00)
- React-Core/RCTVibrationHeaders (= 0.63.3)
- React-jsi (= 0.63.3)
- ReactCommon/turbomodule/core (= 0.63.3)
- ReactCommon/turbomodule/core (0.63.3):
- DoubleConversion - DoubleConversion
- Folly (= 2018.10.22.00) - Folly (= 2020.01.13.00)
- glog - glog
- React-cxxreact (= 0.61.2) - React-callinvoker (= 0.63.3)
- ReactCommon/turbomodule/core (0.61.2): - React-Core (= 0.63.3)
- DoubleConversion - React-cxxreact (= 0.63.3)
- Folly (= 2018.10.22.00) - React-jsi (= 0.63.3)
- glog - SSZipArchive (2.2.3)
- React-Core (= 0.61.2)
- React-cxxreact (= 0.61.2)
- React-jsi (= 0.61.2)
- ReactCommon/jscallinvoker (= 0.61.2)
- SSZipArchive (2.2.2)
- Yoga (1.14.0) - Yoga (1.14.0)
- YogaKit (1.18.1):
- Yoga (~> 1.14)
DEPENDENCIES: DEPENDENCIES:
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
- FBReactNativeSpec (from `../node_modules/react-native/Libraries/FBReactNativeSpec`) - FBReactNativeSpec (from `../node_modules/react-native/Libraries/FBReactNativeSpec`)
- Flipper (= 0.54.0)
- Flipper-DoubleConversion (= 1.1.7)
- Flipper-Folly (~> 2.2)
- Flipper-Glog (= 0.3.6)
- Flipper-PeerTalk (~> 0.0.4)
- Flipper-RSocket (~> 1.1)
- FlipperKit (= 0.54.0)
- FlipperKit/Core (= 0.54.0)
- FlipperKit/CppBridge (= 0.54.0)
- FlipperKit/FBCxxFollyDynamicConvert (= 0.54.0)
- FlipperKit/FBDefines (= 0.54.0)
- FlipperKit/FKPortForwarding (= 0.54.0)
- FlipperKit/FlipperKitHighlightOverlay (= 0.54.0)
- FlipperKit/FlipperKitLayoutPlugin (= 0.54.0)
- FlipperKit/FlipperKitLayoutTextSearchable (= 0.54.0)
- FlipperKit/FlipperKitNetworkPlugin (= 0.54.0)
- FlipperKit/FlipperKitReactPlugin (= 0.54.0)
- FlipperKit/FlipperKitUserDefaultsPlugin (= 0.54.0)
- FlipperKit/SKIOSNetworkPlugin (= 0.54.0)
- Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`) - Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`)
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
- RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`) - RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`)
- RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`) - RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`)
- React (from `../node_modules/react-native/`) - React (from `../node_modules/react-native/`)
- React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`)
- React-Core (from `../node_modules/react-native/`) - React-Core (from `../node_modules/react-native/`)
- React-Core/DevSupport (from `../node_modules/react-native/`) - React-Core/DevSupport (from `../node_modules/react-native/`)
- React-Core/RCTWebSocket (from `../node_modules/react-native/`) - React-Core/RCTWebSocket (from `../node_modules/react-native/`)
@@ -249,6 +352,7 @@ DEPENDENCIES:
- React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`) - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`) - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
- react-native-update (from `../../..`) - react-native-update (from `../../..`)
- react-native-webview (from `../node_modules/react-native-webview`)
- React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`) - React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
- React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`) - React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`)
- React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`) - React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`)
@@ -258,14 +362,24 @@ DEPENDENCIES:
- React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`) - React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`)
- React-RCTText (from `../node_modules/react-native/Libraries/Text`) - React-RCTText (from `../node_modules/react-native/Libraries/Text`)
- React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`) - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
- ReactCommon/jscallinvoker (from `../node_modules/react-native/ReactCommon`)
- ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`) - Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
SPEC REPOS: SPEC REPOS:
trunk: trunk:
- boost-for-react-native - boost-for-react-native
- CocoaAsyncSocket
- CocoaLibEvent
- Flipper
- Flipper-DoubleConversion
- Flipper-Folly
- Flipper-Glog
- Flipper-PeerTalk
- Flipper-RSocket
- FlipperKit
- OpenSSL-Universal
- SSZipArchive - SSZipArchive
- YogaKit
EXTERNAL SOURCES: EXTERNAL SOURCES:
DoubleConversion: DoubleConversion:
@@ -284,6 +398,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/Libraries/TypeSafety" :path: "../node_modules/react-native/Libraries/TypeSafety"
React: React:
:path: "../node_modules/react-native/" :path: "../node_modules/react-native/"
React-callinvoker:
:path: "../node_modules/react-native/ReactCommon/callinvoker"
React-Core: React-Core:
:path: "../node_modules/react-native/" :path: "../node_modules/react-native/"
React-CoreModules: React-CoreModules:
@@ -298,6 +414,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/jsinspector" :path: "../node_modules/react-native/ReactCommon/jsinspector"
react-native-update: react-native-update:
:path: "../../.." :path: "../../.."
react-native-webview:
:path: "../node_modules/react-native-webview"
React-RCTActionSheet: React-RCTActionSheet:
:path: "../node_modules/react-native/Libraries/ActionSheetIOS" :path: "../node_modules/react-native/Libraries/ActionSheetIOS"
React-RCTAnimation: React-RCTAnimation:
@@ -323,34 +441,47 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS: SPEC CHECKSUMS:
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2 CocoaAsyncSocket: 694058e7c0ed05a9e217d1b3c7ded962f4180845
FBLazyVector: 68b6a76960fbd8ecd9fb7ce0aadd3329c3340a99 CocoaLibEvent: 2fab71b8bd46dd33ddb959f7928ec5909f838e3f
FBReactNativeSpec: 5a764c60abdc3336a213e5310c40b74741f32839 DoubleConversion: cde416483dac037923206447da6e1454df403714
Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51 FBLazyVector: 878b59e31113e289e275165efbe4b54fa614d43d
glog: 1f3da668190260b06b429bb211bfbee5cd790c28 FBReactNativeSpec: 7da9338acfb98d4ef9e5536805a0704572d33c2f
RCTRequired: c639d59ed389cfb1f1203f65c2ea946d8ec586e2 Flipper: be611d4b742d8c87fbae2ca5f44603a02539e365
RCTTypeSafety: dc23fb655d6c77667c78e327bf661bc11e3b8aec Flipper-DoubleConversion: 38631e41ef4f9b12861c67d17cb5518d06badc41
React: 7e586e5d7bec12b91c1a096826b0fc9ab1da7865 Flipper-Folly: c12092ea368353b58e992843a990a3225d4533c3
React-Core: 8ddb9770b4a30a6ab4a754e6ed5ec76454e3d699 Flipper-Glog: 1dfd6abf1e922806c52ceb8701a3599a79a200a6
React-CoreModules: b3d9eece8ad7df36c917a41f05c1168c52fe0b34 Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9
React-cxxreact: 1f972757c0bd08d962ef78068e06613c27489a3f Flipper-RSocket: 64e7431a55835eb953b0bf984ef3b90ae9fdddd7
React-jsi: 32285a21b1b24c36060493ed3057a34677d58d09 FlipperKit: ab353d41aea8aae2ea6daaf813e67496642f3d7d
React-jsiexecutor: 8909917ff7d8f21a57e443a866fd8d4560e50c65 Folly: b73c3869541e86821df3c387eb0af5f65addfab4
React-jsinspector: 111d7d342b07a904c400592e02a2b958f1098b60 glog: 40a13f7840415b9a77023fbcae0f1e6f43192af3
react-native-update: 5efc26c6af746154a6b7f7c4c073b9ff3f418322 OpenSSL-Universal: 8b48cc0d10c1b2923617dfe5c178aa9ed2689355
React-RCTActionSheet: 89b037c0fb7d2671607cb645760164e7e0c013f6 RCTRequired: 48884c74035a0b5b76dbb7a998bd93bcfc5f2047
React-RCTAnimation: e3cefa93c38c004c318f7ec04b883eb14b8b8235 RCTTypeSafety: edf4b618033c2f1c5b7bc3d90d8e085ed95ba2ab
React-RCTBlob: d26ac0e313fbf14e7203473fd593ccaaeee8329e React: f36e90f3ceb976546e97df3403e37d226f79d0e3
React-RCTImage: 4bdd9588783fa9e48ef669ccd4f747224e208edf React-callinvoker: 18874f621eb96625df7a24a7dc8d6e07391affcd
React-RCTLinking: 65f0088ff463babd3d5d567964a65b74141eff3b React-Core: ac3d816b8e3493970153f4aaf0cff18af0bb95e6
React-RCTNetwork: 0c1a73576c1cfeafe68396556de1b17d93c0c595 React-CoreModules: 4016d3a4e518bcfc4f5a51252b5a05692ca6f0e1
React-RCTSettings: 4194f1f0edbddf3fd44d1714dc6578bb20379b60 React-cxxreact: ffc9129013b87cb36cf3f30a86695a3c397b0f99
React-RCTText: e3ef6191cdb627855ff7fe8fa0c1e14094967fb8 React-jsi: df07aa95b39c5be3e41199921509bfa929ed2b9d
React-RCTVibration: fb54c732fd20405a76598e431aa2f8c2bf527de9 React-jsiexecutor: b56c03e61c0dd5f5801255f2160a815f4a53d451
ReactCommon: 5848032ed2f274fcb40f6b9ec24067787c42d479 React-jsinspector: 8e68ffbfe23880d3ee9bafa8be2777f60b25cbe2
SSZipArchive: fa16b8cc4cdeceb698e5e5d9f67e9558532fbf23 react-native-update: 74ae0ad726a7cd5194b377361b7a3e921a65201c
Yoga: 14927e37bd25376d216b150ab2a561773d57911f react-native-webview: 4e96d493f9f90ba4f03b28933f30b2964df07e39
React-RCTActionSheet: 53ea72699698b0b47a6421cb1c8b4ab215a774aa
React-RCTAnimation: 1befece0b5183c22ae01b966f5583f42e69a83c2
React-RCTBlob: 0b284339cbe4b15705a05e2313a51c6d8b51fa40
React-RCTImage: d1756599ebd4dc2cb19d1682fe67c6b976658387
React-RCTLinking: 9af0a51c6d6a4dd1674daadafffc6d03033a6d18
React-RCTNetwork: 332c83929cc5eae0b3bbca4add1d668e1fc18bda
React-RCTSettings: d6953772cfd55f2c68ad72b7ef29efc7ec49f773
React-RCTText: 65a6de06a7389098ce24340d1d3556015c38f746
React-RCTVibration: 8e9fb25724a0805107fc1acc9075e26f814df454
ReactCommon: 4167844018c9ed375cc01a843e9ee564399e53c3
SSZipArchive: 62d4947b08730e4cda640473b0066d209ff033c9
Yoga: 7d13633d129fd179e01b8953d38d47be90db185a
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
PODFILE CHECKSUM: 244ba888c650d3945bf72a8d01516fb0f1b3b097 PODFILE CHECKSUM: 449e295f5242b426c8459fd24a8812cc985916d0
COCOAPODS: 1.8.1 COCOAPODS: 1.9.3

View File

@@ -0,0 +1,9 @@
//
// dummy.swift
// testHotUpdate
//
// Created by Qingming, Sunny Luo on 9/1/20.
// Copyright © 2020 Facebook. All rights reserved.
//
import Foundation

View File

@@ -0,0 +1,4 @@
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//

View File

@@ -12,6 +12,7 @@
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
7610A16EF1CB8A7644EAFA55 /* libPods-testHotUpdate.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 28C264AEBE1E206870F9D871 /* libPods-testHotUpdate.a */; }; 7610A16EF1CB8A7644EAFA55 /* libPods-testHotUpdate.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 28C264AEBE1E206870F9D871 /* libPods-testHotUpdate.a */; };
F1CBCFD724FE1CF80019170D /* dummy.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1CBCFD624FE1CF80019170D /* dummy.swift */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
@@ -37,6 +38,8 @@
ED1DB2CEADC1A82A43867C0E /* Pods-testHotUpdateTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-testHotUpdateTests.release.xcconfig"; path = "Target Support Files/Pods-testHotUpdateTests/Pods-testHotUpdateTests.release.xcconfig"; sourceTree = "<group>"; }; ED1DB2CEADC1A82A43867C0E /* Pods-testHotUpdateTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-testHotUpdateTests.release.xcconfig"; path = "Target Support Files/Pods-testHotUpdateTests/Pods-testHotUpdateTests.release.xcconfig"; sourceTree = "<group>"; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
ED2971642150620600B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; }; ED2971642150620600B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; };
F1CBCFD524FE1CF80019170D /* testHotUpdate-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "testHotUpdate-Bridging-Header.h"; sourceTree = "<group>"; };
F1CBCFD624FE1CF80019170D /* dummy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = dummy.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@@ -76,6 +79,8 @@
13B07FB61A68108700A75B9A /* Info.plist */, 13B07FB61A68108700A75B9A /* Info.plist */,
13B07FB11A68108700A75B9A /* LaunchScreen.xib */, 13B07FB11A68108700A75B9A /* LaunchScreen.xib */,
13B07FB71A68108700A75B9A /* main.m */, 13B07FB71A68108700A75B9A /* main.m */,
F1CBCFD624FE1CF80019170D /* dummy.swift */,
F1CBCFD524FE1CF80019170D /* testHotUpdate-Bridging-Header.h */,
); );
name = testHotUpdate; name = testHotUpdate;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -135,6 +140,7 @@
13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */, 13B07F8E1A680F5B00A75B9A /* Resources */,
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
1CD78CE8A2E7B88FAE48FCEE /* [CP] Copy Pods Resources */,
); );
buildRules = ( buildRules = (
); );
@@ -153,6 +159,12 @@
attributes = { attributes = {
LastUpgradeCheck = 0940; LastUpgradeCheck = 0940;
ORGANIZATIONNAME = Facebook; ORGANIZATIONNAME = Facebook;
TargetAttributes = {
13B07F861A680F5B00A75B9A = {
DevelopmentTeam = JD75Q9JJL2;
LastSwiftMigration = 1160;
};
};
}; };
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "testHotUpdate" */; buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "testHotUpdate" */;
compatibilityVersion = "Xcode 3.2"; compatibilityVersion = "Xcode 3.2";
@@ -198,7 +210,27 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh"; shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh\n";
};
1CD78CE8A2E7B88FAE48FCEE /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-testHotUpdate/Pods-testHotUpdate-resources.sh",
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle",
"${PODS_ROOT}/../../../../ios/pushy_build_time.txt",
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/pushy_build_time.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-testHotUpdate/Pods-testHotUpdate-resources.sh\"\n";
showEnvVarsInLog = 0;
}; };
C49C562FA1BDE1F80ECD9C13 /* [CP] Check Pods Manifest.lock */ = { C49C562FA1BDE1F80ECD9C13 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
@@ -249,6 +281,7 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */, 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */,
F1CBCFD724FE1CF80019170D /* dummy.swift in Sources */,
13B07FC11A68108700A75B9A /* main.m in Sources */, 13B07FC11A68108700A75B9A /* main.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@@ -273,8 +306,10 @@
baseConfigurationReference = 84EBA9C1A760F4136B306391 /* Pods-testHotUpdate.debug.xcconfig */; baseConfigurationReference = 84EBA9C1A760F4136B306391 /* Pods-testHotUpdate.debug.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEAD_CODE_STRIPPING = NO; DEAD_CODE_STRIPPING = NO;
DEVELOPMENT_TEAM = JD75Q9JJL2;
INFOPLIST_FILE = testHotUpdate/Info.plist; INFOPLIST_FILE = testHotUpdate/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
@@ -284,6 +319,9 @@
); );
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = testHotUpdate; PRODUCT_NAME = testHotUpdate;
SWIFT_OBJC_BRIDGING_HEADER = "testHotUpdate-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
}; };
name = Debug; name = Debug;
@@ -293,7 +331,9 @@
baseConfigurationReference = 1A3E77317B15A5C3816ACE3A /* Pods-testHotUpdate.release.xcconfig */; baseConfigurationReference = 1A3E77317B15A5C3816ACE3A /* Pods-testHotUpdate.release.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = JD75Q9JJL2;
INFOPLIST_FILE = testHotUpdate/Info.plist; INFOPLIST_FILE = testHotUpdate/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
@@ -303,6 +343,8 @@
); );
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = testHotUpdate; PRODUCT_NAME = testHotUpdate;
SWIFT_OBJC_BRIDGING_HEADER = "testHotUpdate-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
}; };
name = Release; name = Release;

View File

@@ -55,6 +55,15 @@
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"> shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "testHotUpdate.app"
BlueprintName = "testHotUpdate"
ReferencedContainer = "container:testHotUpdate.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables> <Testables>
<TestableReference <TestableReference
skipped = "NO"> skipped = "NO">
@@ -67,17 +76,6 @@
</BuildableReference> </BuildableReference>
</TestableReference> </TestableReference>
</Testables> </Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "testHotUpdate.app"
BlueprintName = "testHotUpdate"
ReferencedContainer = "container:testHotUpdate.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction> </TestAction>
<LaunchAction <LaunchAction
buildConfiguration = "Debug" buildConfiguration = "Debug"
@@ -99,8 +97,6 @@
ReferencedContainer = "container:testHotUpdate.xcodeproj"> ReferencedContainer = "container:testHotUpdate.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildableProductRunnable> </BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction> </LaunchAction>
<ProfileAction <ProfileAction
buildConfiguration = "Release" buildConfiguration = "Release"

View File

@@ -10,12 +10,34 @@
#import <React/RCTBridge.h> #import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h> #import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h> #import <React/RCTRootView.h>
#import "RCTHotUpdate.h" #import "RCTPushy.h"
#ifdef FB_SONARKIT_ENABLED
#import <FlipperKit/FlipperClient.h>
#import <FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h>
#import <FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h>
#import <FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h>
#import <SKIOSNetworkPlugin/SKIOSNetworkAdapter.h>
#import <FlipperKitReactPlugin/FlipperKitReactPlugin.h>
static void InitializeFlipper(UIApplication *application) {
FlipperClient *client = [FlipperClient sharedClient];
SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults];
[client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]];
[client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]];
[client addPlugin:[FlipperKitReactPlugin new]];
[client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]];
[client start];
}
#endif
@implementation AppDelegate @implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{ {
#ifdef FB_SONARKIT_ENABLED
InitializeFlipper(application);
#endif
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
moduleName:@"testHotUpdate" moduleName:@"testHotUpdate"
@@ -37,7 +59,7 @@
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
#else #else
// DEBUGbundle // DEBUGbundle
return [RCTHotUpdate bundleURL]; return [RCTPushy bundleURL];
#endif #endif
} }

View File

@@ -7,22 +7,24 @@
"ios": "react-native run-ios", "ios": "react-native run-ios",
"start": "react-native start", "start": "react-native start",
"test": "jest", "test": "jest",
"lint": "eslint ." "lint": "eslint .",
"apk": "(cd android && ./gradlew aR)"
}, },
"dependencies": { "dependencies": {
"react": "16.9.0", "react": "16.13.1",
"react-native": "0.61.2", "react-native": "0.63.3",
"react-native-update": "link:../.." "react-native-update": "link:../..",
"react-native-webview": "^10.9.2"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.6.2", "@babel/core": "^7.8.4",
"@babel/runtime": "^7.6.2", "@babel/runtime": "^7.8.4",
"@react-native-community/eslint-config": "^0.0.5", "@react-native-community/eslint-config": "^1.1.0",
"babel-jest": "^24.9.0", "babel-jest": "^25.1.0",
"eslint": "^6.5.1", "eslint": "^6.5.1",
"jest": "^24.9.0", "jest": "^25.1.0",
"metro-react-native-babel-preset": "^0.56.0", "metro-react-native-babel-preset": "^0.59.0",
"react-test-renderer": "16.9.0" "react-test-renderer": "16.13.1"
}, },
"jest": { "jest": {
"preset": "react-native" "preset": "react-native"

View File

@@ -20,12 +20,17 @@ import {
switchVersion, switchVersion,
switchVersionLater, switchVersionLater,
markSuccess, markSuccess,
downloadAndInstallApk,
} from 'react-native-update'; } from 'react-native-update';
import _updateConfig from '../update.json'; import _updateConfig from '../update.json';
const {appKey} = _updateConfig[Platform.OS]; const {appKey} = _updateConfig[Platform.OS];
export default class App extends Component { export default class App extends Component {
state = {
received: 0,
total: 0,
};
componentDidMount() { componentDidMount() {
if (isRolledBack) { if (isRolledBack) {
Alert.alert('提示', '刚刚更新失败了,版本被回滚.'); Alert.alert('提示', '刚刚更新失败了,版本被回滚.');
@@ -50,23 +55,34 @@ export default class App extends Component {
); );
} }
} }
doUpdate = async info => { doUpdate = async (info) => {
const hash = await downloadUpdate(info); try {
Alert.alert('提示', '下载完毕,是否重启应用?', [ const hash = await downloadUpdate(info, {
{ onDownloadProgress: ({received, total}) => {
text: '是', this.setState({
onPress: () => { received,
switchVersion(hash); total,
});
}, },
}, });
{text: '否'}, Alert.alert('提示', '下载完毕,是否重启应用?', [
{ {
text: '下次启动时', text: '',
onPress: () => { onPress: () => {
switchVersionLater(hash); switchVersion(hash);
},
}, },
}, {text: '否'},
]); {
text: '下次启动时',
onPress: () => {
switchVersionLater(hash);
},
},
]);
} catch (err) {
Alert.alert('更新失败', err.message);
}
}; };
checkUpdate = async () => { checkUpdate = async () => {
@@ -74,15 +90,33 @@ export default class App extends Component {
try { try {
info = await checkUpdate(appKey); info = await checkUpdate(appKey);
} catch (err) { } catch (err) {
console.warn(err); Alert.alert('更新检查失败', err.message);
return; return;
} }
if (info.expired) { if (info.expired) {
Alert.alert('提示', '您的应用版本已更新,请前往应用商店下载新的版本', [ Alert.alert('提示', '您的应用版本已更新,点击确定下载安装新版本', [
{ {
text: '确定', text: '确定',
onPress: () => { onPress: () => {
info.downloadUrl && Linking.openURL(info.downloadUrl); if (info.downloadUrl) {
// apk可直接下载安装
if (
Platform.OS === 'android' &&
info.downloadUrl.endsWith('.apk')
) {
downloadAndInstallApk({
url: info.downloadUrl,
onDownloadProgress: ({received, total}) => {
this.setState({
received,
total,
});
},
});
} else {
Linking.openURL(info.downloadUrl);
}
}
}, },
}, },
]); ]);
@@ -106,24 +140,29 @@ export default class App extends Component {
}; };
render() { render() {
const { received, total } = this.state;
return ( return (
<View style={styles.container}> <View style={styles.container}>
<Text style={styles.welcome}>欢迎使用热更新服务</Text> <Text style={styles.welcome}>欢迎使用热更新服务</Text>
<Image <Image
resizeMode={'contain'} resizeMode={'contain'}
source={require('./assets/shoucang.png')} source={require('./assets/shezhi.png')}
style={styles.image} style={styles.image}
/> />
<Text style={styles.instructions}> <Text style={styles.instructions}>
这是版本一 {'\n'} 这是版本一 {'\n'}
当前包版本号: {packageVersion} 当前原生包版本号: {packageVersion}
{'\n'} {'\n'}
当前版本Hash: {currentVersion || '(空)'} 当前热更新版本Hash: {currentVersion || '(空)'}
{'\n'} {'\n'}
</Text> </Text>
<Text>
下载进度{received} / {total}
</Text>
<TouchableOpacity onPress={this.checkUpdate}> <TouchableOpacity onPress={this.checkUpdate}>
<Text style={styles.instructions}>点击这里检查更新</Text> <Text style={styles.instructions}>点击这里检查更新</Text>
</TouchableOpacity> </TouchableOpacity>
{/* <WebView style={{flex:1}} source={{uri: require('../www/index.html')}}/> */}
</View> </View>
); );
} }

22
Example/testHotUpdate/src/www/echarts.min.js vendored Executable file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<!-- 引入 ECharts 文件 -->
<script src="echarts.min.js"></script>
</head>
<body>
<!-- 为 ECharts 准备一个具备大小(宽高)的 DOM -->
<div id="main" style="width: 600px;height:400px;"></div>
<script type="text/javascript">
// 基于准备好的dom初始化echarts实例
var myChart = echarts.init(document.getElementById('main'));
// 指定图表的配置项和数据
var option = {
title: {
text: 'ECharts 入门示例'
},
tooltip: {},
legend: {
data:['销量']
},
xAxis: {
data: ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"]
},
yAxis: {},
series: [{
name: '销量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20]
}]
};
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);
</script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@@ -1,31 +1,10 @@
# react-native-update [![npm version](https://badge.fury.io/js/react-native-update.svg)](http://badge.fury.io/js/react-native-update) # react-native-update [![npm version](https://badge.fury.io/js/react-native-update.svg)](http://badge.fury.io/js/react-native-update)
本组件是面向 React Native 提供热更新功能的组件,建议结合[Update 服务](https://update.reactnative.cn/)使用 本组件是面向 React Native 提供热更新功能的组件,详情请访问我们的官方网站 <https://pushy.reactnative.cn>
注意在iOS上使用热更新有被拒的可能。可以按以下步骤屏蔽iOS端 ### 快速开始
1. 如果RN版本>=0.60,在项目根目录下编辑或创建react-native.config.js添加如下内容 请查看[文档](https://pushy.reactnative.cn/docs/getting-started.html)
```js
// react-native.config.js
module.exports = {
dependencies: {
'react-native-update': {
platforms: {
ios: null, // 阻止ios模块自动链接
},
},
},
};
```
2. 如果在原生代码端尚未配置则跳过下面文档中的ios端的配置。如果已经配置则按文档的步骤反向操作添加的ios代码删去
3. 如果是0.60以上版本或使用了cocoapods在ios目录中再次运行pod install确保Podfile和Podfile.lock中都没有'react-native-update'。如果RN版本<0.60则运行`react-native unlink react-native-update`
4. 在js代码里调用checkUpdate()方法前判断Platform.OS如果是ios平台则直接return跳过
```
### 最近更新
请查阅[最近更新文档](CHANGELOG.md)。
### 优势 ### 优势
@@ -35,26 +14,6 @@ module.exports = {
4. meta 信息及开放 API提供更高扩展性。 4. meta 信息及开放 API提供更高扩展性。
5. 跨越多个版本进行更新时,只需要下载**一个更新包**,不需要逐版本依次更新。 5. 跨越多个版本进行更新时,只需要下载**一个更新包**,不需要逐版本依次更新。
### 安装与快速入门
请查阅
- [文档-快速入门-准备工作](docs/guide.md)。
- [文档-快速入门-添加热更新功能](docs/guide2.md)。
- [文档-快速入门-发布版本](docs/guide3.md)。
- [文档-常见问题与高级指南](docs/faq_advance.md)。
### 命令行工具
请查阅[文档-命令行工具](docs/cli.md)。
### API 接口
请查阅[文档-API 接口](docs/api.md)。
### 本地开发 ### 本地开发
``` ```
@@ -64,11 +23,10 @@ $ yarn
$ yarn start $ yarn start
``` ```
本地库文件使用yarn link链接因此可直接在源文件中修改testHotUpdate项目中调试 本地库文件使用 yarn link 链接因此可直接在源文件中修改,在 testHotUpdate 项目中调试
### 关于我们 ### 关于我们
本组件由[React Native 中文网](https://reactnative.cn/)独家发布,如有定制需求可以[联系我们](https://reactnative.cn/about.html#content)。 本组件由[React Native 中文网](https://reactnative.cn/)独家发布,如有定制需求可以[联系我们](https://reactnative.cn/about.html#content)。
关于此插件发现任何问题可以前往[Issues](https://github.com/reactnativecn/react-native-pushy/issues)或者[中文社区](http://bbs.reactnative.cn/category/7)发帖提问 关于此插件发现任何问题可以前往[Issues](https://github.com/reactnativecn/react-native-pushy/issues)发帖提问。

View File

@@ -27,11 +27,9 @@ android {
buildTypes { buildTypes {
release { release {
buildConfigField("String", "PUSHY_BUILD_TIME", "\"${minutesSinceEpoch}\"")
resValue("string", "pushy_build_time", "${minutesSinceEpoch}") resValue("string", "pushy_build_time", "${minutesSinceEpoch}")
} }
debug { debug {
buildConfigField("String", "PUSHY_BUILD_TIME", "\"0\"")
resValue("string", "pushy_build_time", "0") resValue("string", "pushy_build_time", "0")
} }
} }

View File

@@ -1,5 +1,17 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cn.reactnative.modules.update"> package="cn.reactnative.modules.update">
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<application>
<meta-data android:name="pushy_build_time" android:value="@string/pushy_build_time" />
<provider
android:name=".PushyFileProvider"
android:authorities="${applicationId}.pushy.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/pushy_file_paths" />
</provider>
</application>
</manifest> </manifest>

View File

@@ -3,6 +3,8 @@ package cn.reactnative.modules.update;
import android.content.Context; import android.content.Context;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.util.Log; import android.util.Log;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableMap;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import okhttp3.Request; import okhttp3.Request;
@@ -31,14 +33,15 @@ import java.util.HashMap;
import okio.BufferedSink; import okio.BufferedSink;
import okio.BufferedSource; import okio.BufferedSource;
import okio.Okio; import okio.Okio;
import static cn.reactnative.modules.update.UpdateModule.sendEvent;
/** /**
* Created by tdzl2003 on 3/31/16. * Created by tdzl2003 on 3/31/16.
*/ */
class DownloadTask extends AsyncTask<DownloadTaskParams, Void, Void> { class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
final int DOWNLOAD_CHUNK_SIZE = 4096; final int DOWNLOAD_CHUNK_SIZE = 4096;
Context context; Context context;
String hash;
DownloadTask(Context context) { DownloadTask(Context context) {
this.context = context; this.context = context;
@@ -67,7 +70,10 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, Void, Void> {
} }
} }
private void downloadFile(String url, File writePath) throws IOException { private void downloadFile(DownloadTaskParams param) throws IOException {
String url = param.url;
File writePath = param.targetFile;
this.hash = param.hash;
OkHttpClient client = new OkHttpClient(); OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url) Request request = new Request.Builder().url(url)
.build(); .build();
@@ -90,16 +96,25 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, Void, Void> {
} }
long bytesRead = 0; long bytesRead = 0;
long totalRead = 0; long received = 0;
int currentPercentage = 0;
while ((bytesRead = source.read(sink.buffer(), DOWNLOAD_CHUNK_SIZE)) != -1) { while ((bytesRead = source.read(sink.buffer(), DOWNLOAD_CHUNK_SIZE)) != -1) {
totalRead += bytesRead; received += bytesRead;
sink.emit();
if (UpdateContext.DEBUG) { if (UpdateContext.DEBUG) {
Log.d("RNUpdate", "Progress " + totalRead + "/" + contentLength); Log.d("RNUpdate", "Progress " + received + "/" + contentLength);
}
int percentage = (int)(received * 100.0 / contentLength + 0.5);
if (percentage > currentPercentage) {
currentPercentage = percentage;
publishProgress(new long[]{received, contentLength});
} }
} }
if (totalRead != contentLength) { if (received != contentLength) {
throw new Error("Unexpected eof while reading ppk"); throw new Error("Unexpected eof while reading downloaded update");
} }
publishProgress(new long[]{received, contentLength});
sink.writeAll(source); sink.writeAll(source);
sink.close(); sink.close();
@@ -108,6 +123,17 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, Void, Void> {
} }
} }
@Override
protected void onProgressUpdate(long[]... values) {
super.onProgressUpdate(values);
WritableMap params = Arguments.createMap();
params.putDouble("received", (values[0][0]));
params.putDouble("total", (values[0][1]));
params.putString("hash", this.hash);
sendEvent("RCTPushyDownloadProgress", params);
}
byte[] buffer = new byte[1024]; byte[] buffer = new byte[1024];
private static native byte[] bsdiffPatch(byte[] origin, byte[] patch); private static native byte[] bsdiffPatch(byte[] origin, byte[] patch);
@@ -217,10 +243,10 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, Void, Void> {
copyFilesWithBlacklist("", from, to, blackList); copyFilesWithBlacklist("", from, to, blackList);
} }
private void doDownload(DownloadTaskParams param) throws IOException { private void doFullPatch(DownloadTaskParams param) throws IOException {
downloadFile(param.url, param.zipFilePath); downloadFile(param);
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(param.zipFilePath))); ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(param.targetFile)));
ZipEntry ze; ZipEntry ze;
String filename; String filename;
@@ -251,12 +277,12 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, Void, Void> {
} }
} }
private void copyFromResource(HashMap<String, ArrayList<File> > map) throws IOException { private void copyFromResource(HashMap<String, ArrayList<File> > resToCopy) throws IOException {
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(context.getPackageResourcePath()))); ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(context.getPackageResourcePath())));
ZipEntry ze; ZipEntry ze;
while ((ze = zis.getNextEntry()) != null) { while ((ze = zis.getNextEntry()) != null) {
String fn = ze.getName(); String fn = ze.getName();
ArrayList<File> targets = map.get(fn); ArrayList<File> targets = resToCopy.get(fn);
if (targets != null) { if (targets != null) {
File lastTarget = null; File lastTarget = null;
for (File target: targets) { for (File target: targets) {
@@ -275,12 +301,14 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, Void, Void> {
} }
private void doPatchFromApk(DownloadTaskParams param) throws IOException, JSONException { private void doPatchFromApk(DownloadTaskParams param) throws IOException, JSONException {
downloadFile(param.url, param.zipFilePath); downloadFile(param);
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(param.zipFilePath))); ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(param.targetFile)));
ZipEntry ze; ZipEntry ze;
int count; int count;
String filename; String filename;
boolean foundDiff = false;
boolean foundBundlePatch = false;
removeDirectory(param.unzipDirectory); removeDirectory(param.unzipDirectory);
param.unzipDirectory.mkdirs(); param.unzipDirectory.mkdirs();
@@ -292,6 +320,7 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, Void, Void> {
String fn = ze.getName(); String fn = ze.getName();
if (fn.equals("__diff.json")) { if (fn.equals("__diff.json")) {
foundDiff = true;
// copy files from assets // copy files from assets
byte[] bytes = readBytes(zis); byte[] bytes = readBytes(zis);
String json = new String(bytes, "UTF-8"); String json = new String(bytes, "UTF-8");
@@ -318,6 +347,7 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, Void, Void> {
continue; continue;
} }
if (fn.equals("index.bundlejs.patch")) { if (fn.equals("index.bundlejs.patch")) {
foundBundlePatch = true;
// do bsdiff patch // do bsdiff patch
byte[] patched = bsdiffPatch(readOriginBundle(), readBytes(zis)); byte[] patched = bsdiffPatch(readOriginBundle(), readBytes(zis));
@@ -341,6 +371,12 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, Void, Void> {
} }
zis.close(); zis.close();
if (!foundDiff) {
throw new Error("diff.json not found");
}
if (!foundBundlePatch) {
throw new Error("bundle patch not found");
}
copyFromResource(copyList); copyFromResource(copyList);
@@ -351,12 +387,14 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, Void, Void> {
} }
private void doPatchFromPpk(DownloadTaskParams param) throws IOException, JSONException { private void doPatchFromPpk(DownloadTaskParams param) throws IOException, JSONException {
downloadFile(param.url, param.zipFilePath); downloadFile(param);
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(param.zipFilePath))); ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(param.targetFile)));
ZipEntry ze; ZipEntry ze;
int count; int count;
String filename; String filename;
boolean foundDiff = false;
boolean foundBundlePatch = false;
removeDirectory(param.unzipDirectory); removeDirectory(param.unzipDirectory);
param.unzipDirectory.mkdirs(); param.unzipDirectory.mkdirs();
@@ -366,6 +404,7 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, Void, Void> {
String fn = ze.getName(); String fn = ze.getName();
if (fn.equals("__diff.json")) { if (fn.equals("__diff.json")) {
foundDiff = true;
// copy files from assets // copy files from assets
byte[] bytes = readBytes(zis); byte[] bytes = readBytes(zis);
String json = new String(bytes, "UTF-8"); String json = new String(bytes, "UTF-8");
@@ -386,6 +425,7 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, Void, Void> {
continue; continue;
} }
if (fn.equals("index.bundlejs.patch")) { if (fn.equals("index.bundlejs.patch")) {
foundBundlePatch = true;
// do bsdiff patch // do bsdiff patch
byte[] patched = bsdiffPatch(readFile(new File(param.originDirectory, "index.bundlejs")), readBytes(zis)); byte[] patched = bsdiffPatch(readFile(new File(param.originDirectory, "index.bundlejs")), readBytes(zis));
@@ -410,6 +450,12 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, Void, Void> {
zis.close(); zis.close();
if (!foundDiff) {
throw new Error("diff.json not found");
}
if (!foundBundlePatch) {
throw new Error("bundle patch not found");
}
if (UpdateContext.DEBUG) { if (UpdateContext.DEBUG) {
Log.d("RNUpdate", "Unzip finished"); Log.d("RNUpdate", "Unzip finished");
} }
@@ -436,10 +482,11 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, Void, Void> {
@Override @Override
protected Void doInBackground(DownloadTaskParams... params) { protected Void doInBackground(DownloadTaskParams... params) {
int taskType = params[0].type;
try { try {
switch (params[0].type) { switch (taskType) {
case DownloadTaskParams.TASK_TYPE_FULL_DOWNLOAD: case DownloadTaskParams.TASK_TYPE_PATCH_FULL:
doDownload(params[0]); doFullPatch(params[0]);
break; break;
case DownloadTaskParams.TASK_TYPE_PATCH_FROM_APK: case DownloadTaskParams.TASK_TYPE_PATCH_FROM_APK:
doPatchFromApk(params[0]); doPatchFromApk(params[0]);
@@ -447,15 +494,41 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, Void, Void> {
case DownloadTaskParams.TASK_TYPE_PATCH_FROM_PPK: case DownloadTaskParams.TASK_TYPE_PATCH_FROM_PPK:
doPatchFromPpk(params[0]); doPatchFromPpk(params[0]);
break; break;
case DownloadTaskParams.TASK_TYPE_CLEARUP: case DownloadTaskParams.TASK_TYPE_CLEANUP:
doCleanUp(params[0]); doCleanUp(params[0]);
break; break;
case DownloadTaskParams.TASK_TYPE_PLAIN_DOWNLOAD:
downloadFile(params[0]);
break;
default:
break;
}
if (params[0].listener != null) {
params[0].listener.onDownloadCompleted(params[0]);
} }
params[0].listener.onDownloadCompleted();
} catch (Throwable e) { } catch (Throwable e) {
if (UpdateContext.DEBUG) { if (UpdateContext.DEBUG) {
e.printStackTrace(); e.printStackTrace();
} }
switch (taskType) {
case DownloadTaskParams.TASK_TYPE_PATCH_FULL:
case DownloadTaskParams.TASK_TYPE_PATCH_FROM_APK:
case DownloadTaskParams.TASK_TYPE_PATCH_FROM_PPK:
try {
removeDirectory(params[0].unzipDirectory);
} catch (IOException ioException) {
ioException.printStackTrace();
}
break;
case DownloadTaskParams.TASK_TYPE_PLAIN_DOWNLOAD:
// if (targetToClean.exists()) {
params[0].targetFile.delete();
// }
break;
default:
break;
}
Log.e("pushy", "download task failed", e);
params[0].listener.onDownloadFailed(e); params[0].listener.onDownloadFailed(e);
} }
return null; return null;

View File

@@ -8,17 +8,18 @@ import java.io.File;
* Created by tdzl2003 on 3/31/16. * Created by tdzl2003 on 3/31/16.
*/ */
class DownloadTaskParams { class DownloadTaskParams {
static final int TASK_TYPE_FULL_DOWNLOAD = 1; static final int TASK_TYPE_PATCH_FULL = 1;
static final int TASK_TYPE_PATCH_FROM_APK = 2; static final int TASK_TYPE_PATCH_FROM_APK = 2;
static final int TASK_TYPE_PATCH_FROM_PPK = 3; static final int TASK_TYPE_PATCH_FROM_PPK = 3;
static final int TASK_TYPE_PLAIN_DOWNLOAD = 4;
static final int TASK_TYPE_CLEARUP = 0; //Keep hash & originHash static final int TASK_TYPE_CLEANUP = 0; //Keep hash & originHash
int type; int type;
String url; String url;
String hash; String hash;
String originHash; String originHash;
File zipFilePath; File targetFile;
File unzipDirectory; File unzipDirectory;
File originDirectory; File originDirectory;
UpdateContext.DownloadFileListener listener; UpdateContext.DownloadFileListener listener;

View File

@@ -0,0 +1,14 @@
package cn.reactnative.modules.update;
import android.support.v4.content.FileProvider;
/**
* Providing a custom {@code FileProvider} prevents manifest {@code <provider>} name collisions.
*
* See https://developer.android.com/guide/topics/manifest/provider-element.html for details.
*/
public class PushyFileProvider extends FileProvider {
// This class intentionally left blank.
}

View File

@@ -4,6 +4,11 @@ import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.util.Log;
import com.facebook.react.ReactInstanceManager;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@@ -19,6 +24,8 @@ public class UpdateContext {
private Executor executor; private Executor executor;
public static boolean DEBUG = false; public static boolean DEBUG = false;
private static ReactInstanceManager mReactInstanceManager;
private static boolean isUsingBundleUrl = false;
public UpdateContext(Context context) { public UpdateContext(Context context) {
this.context = context; this.context = context;
@@ -39,7 +46,7 @@ public class UpdateContext {
editor.putString("packageVersion", packageVersion); editor.putString("packageVersion", packageVersion);
editor.apply(); editor.apply();
this.clearUp(); this.cleanUp();
} }
} }
@@ -60,58 +67,84 @@ public class UpdateContext {
} }
public String getBuildTime() { public String getBuildTime() {
return BuildConfig.PUSHY_BUILD_TIME; return context.getString(R.string.pushy_build_time);
}
public String getUuid() {
return sp.getString("uuid", null);
}
public Map getBlockUpdate() {
return new HashMap<String, Object>() {{
put("until", sp.getInt("blockUntil", 0));
put("reason", sp.getString("blockReason", null));
}};
}
public boolean getIsUsingBundleUrl() {
return isUsingBundleUrl;
} }
public interface DownloadFileListener { public interface DownloadFileListener {
void onDownloadCompleted(); void onDownloadCompleted(DownloadTaskParams params);
void onDownloadFailed(Throwable error); void onDownloadFailed(Throwable error);
} }
public void downloadFile(String url, String hashName, DownloadFileListener listener) { public void downloadFullUpdate(String url, String hash, DownloadFileListener listener) {
DownloadTaskParams params = new DownloadTaskParams(); DownloadTaskParams params = new DownloadTaskParams();
params.type = DownloadTaskParams.TASK_TYPE_FULL_DOWNLOAD; params.type = DownloadTaskParams.TASK_TYPE_PATCH_FULL;
params.url = url; params.url = url;
params.hash = hashName; params.hash = hash;
params.listener = listener; params.listener = listener;
params.zipFilePath = new File(rootDir, hashName + ".ppk"); params.targetFile = new File(rootDir, hash + ".ppk");
params.unzipDirectory = new File(rootDir, hashName); params.unzipDirectory = new File(rootDir, hash);
new DownloadTask(context).executeOnExecutor(this.executor, params); new DownloadTask(context).executeOnExecutor(this.executor, params);
} }
public void downloadPatchFromApk(String url, String hashName, DownloadFileListener listener) { public void downloadFile(String url, String hash, String fileName, DownloadFileListener listener) {
DownloadTaskParams params = new DownloadTaskParams();
params.type = DownloadTaskParams.TASK_TYPE_PLAIN_DOWNLOAD;
params.url = url;
params.hash = hash;
params.listener = listener;
params.targetFile = new File(rootDir, fileName);
// params.unzipDirectory = new File(rootDir, hash);
new DownloadTask(context).executeOnExecutor(this.executor, params);
}
public void downloadPatchFromApk(String url, String hash, DownloadFileListener listener) {
DownloadTaskParams params = new DownloadTaskParams(); DownloadTaskParams params = new DownloadTaskParams();
params.type = DownloadTaskParams.TASK_TYPE_PATCH_FROM_APK; params.type = DownloadTaskParams.TASK_TYPE_PATCH_FROM_APK;
params.url = url; params.url = url;
params.hash = hashName; params.hash = hash;
params.listener = listener; params.listener = listener;
params.zipFilePath = new File(rootDir, hashName + ".apk.patch"); params.targetFile = new File(rootDir, hash + ".apk.patch");
params.unzipDirectory = new File(rootDir, hashName); params.unzipDirectory = new File(rootDir, hash);
new DownloadTask(context).executeOnExecutor(this.executor, params); new DownloadTask(context).executeOnExecutor(this.executor, params);
} }
public void downloadPatchFromPpk(String url, String hashName, String originHashName, DownloadFileListener listener) { public void downloadPatchFromPpk(String url, String hash, String originHash, DownloadFileListener listener) {
DownloadTaskParams params = new DownloadTaskParams(); DownloadTaskParams params = new DownloadTaskParams();
params.type = DownloadTaskParams.TASK_TYPE_PATCH_FROM_PPK; params.type = DownloadTaskParams.TASK_TYPE_PATCH_FROM_PPK;
params.url = url; params.url = url;
params.hash = hashName; params.hash = hash;
params.originHash = originHashName; params.originHash = originHash;
params.listener = listener; params.listener = listener;
params.zipFilePath = new File(rootDir, originHashName + "-" + hashName + ".ppk.patch"); params.targetFile = new File(rootDir, originHash + "-" + hash + ".ppk.patch");
params.unzipDirectory = new File(rootDir, hashName); params.unzipDirectory = new File(rootDir, hash);
params.originDirectory = new File(rootDir, originHashName); params.originDirectory = new File(rootDir, originHash);
new DownloadTask(context).executeOnExecutor(this.executor, params); new DownloadTask(context).executeOnExecutor(this.executor, params);
} }
private SharedPreferences sp; private SharedPreferences sp;
public void switchVersion(String hashName) { public void switchVersion(String hash) {
if (!new File(rootDir, hashName).exists()) { if (!new File(rootDir, hash+"/index.bundlejs").exists()) {
throw new Error("Hash name not found, must download first."); throw new Error("Bundle version " + hash + " not found.");
} }
String lastVersion = getCurrentVersion(); String lastVersion = getCurrentVersion();
SharedPreferences.Editor editor = sp.edit(); SharedPreferences.Editor editor = sp.edit();
editor.putString("currentVersion", hashName); editor.putString("currentVersion", hash);
if (lastVersion != null) { if (lastVersion != null) {
editor.putString("lastVersion", lastVersion); editor.putString("lastVersion", lastVersion);
} }
@@ -121,6 +154,19 @@ public class UpdateContext {
editor.apply(); editor.apply();
} }
public void setUuid(String uuid) {
SharedPreferences.Editor editor = sp.edit();
editor.putString("uuid", uuid);
editor.apply();
}
public void setBlockUpdate(int until, String reason) {
SharedPreferences.Editor editor = sp.edit();
editor.putInt("blockUntil", until);
editor.putString("blockReason", reason);
editor.apply();
}
public String getCurrentVersion() { public String getCurrentVersion() {
return sp.getString("currentVersion", null); return sp.getString("currentVersion", null);
} }
@@ -139,7 +185,7 @@ public class UpdateContext {
editor.remove("lastVersion"); editor.remove("lastVersion");
editor.apply(); editor.apply();
this.clearUp(); this.cleanUp();
} }
public void clearFirstTime() { public void clearFirstTime() {
@@ -147,7 +193,7 @@ public class UpdateContext {
editor.putBoolean("firstTime", false); editor.putBoolean("firstTime", false);
editor.apply(); editor.apply();
this.clearUp(); this.cleanUp();
} }
public void clearRollbackMark() { public void clearRollbackMark() {
@@ -155,7 +201,16 @@ public class UpdateContext {
editor.putBoolean("rolledBack", false); editor.putBoolean("rolledBack", false);
editor.apply(); editor.apply();
this.clearUp(); this.cleanUp();
}
public static void setCustomInstanceManager(ReactInstanceManager instanceManager) {
mReactInstanceManager = instanceManager;
}
public ReactInstanceManager getCustomReactInstanceManager() {
return mReactInstanceManager;
} }
public static String getBundleUrl(Context context) { public static String getBundleUrl(Context context) {
@@ -171,6 +226,7 @@ public class UpdateContext {
} }
public String getBundleUrl(String defaultAssetsUrl) { public String getBundleUrl(String defaultAssetsUrl) {
isUsingBundleUrl = true;
String currentVersion = getCurrentVersion(); String currentVersion = getCurrentVersion();
if (currentVersion == null) { if (currentVersion == null) {
return defaultAssetsUrl; return defaultAssetsUrl;
@@ -182,10 +238,18 @@ public class UpdateContext {
currentVersion = this.rollBack(); currentVersion = this.rollBack();
} }
} }
if (currentVersion == null) {
return defaultAssetsUrl; while (currentVersion != null) {
File bundleFile = new File(rootDir, currentVersion+"/index.bundlejs");
if (!bundleFile.exists()) {
Log.e("getBundleUrl", "Bundle version " + currentVersion + " not found.");
currentVersion = this.rollBack();
continue;
}
return bundleFile.toString();
} }
return (new File(rootDir, currentVersion+"/index.bundlejs").toString());
return defaultAssetsUrl;
} }
private String rollBack() { private String rollBack() {
@@ -203,21 +267,12 @@ public class UpdateContext {
return lastVersion; return lastVersion;
} }
private void clearUp() { private void cleanUp() {
DownloadTaskParams params = new DownloadTaskParams(); DownloadTaskParams params = new DownloadTaskParams();
params.type = DownloadTaskParams.TASK_TYPE_CLEARUP; params.type = DownloadTaskParams.TASK_TYPE_CLEANUP;
params.hash = sp.getString("currentVersion", null); params.hash = sp.getString("currentVersion", null);
params.originHash = sp.getString("lastVersion", null); params.originHash = sp.getString("lastVersion", null);
params.unzipDirectory = rootDir; params.unzipDirectory = rootDir;
params.listener = new DownloadFileListener() {
@Override
public void onDownloadCompleted() {
}
@Override
public void onDownloadFailed(Throwable error) {
}
};
new DownloadTask(context).executeOnExecutor(this.executor, params); new DownloadTask(context).executeOnExecutor(this.executor, params);
} }
} }

View File

@@ -3,32 +3,42 @@ package cn.reactnative.modules.update;
import android.app.Activity; import android.app.Activity;
import android.app.Application; import android.app.Application;
import android.content.Intent; import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.util.Log; import android.util.Log;
import com.facebook.react.ReactApplication; import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager; import com.facebook.react.ReactInstanceManager;
import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.bridge.JSBundleLoader; import com.facebook.react.bridge.JSBundleLoader;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import java.io.File;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import static android.support.v4.content.FileProvider.getUriForFile;
/** /**
* Created by tdzl2003 on 3/31/16. * Created by tdzl2003 on 3/31/16.
*/ */
public class UpdateModule extends ReactContextBaseJavaModule{ public class UpdateModule extends ReactContextBaseJavaModule {
UpdateContext updateContext; UpdateContext updateContext;
public static ReactApplicationContext mContext;
public UpdateModule(ReactApplicationContext reactContext, UpdateContext updateContext) { public UpdateModule(ReactApplicationContext reactContext, UpdateContext updateContext) {
super(reactContext); super(reactContext);
this.updateContext = updateContext; this.updateContext = updateContext;
mContext = reactContext;
} }
public UpdateModule(ReactApplicationContext reactContext) { public UpdateModule(ReactApplicationContext reactContext) {
@@ -42,6 +52,7 @@ public class UpdateModule extends ReactContextBaseJavaModule{
constants.put("packageVersion", updateContext.getPackageVersion()); constants.put("packageVersion", updateContext.getPackageVersion());
constants.put("currentVersion", updateContext.getCurrentVersion()); constants.put("currentVersion", updateContext.getCurrentVersion());
constants.put("buildTime", updateContext.getBuildTime()); constants.put("buildTime", updateContext.getBuildTime());
constants.put("isUsingBundleUrl", updateContext.getIsUsingBundleUrl());
boolean isFirstTime = updateContext.isFirstTime(); boolean isFirstTime = updateContext.isFirstTime();
constants.put("isFirstTime", isFirstTime); constants.put("isFirstTime", isFirstTime);
if (isFirstTime) { if (isFirstTime) {
@@ -52,21 +63,23 @@ public class UpdateModule extends ReactContextBaseJavaModule{
if (isRolledBack) { if (isRolledBack) {
updateContext.clearRollbackMark(); updateContext.clearRollbackMark();
} }
constants.put("blockUpdate", updateContext.getBlockUpdate());
constants.put("uuid", updateContext.getUuid());
return constants; return constants;
} }
@Override @Override
public String getName() { public String getName() {
return "RCTHotUpdate"; return "RCTPushy";
} }
@ReactMethod @ReactMethod
public void downloadUpdate(ReadableMap options, final Promise promise){ public void downloadUpdate(ReadableMap options, final Promise promise) {
String url = options.getString("updateUrl"); String url = options.getString("updateUrl");
String hash = options.getString("hashName"); String hash = options.getString("hash");
updateContext.downloadFile(url, hash, new UpdateContext.DownloadFileListener() { updateContext.downloadFullUpdate(url, hash, new UpdateContext.DownloadFileListener() {
@Override @Override
public void onDownloadCompleted() { public void onDownloadCompleted(DownloadTaskParams params) {
promise.resolve(null); promise.resolve(null);
} }
@@ -78,12 +91,61 @@ public class UpdateModule extends ReactContextBaseJavaModule{
} }
@ReactMethod @ReactMethod
public void downloadPatchFromPackage(ReadableMap options, final Promise promise){ public void downloadAndInstallApk(ReadableMap options, final Promise promise) {
String url = options.getString("url");
String hash = options.getString("hash");
String target = options.getString("target");
updateContext.downloadFile(url, hash, target, new UpdateContext.DownloadFileListener() {
@Override
public void onDownloadCompleted(DownloadTaskParams params) {
installApk(params.targetFile);
promise.resolve(null);
}
@Override
public void onDownloadFailed(Throwable error) {
promise.reject(error);
}
});
}
// install downloaded apk
@ReactMethod
public static void installApk(String url) {
File toInstall = new File(url);
installApk(toInstall);
}
public static void installApk(File toInstall) {
Uri apkUri;
Intent intent;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
apkUri = getUriForFile(mContext, mContext.getPackageName() + ".pushy.fileprovider", toInstall);
intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.setData(apkUri);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
} else {
apkUri = Uri.fromFile(toInstall);
intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
}
}
@ReactMethod
public void downloadPatchFromPackage(ReadableMap options, final Promise promise) {
String url = options.getString("updateUrl"); String url = options.getString("updateUrl");
String hash = options.getString("hashName"); String hash = options.getString("hash");
if (hash == null) {
hash = options.getString("hashName");
}
updateContext.downloadPatchFromApk(url, hash, new UpdateContext.DownloadFileListener() { updateContext.downloadPatchFromApk(url, hash, new UpdateContext.DownloadFileListener() {
@Override @Override
public void onDownloadCompleted() { public void onDownloadCompleted(DownloadTaskParams params) {
promise.resolve(null); promise.resolve(null);
} }
@@ -95,13 +157,19 @@ public class UpdateModule extends ReactContextBaseJavaModule{
} }
@ReactMethod @ReactMethod
public void downloadPatchFromPpk(ReadableMap options, final Promise promise){ public void downloadPatchFromPpk(ReadableMap options, final Promise promise) {
String url = options.getString("updateUrl"); String url = options.getString("updateUrl");
String hash = options.getString("hashName"); String hash = options.getString("hash");
String originHash = options.getString("originHashName"); if (hash == null) {
hash = options.getString("hashName");
}
String originHash = options.getString("originHash");
if (originHash == null) {
originHash = options.getString(("originHashName"));
}
updateContext.downloadPatchFromPpk(url, hash, originHash, new UpdateContext.DownloadFileListener() { updateContext.downloadPatchFromPpk(url, hash, originHash, new UpdateContext.DownloadFileListener() {
@Override @Override
public void onDownloadCompleted() { public void onDownloadCompleted(DownloadTaskParams params) {
promise.resolve(null); promise.resolve(null);
} }
@@ -114,36 +182,41 @@ public class UpdateModule extends ReactContextBaseJavaModule{
@ReactMethod @ReactMethod
public void reloadUpdate(ReadableMap options) { public void reloadUpdate(ReadableMap options) {
final String hash = options.getString("hashName"); final String hash = options.getString("hash") == null ?
options.getString("hashName") : options.getString("hash");
UiThreadUtil.runOnUiThread(new Runnable() { UiThreadUtil.runOnUiThread(new Runnable() {
@Override @Override
public void run() { public void run() {
updateContext.switchVersion(hash);
try { try {
updateContext.switchVersion(hash);
Activity activity = getCurrentActivity(); Activity activity = getCurrentActivity();
Application application = activity.getApplication(); Application application = activity.getApplication();
ReactInstanceManager instanceManager = ((ReactApplication) application).getReactNativeHost().getReactInstanceManager(); ReactInstanceManager instanceManager = updateContext.getCustomReactInstanceManager();
if (instanceManager == null) {
instanceManager = ((ReactApplication) application).getReactNativeHost().getReactInstanceManager();
}
try { try {
Field jsBundleField = instanceManager.getClass().getDeclaredField("mJSBundleFile");
jsBundleField.setAccessible(true);
jsBundleField.set(instanceManager, UpdateContext.getBundleUrl(application));
} catch (Throwable err) {
JSBundleLoader loader = JSBundleLoader.createFileLoader(UpdateContext.getBundleUrl(application)); JSBundleLoader loader = JSBundleLoader.createFileLoader(UpdateContext.getBundleUrl(application));
Field loadField = instanceManager.getClass().getDeclaredField("mBundleLoader"); Field loadField = instanceManager.getClass().getDeclaredField("mBundleLoader");
loadField.setAccessible(true); loadField.setAccessible(true);
loadField.set(instanceManager, loader); loadField.set(instanceManager, loader);
} catch (Throwable err) {
Field jsBundleField = instanceManager.getClass().getDeclaredField("mJSBundleFile");
jsBundleField.setAccessible(true);
jsBundleField.set(instanceManager, UpdateContext.getBundleUrl(application));
} }
try { try {
instanceManager.recreateReactContextInBackground(); instanceManager.recreateReactContextInBackground();
} catch(Throwable err) { } catch (Throwable err) {
activity.recreate(); activity.recreate();
} }
} catch (Throwable err) { } catch (Throwable err) {
Log.e("pushy", "Failed to restart application", err); Log.e("pushy", "switchVersion failed", err);
} }
} }
}); });
@@ -151,12 +224,17 @@ public class UpdateModule extends ReactContextBaseJavaModule{
@ReactMethod @ReactMethod
public void setNeedUpdate(ReadableMap options) { public void setNeedUpdate(ReadableMap options) {
final String hash = options.getString("hashName"); final String hash = options.getString("hash") == null ?
options.getString("hashName") : options.getString("hash");
UiThreadUtil.runOnUiThread(new Runnable() { UiThreadUtil.runOnUiThread(new Runnable() {
@Override @Override
public void run() { public void run() {
updateContext.switchVersion(hash); try {
updateContext.switchVersion(hash);
} catch (Throwable err) {
Log.e("pushy", "switchVersionLater failed", err);
}
} }
}); });
} }
@@ -170,4 +248,32 @@ public class UpdateModule extends ReactContextBaseJavaModule{
} }
}); });
} }
@ReactMethod
public void setBlockUpdate(ReadableMap options) {
final int until = options.getInt("until");
final String reason = options.getString("reason");
UiThreadUtil.runOnUiThread(new Runnable() {
@Override
public void run() {
updateContext.setBlockUpdate(until, reason);
}
});
}
@ReactMethod
public void setUuid(final String uuid) {
UiThreadUtil.runOnUiThread(new Runnable() {
@Override
public void run() {
updateContext.setUuid(uuid);
}
});
}
/* 发送事件*/
public static void sendEvent(String eventName, WritableMap params) {
((ReactContext) mContext).getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName,
params);
}
} }

View File

@@ -0,0 +1,5 @@
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path
name="pushy"
path="." />
</paths>

View File

@@ -1,66 +0,0 @@
# API
### downloadRootDir
下载的根目录。你可以使用react-native-fs等第三方组件检查其中的内容。
### packageVersion
当前应用包的版本名。
### currentVersion
当前版本的Hash号。
### isFirstTime
是否更新后的首次启动。当此项为真时,你需要在合适的时候调用`markSuccess()`以确保更新成功。否则应用下一次启动时将会回滚。
### isRolledBack
是否刚刚经历了一次回滚。
### async function checkUpdate(appKey)
检查更新,返回值有三种情形:
1. `{expired: true}`:该应用包(原生部分)已过期,需要前往应用市场下载新的版本。
```
{
expired: true,
downloadUrl: 'http://appstore/downloadUrl',
}
```
2. `{upToDate: true}`:当前已经更新到最新,无需进行更新。
3. `{update: true}`当前有新版本可以更新。info的`name``description`字段可
以用于提示用户,而`metaInfo`字段则可以根据你的需求自定义其它属性(如是否静默更新、
是否强制更新等等)。另外还有几个字段,包含了完整更新包或补丁包的下载地址,
```
{
update: true,
name: '1.0.3-rc',
hash: 'hash',
description: '添加聊天功能\n修复商城页面BUG',
metaInfo: '{"silent":true}',
updateUrl: 'http://update-packages.reactnative.cn/hash',
pdiffUrl: 'http://update-packages.reactnative.cn/hash',
diffUrl: 'http://update-packages.reactnative.cn/hash',
}
```
### async function downloadUpdate(info)
下载更新版本。info为checkUpdate函数的返回值并且仅当`update:true`时实际进行下载。
### function switchVersion(hash)
立即重启应用,并加载已经下载完毕的版本。
### function switchVersionLater(hash)
在下一次启动应用的时候加载已经下载完毕的版本。
### function markSuccess()
在isFirstTime为true时需在应用成功启动后调用此函数

View File

@@ -1,118 +0,0 @@
# 命令行工具
## 安装
```
$ npm install -g react-native-update-cli
$ npm install --save react-native-update
```
## 使用
#### pushy bundle
生成资源包
* platform: ios|android 对应的平台
* entryFile: 入口脚本文件
* intermediaDir: 临时文件输出目录
* output: 最终ppk文件输出路径
* dev: 是否打包开发版本
* verbose: 是否展现打包过程的详细信息
#### pushy diff <origin> <next>
提供两个ppk文件生成从origin到next版本的差异更新包。
* output: diff文件输出路径
#### pushy diffFromApk <apkFile> <next>
提供一个apk文件和一个ppk文件生成从apk文件到next版本的差异更新包。
如果使用热更新开放平台,你不需要自己执行此命令。
* output: diff文件输出路径
#### pushy diffFromIpa <ipaFile> <next>
提供一个ipa文件和一个ppk文件生成从ipa文件到next版本的差异更新包。
如果使用热更新开放平台,你不需要自己执行此命令。
* output: diff文件输出路径
#### pushy login [<email>] [<pwd>]
登录热更新开放平台。你需要先登录才能使用下面的命令。
#### pushy logout
登出并清除本地的登录信息
#### pushy me
查看自己是否已经登录,以及昵称等信息。
#### pushy createApp
创建应用并立刻绑定到当前工程。这项操作也可以在网页管理端进行。
- platform: ios|android 对应的平台
- name: 应用名称
- downloadUrl: 应用安装包的下载地址
#### pushy deleteApp [appId]
删除已有应用。所有已创建的应用包、热更新版本都会被同时删除。这项操作也可以在网页管理端进行。
- platform: ios|android 对应的平台
#### pushy apps
查看当前已创建的全部应用。这项操作也可以在网页管理端进行。
- platform: ios|android 对应的平台
#### pushy selectApp [appId]
绑定应用到当前工程。
- platform: ios|android 对应的平台
#### pushy uploadIpa <ipaFile>
上传 ipa 文件到开放平台。
#### pushy uploadApk <apkFile>
上传 apk 文件到开放平台。
#### pushy packages
查看已经上传的包。这项操作也可以在网页管理端进行。
- platform: ios|android 对应的平台
#### pushy publish <ppkFile>
发布新的更新版本。
- platform: ios|android 对应的平台
- name: 当前版本的名字(版本号)
- description: 当前版本的描述信息,可以对用户进行展示
- metaInfo: 当前版本的元信息,可以用来保存一些额外信息
#### pushy versions
分页列举可用的版本。这项操作也可以在网页管理端进行。
- platform: ios|android 对应的平台
#### pushy update
为一个包版本绑定一个更新版本。这项操作也可以在网页管理端进行。
- platform: ios|android 对应的平台
- versionId: 要绑定的版本 ID
- packageId: 要绑定的包 ID

View File

@@ -1,71 +0,0 @@
## 常见问题
#### 报错 NDK not configured.
你需要下载并安装NDK然后设置到环境变量`ANDROID_NDK_HOME`中。
#### 报错 Execution failed for task ':react-native-update:compileReleaseNdk'
参看 https://github.com/reactnativecn/react-native-pushy/issues/64#issuecomment-287967742
 
#### iOS报错 Unable to execute JS call: __fbBatchedBridge is undefined
如果直接修改了jsCodeLocation将不能在iOS模拟器上运行。可以使用真机测试。要在发布之前测试热更新功能可以用adhoc方式发布测试包并进行测试。adhoc发布的包可以用于uploadIpa和生成差异包。
#### XCode报错 "_BZ2_bzRead", referenced from 等
在工程target的Build Phases->Link Binary with Libraries中加入libz.tbd、libbz2.1.0.tbd
## 高级指南
#### 过期的版本
你可以删除掉过期很久的版本。在一段时间后,版本会被真正清理。
如果有用户还处在已经被清理的版本上,当他发起更新的时候,将不能通过版本差异比对进行更新,即只能进行全量更新。
#### CI的集成
在开发环境中每次bundle都会生成一个不同名字的ppk文件这不利于持续集成(CI)系统的引入。
要解决这个问题,你可以使用`--output`参数来指定输出ppk文件的名字和路径便于进行自动发布。
#### 版本测试与发布
我们强烈建议您先发布一个测试包,再发布一个除了版本号以外均完全相同的发布包。
在每次往发布包发起热更新之前,先往对应的测试包进行更新操作,基本测试通过之后,可以将发布包更新到完全相同的热更新版本之上。
如果在测试包中发现了重大问题,你就可以先进行修复,再次更新测试通过后,再将发布包更新至修复后的版本。
这样,可以最大程度的避免用户通过热更新获得一个有问题的版本。
#### 元信息(Meta Info)的使用
在发布热更新版本时,或者在网页端,你可以编辑版本的元信息。
这是一段在检查更新时可以获得的字符串,你可以在其中按你所想的格式保存一些信息。
举例来说,可能某个版本包含一些重要的更新内容,所以用户会得到一个不同样式的通知。如何使用元信息,完全取决于您的想象力!
下面会列举一些实战中更有意义的元信息的使用。
#### Hot-fix
有时候我们不小心发布了一个有严重问题的版本,所以需要进行一个紧急的修复,
此时我们可能希望之前已经更新到有问题版本的用户进行紧急甚至静默进行更新。
这时候,我们可以在元信息中包含有问题的版本的列表,而在客户端检查更新时,将从元信息里取到的列表与当前版本(currentVersion)比对,
如果匹配成功,我们就进行静默更新,否则则按照一般的更新流程提示用户。
#### 这个热更新服务收费吗?
目前我们的热更新服务完全免费但限制每个账号不超过3个应用每个应用不超过10个活跃的包和100个活跃的热更新版本每个应用每个月不超过10000次下载。iOS和Android版本记做不同的应用。
已经移除的应用、包版本、热更新版本不在统计之列,所以你可以移除测试时产生的和已过期版本来更有效的利用空间。
我们会在将来推出付费的升级版本,针对用户量较大、版本迭代较快的用户提供扩容方案。如果您有急迫的需求,可以[联系我们](https://reactnative.cn/about.html#content)。
#### 我是否可以搭建自己的热更新服务?
你可以单独使用本组件的原生部分(不包括js模块)和命令行工具中的`bundle``diff``diffFromIpa``diffFromApk`四个功能。
这些功能都不会使用我们的热更新服务也无需注册或登录账号。但你可能要编写自己的js模块来与不同的热更新服务器通讯。
如果您有兴趣使用我们的成果,搭建私有云服务,可以[联系我们](https://reactnative.cn/about.html#content)。

View File

@@ -1,218 +0,0 @@
# 快速入门-准备工作
首先你应该有一个基于React Native开发的应用我们把具有package.json的目录叫做你的"应用根目录"。
如果你还没有初始化应用,请参阅[开始使用React Native](https://reactnative.cn/docs/getting-started.html#content)。
所以我们也假设你已经拥有了开发React Native应用的一切环境包括`Node.js``npm``XCode``Android SDK`等等。
## 安装
在你的项目根目录下运行以下命令:
```bash
npm i -g react-native-update-cli
npm i react-native-update
```
> 如果下载极慢或者显示网络失败,请设置使用淘宝镜像`npx nrm use taobao`
如果你的RN版本 >= 0.60请在iOS目录下执行:
```bash
pod install
```
如果你的RN版本 < 0.60那么还需要[手动link](#一手动link)
<details>
<summary>
如果你的RN版本比较老< 0.46请点击这里的注意事项
</summary>
如果你的RN版本比较老请按下面表格尝试老一些的版本但这些版本我们已不再维护不能保证可以使用
| React Native版本 | react-native-update版本 |
| ---------------- | ----------------------- |
| 0.26及以下 | 1.0.x |
| 0.27 - 0.28 | 2.x |
| 0.29 - 0.33 | 3.x |
| 0.34 - 0.45 | 4.x |
安装命令示例:
```
npm i react-native-update@4.x
```
如果RN的版本是0.45及以下,你还必须安装[Android NDK](http://androiddevtools.cn)版本最好选用r10e并设置环境变量`ANDROID_NDK_HOME`指向你的NDK根目录(例如`/Users/tdzl2003/Downloads/android-ndk-r10e`)。
</details>
请记得一定要重新编译react-native run-ios或run-android命令编译或在Xcode/Android Studio中重新编译
## 一、手动link
如果RN版本 >= 0.60则可以跳过此步骤
### iOS
<details>
<summary>RN < 0.60且使用CocoaPods推荐</summary>
1. 在ios/Podfile中添加
```
pod 'react-native-update', path: '../node_modules/react-native-update'
```
2. 在项目的ios目录下运行`pod install`
3. 重新编译
</details>
<details>
<summary>RN < 0.60且不使用CocoaPods</summary>
1. 在XCode中的Project Navigator里,右键点击`Libraries``Add Files to [你的工程名]`
2. 进入`node_modules``react-native-update``ios 并选中 `RCTHotUpdate.xcodeproj`
3. 在XCode中的project navigator里,选中你的工程,在 `Build Phases``Link Binary With Libraries` 中添加 `libRCTHotUpdate.a`
4. 继续在`Build Settings`里搜索`Header Search Path`,添加$(SRCROOT)/../node_modules/react-native-update/ios
5. 重新编译
</details>
### Android
<details>
<summary>RN < 0.60</summary>
1.`android/settings.gradle`中添加如下代码:
```
include ':react-native-update'
project(':react-native-update').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-update/android')
```
2. 在`android/app/build.gradle`的 dependencies 部分增加如下代码:
```
implementation project(':react-native-update')
```
3. 打开`android/app/src/main/java/[...]/MainApplication.java`,
- 在文件开头增加 `import cn.reactnative.modules.update.UpdatePackage;`
- 在`getPackages()` 方法中增加 `new UpdatePackage()`(注意上一行可能要增加一个逗号)
</details>
## 二、配置Bundle URL
注意此步骤无论任何版本,目前都需要手动配置。
### iOS
1. (RN >= 0.60或者使用CocoaPods集成此步可跳过)在工程target的Build Phases->Link Binary with Libraries中加入`libz.tbd`、`libbz2.1.0.tbd`
2. 在你的AppDelegate.m文件中增加如下代码
```objective-c
// ... 其它代码
#import "RCTHotUpdate.h"
// 如果RN版本 >= 0.59修改sourceURLForBridge
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
#else
 // 非DEBUG情况下替换为热更新bundle
return [RCTHotUpdate bundleURL];
#endif
}
// 如果RN版本 < 0.59修改didFinishLaunchingWithOptions
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
#if DEBUG
 // 原来的jsCodeLocation保留在这里
 jsCodeLocation = ..........
#else
 // 非DEBUG情况下替换为热更新bundle
 jsCodeLocation = [RCTHotUpdate bundleURL];
#endif
// ... 其它代码
}
```
### Android
在MainApplication中增加如下代码
```java
// ... 其它代码
// 请注意不要少了这句import
import cn.reactnative.modules.update.UpdateContext;
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
protected String getJSBundleFile() {
return UpdateContext.getBundleUrl(MainApplication.this);
}
// ... 其它代码
}
}
```
## 三、登录与创建应用
首先请在<https://update.reactnative.cn>注册帐号,然后在你的项目根目录下运行以下命令:
```bash
$ pushy login
email: <输入你的注册邮箱>
password: <输入你的密码>
```
这会在项目文件夹下创建一个`.update`文件注意不要把这个文件上传到Git等CVS系统上。你可以在`.gitignore`末尾增加一行`.update`来忽略这个文件。
登录之后可以创建应用。注意iOS平台和安卓平台需要分别创建
```bash
$ pushy createApp --platform ios
App Name: <输入应用名字>
$ pushy createApp --platform android
App Name: <输入应用名字>
```
> 两次输入的名字可以相同,这没有关系。
如果你已经在网页端或者其它地方创建过应用,也可以直接选择应用:
```bash
$ pushy selectApp --platform ios
1) 鱼多多(ios)
2) 招财旺(ios)
Total 2 ios apps
Enter appId: <输入应用前面的编号>
```
选择或者创建过应用后,你将可以在文件夹下看到`update.json`文件,其内容类似如下形式:
```bash
{
"ios": {
"appId": 1,
"appKey": "<一串随机字符串>"
},
"android": {
"appId": 2,
"appKey": "<一串随机字符串>"
}
}
```
你可以安全的把`update.json`上传到Git等CVS系统上与你的团队共享这个文件它不包含任何敏感信息。当然他们在使用任何功能之前都必须首先输入`pushy login`进行登录。
至此应用的创建/选择就已经成功了。下一步,你需要给代码添加相应的功能,请参阅[添加热更新功能](guide2.md)

View File

@@ -1,177 +0,0 @@
# 快速入门-添加热更新功能
## 获取appKey
检查更新时必须提供你的`appKey`,这个值保存在`update.json`中,并且根据平台不同而不同。你可以用如下的代码获取:
```javascript
import {
Platform,
} from 'react-native';
import _updateConfig from './update.json';
const {appKey} = _updateConfig[Platform.OS];
```
如果你不使用pushy命令行你也可以从网页端查看到两个应用appKey并根据平台的不同来选择。
## 检查更新、下载更新
异步函数checkUpdate可以检查当前版本是否需要更新
```javascript
const info = await checkUpdate(appKey)
```
返回的info有三种情况
1. `{expired: true}`:该应用包(原生部分)已过期,需要前往应用市场下载新的版本。
2. `{upToDate: true}`:当前已经更新到最新,无需进行更新。
3. `{update: true}`当前有新版本可以更新。info的`name``description`字段可
以用于提示用户,而`metaInfo`字段则可以根据你的需求自定义其它属性(如是否静默更新、
是否强制更新等等)。另外还有几个字段,包含了完整更新包或补丁包的下载地址,
react-native-update会首先尝试耗费流量更少的更新方式。将info对象传递给downloadUpdate作为参数即可。
## 切换版本
downloadUpdate的返回值是一个hash字符串它是当前版本的唯一标识。
你可以使用`switchVersion`函数立即切换版本(此时应用会立即重新加载),或者选择调用
`switchVersionLater`,让应用在下一次启动的时候再加载新的版本。
## 首次启动、回滚
在每次更新完毕后的首次启动时,`isFirstTime`常量会为`true`
你必须在应用退出前合适的任何时机,调用`markSuccess`,否则应用下一次启动的时候将会进行回滚操作。
这一机制称作“反触发”,这样当你应用启动初期即遭遇问题的时候,也能在下一次启动时恢复运作。
你可以通过`isFirstTime`来获知这是当前版本的首次启动,也可以通过`isRolledBack`来获知应用刚刚经历了一次回滚操作。
你可以在此时给予用户合理的提示。
## 完整的示例
```javascript
import React, {
Component,
} from 'react';
import {
AppRegistry,
StyleSheet,
Platform,
Text,
View,
Alert,
TouchableOpacity,
Linking,
} from 'react-native';
import {
isFirstTime,
isRolledBack,
packageVersion,
currentVersion,
checkUpdate,
downloadUpdate,
switchVersion,
switchVersionLater,
markSuccess,
} from 'react-native-update';
import _updateConfig from './update.json';
const {appKey} = _updateConfig[Platform.OS];
class MyProject extends Component {
componentDidMount(){
if (isFirstTime) {
Alert.alert('提示', '这是当前版本第一次启动,是否要模拟启动失败?失败将回滚到上一版本', [
{text: '是', onPress: ()=>{throw new Error('模拟启动失败,请重启应用')}},
{text: '否', onPress: ()=>{markSuccess()}},
]);
} else if (isRolledBack) {
Alert.alert('提示', '刚刚更新失败了,版本被回滚.');
}
}
doUpdate = async (info) => {
try {
const hash = await downloadUpdate(info);
Alert.alert('提示', '下载完毕,是否重启应用?', [
{text: '是', onPress: ()=>{switchVersion(hash);}},
{text: '否',},
{text: '下次启动时', onPress: ()=>{switchVersionLater(hash);}},
]);
} catch(err) {
Alert.alert('提示', '更新失败.');
}
};
checkUpdate = async () => {
if (__DEV__) {
// 开发模式不支持热更新,跳过检查
return;
}
let info;
try {
info = await checkUpdate(appKey);
} catch (err) {
console.warn(err);
return;
}
if (info.expired) {
Alert.alert('提示', '您的应用版本已更新,请前往应用商店下载新的版本', [
{text: '确定', onPress: ()=>{info.downloadUrl && Linking.openURL(info.downloadUrl)}},
]);
} else if (info.upToDate) {
Alert.alert('提示', '您的应用版本已是最新.');
} else {
Alert.alert('提示', '检查到新的版本'+info.name+',是否下载?\n'+ info.description, [
{text: '是', onPress: ()=>{this.doUpdate(info)}},
{text: '否',},
]);
}
};
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
欢迎使用热更新服务
</Text>
<Text style={styles.instructions}>
这是版本一 {'\n'}
当前包版本号: {packageVersion}{'\n'}
当前版本Hash: {currentVersion||'(空)'}{'\n'}
</Text>
<TouchableOpacity onPress={this.checkUpdate}>
<Text style={styles.instructions}>
点击这里检查更新
</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
AppRegistry.registerComponent('MyProject', () => MyProject);
```
现在你的应用已经可以通过update服务检查版本并进行更新了。下一步你可以开始尝试发布应用包和版本请参阅[发布应用](guide3.md)

View File

@@ -1,80 +0,0 @@
# 快速入门-发布应用
现在你的应用已经具备了检测更新的功能,下面我们来尝试发布并更新它。
> **注意**从update上传发布版本到发布版本正式上线期间不要修改任何脚本和资源这会影响update
获取本地代码,从而导致版本不能更新。如果在发布之前修改了脚本或资源,请在网页端删除之前上传的版本并重新上传。
## 发布iOS应用
首先参考[文档-在设备上运行](https://reactnative.cn/docs/running-on-device-ios.html#content)
确定你正在使用离线包。然后点击菜单。
按照正常的发布流程打包`.ipa`文件(Xcode中运行设备选真机或Generic iOS Device然后菜单中选择Product-Archive),然后运行如下命令
```bash
$ pushy uploadIpa <your-package.ipa>
```
即可上传ipa以供后续版本比对之用。
随后你可以选择往AppStore发布这个版本也可以先通过Test flight等方法进行测试。
## 发布安卓应用
首先参考[文档-生成已签名的APK](https://reactnative.cn/docs/signed-apk-android.html#content)设置签名,
然后在android文件夹下运行`./gradlew assembleRelease``./gradlew aR`,你就可以在`android/app/build/outputs/apk/release/app-release.apk`中找到你的应用包。
然后运行如下命令
```bash
$ pushy uploadApk android/app/build/outputs/apk/release/app-release.apk
```
即可上传apk以供后续版本比对之用。
随后你可以选择往应用市场发布这个版本也可以先往设备上直接安装这个apk文件以进行测试。
## 发布新的热更新版本
你可以尝试修改一行代码(譬如将版本一修改为版本二),然后生成新的热更新版本。
```bash
$ pushy bundle --platform <ios|android>
Bundling with React Native version: 0.22.2
<各种进度输出>
Bundled saved to: build/output/android.1459850548545.ppk
Would you like to publish it?(Y/N)
```
如果想要立即发布此时输入Y。当然你也可以在将来使用`pushy publish --platform <ios|android> <ppkFile>`来发布版本。
```
Uploading [========================================================] 100% 0.0s
Enter version name: <输入版本名字如1.0.0-rc>
Enter description: <输入版本描述>
Enter meta info: {"ok":1}
Ok.
Would you like to bind packages to this version?(Y/N)
```
此时版本已经提交到update服务但用户暂时看不到此更新你需要先将特定的包版本绑定到此热更新版本上。
此时输入Y立即绑定你也可以在将来使用`pushy update --platform <ios|android>`来使得对应包版本的用户更新。
除此以外,你还可以在网页端操作,简单的将对应的包版本拖到此版本下即可。
```
Offset 0
1) FvXnROJ1 1.0.1 (no package)
2) FiWYm9lB 1.0 [1.0]
Enter versionId or page Up/page Down/Begin(U/D/B) <输入序号,U/D翻页,B回到开始序号就是上面列表中)前面的数字>
1) 1.0(normal) - 3 FiWYm9lB (未命名)
Total 1 packages.
Enter packageId: <输入包版本序号,序号就是上面列表中)前面的数字>
```
版本绑定完毕后,客户端就应当可以检查到更新并进行更新了。
恭喜你,至此为止,你已经完成了植入代码热更新的全部工作。接下来,你可以查阅[常见问题与高级指南](faq_advance.md)了解更多深入的知识,尤其是在实际项目中的运用技巧。

1
domains.json Normal file
View File

@@ -0,0 +1 @@
["update.react-native.cn", "update.reactnative.cn"]

1
endpoints.json Normal file
View File

@@ -0,0 +1 @@
["https://update.react-native.cn/api", "https://update.reactnative.cn/api"]

View File

@@ -1,80 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0720"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "91C5EFFE1C76ECA90037E727"
BuildableName = "libRCTHotUpdate.a"
BlueprintName = "RCTHotUpdate"
ReferencedContainer = "container:RCTHotUpdate.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "91C5EFFE1C76ECA90037E727"
BuildableName = "libRCTHotUpdate.a"
BlueprintName = "RCTHotUpdate"
ReferencedContainer = "container:RCTHotUpdate.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "91C5EFFE1C76ECA90037E727"
BuildableName = "libRCTHotUpdate.a"
BlueprintName = "RCTHotUpdate"
ReferencedContainer = "container:RCTHotUpdate.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>RCTHotUpdate.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>22</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>91C5EFFE1C76ECA90037E727</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

View File

@@ -1,80 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0720"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "91C5EFFE1C76ECA90037E727"
BuildableName = "libRCTHotUpdate.a"
BlueprintName = "RCTHotUpdate"
ReferencedContainer = "container:RCTHotUpdate.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "91C5EFFE1C76ECA90037E727"
BuildableName = "libRCTHotUpdate.a"
BlueprintName = "RCTHotUpdate"
ReferencedContainer = "container:RCTHotUpdate.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "91C5EFFE1C76ECA90037E727"
BuildableName = "libRCTHotUpdate.a"
BlueprintName = "RCTHotUpdate"
ReferencedContainer = "container:RCTHotUpdate.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>RCTHotUpdate.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>1</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>91C5EFFE1C76ECA90037E727</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

View File

@@ -1,19 +0,0 @@
//
// RCTHotUpdate.h
// RCTHotUpdate
//
// Created by LvBingru on 2/19/16.
// Copyright © 2016 erica. All rights reserved.
//
#if __has_include(<React/RCTBridge.h>)
#import <React/RCTBridgeModule.h>
#else
#import "RCTBridgeModule.h"
#endif
@interface RCTHotUpdate : NSObject<RCTBridgeModule>
+ (NSURL *)bundleURL;
@end

View File

@@ -7,12 +7,12 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
91C5F0031C76ECA90037E727 /* RCTHotUpdate.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 91C5F0021C76ECA90037E727 /* RCTHotUpdate.h */; }; 91C5F0031C76ECA90037E727 /* RCTPushy.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 91C5F0021C76ECA90037E727 /* RCTPushy.h */; };
91C5F0051C76ECA90037E727 /* RCTHotUpdate.m in Sources */ = {isa = PBXBuildFile; fileRef = 91C5F0041C76ECA90037E727 /* RCTHotUpdate.m */; }; 91C5F0051C76ECA90037E727 /* RCTPushy.m in Sources */ = {isa = PBXBuildFile; fileRef = 91C5F0041C76ECA90037E727 /* RCTPushy.m */; };
9F1BCB1D1CAE5937000EF2CB /* RCTHotUpdateManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F1BCB1C1CAE5937000EF2CB /* RCTHotUpdateManager.m */; }; 9F1BCB1D1CAE5937000EF2CB /* RCTPushyManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F1BCB1C1CAE5937000EF2CB /* RCTPushyManager.m */; };
9F1BCB461CAF6B3E000EF2CB /* bspatch.c in Sources */ = {isa = PBXBuildFile; fileRef = 9F1BCB3B1CAF6B3E000EF2CB /* bspatch.c */; }; 9F1BCB461CAF6B3E000EF2CB /* bspatch.c in Sources */ = {isa = PBXBuildFile; fileRef = 9F1BCB3B1CAF6B3E000EF2CB /* bspatch.c */; };
9F1BCB4F1CAF6B68000EF2CB /* BSDiff.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F1BCB4E1CAF6B68000EF2CB /* BSDiff.m */; }; 9F1BCB4F1CAF6B68000EF2CB /* BSDiff.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F1BCB4E1CAF6B68000EF2CB /* BSDiff.m */; };
9F292F7D1C7C44290095945D /* RCTHotUpdateDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F292F7C1C7C44290095945D /* RCTHotUpdateDownloader.m */; }; 9F292F7D1C7C44290095945D /* RCTPushyDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F292F7C1C7C44290095945D /* RCTPushyDownloader.m */; };
9F394D7D1C7C25DC00C794C0 /* aescrypt.c in Sources */ = {isa = PBXBuildFile; fileRef = 9F394D5B1C7C25DC00C794C0 /* aescrypt.c */; }; 9F394D7D1C7C25DC00C794C0 /* aescrypt.c in Sources */ = {isa = PBXBuildFile; fileRef = 9F394D5B1C7C25DC00C794C0 /* aescrypt.c */; };
9F394D7E1C7C25DC00C794C0 /* aeskey.c in Sources */ = {isa = PBXBuildFile; fileRef = 9F394D5C1C7C25DC00C794C0 /* aeskey.c */; }; 9F394D7E1C7C25DC00C794C0 /* aeskey.c in Sources */ = {isa = PBXBuildFile; fileRef = 9F394D5C1C7C25DC00C794C0 /* aeskey.c */; };
9F394D7F1C7C25DC00C794C0 /* aestab.c in Sources */ = {isa = PBXBuildFile; fileRef = 9F394D5E1C7C25DC00C794C0 /* aestab.c */; }; 9F394D7F1C7C25DC00C794C0 /* aestab.c in Sources */ = {isa = PBXBuildFile; fileRef = 9F394D5E1C7C25DC00C794C0 /* aestab.c */; };
@@ -36,24 +36,24 @@
dstPath = "include/$(PRODUCT_NAME)"; dstPath = "include/$(PRODUCT_NAME)";
dstSubfolderSpec = 16; dstSubfolderSpec = 16;
files = ( files = (
91C5F0031C76ECA90037E727 /* RCTHotUpdate.h in CopyFiles */, 91C5F0031C76ECA90037E727 /* RCTPushy.h in CopyFiles */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
/* End PBXCopyFilesBuildPhase section */ /* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
91C5EFFF1C76ECA90037E727 /* libRCTHotUpdate.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTHotUpdate.a; sourceTree = BUILT_PRODUCTS_DIR; }; 91C5EFFF1C76ECA90037E727 /* libRCTPushy.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTPushy.a; sourceTree = BUILT_PRODUCTS_DIR; };
91C5F0021C76ECA90037E727 /* RCTHotUpdate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTHotUpdate.h; sourceTree = "<group>"; }; 91C5F0021C76ECA90037E727 /* RCTPushy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTPushy.h; sourceTree = "<group>"; };
91C5F0041C76ECA90037E727 /* RCTHotUpdate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTHotUpdate.m; sourceTree = "<group>"; }; 91C5F0041C76ECA90037E727 /* RCTPushy.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTPushy.m; sourceTree = "<group>"; };
9F1BCB1B1CAE5937000EF2CB /* RCTHotUpdateManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTHotUpdateManager.h; sourceTree = "<group>"; }; 9F1BCB1B1CAE5937000EF2CB /* RCTPushyManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPushyManager.h; sourceTree = "<group>"; };
9F1BCB1C1CAE5937000EF2CB /* RCTHotUpdateManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTHotUpdateManager.m; sourceTree = "<group>"; }; 9F1BCB1C1CAE5937000EF2CB /* RCTPushyManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTPushyManager.m; sourceTree = "<group>"; };
9F1BCB3B1CAF6B3E000EF2CB /* bspatch.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bspatch.c; sourceTree = "<group>"; }; 9F1BCB3B1CAF6B3E000EF2CB /* bspatch.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bspatch.c; sourceTree = "<group>"; };
9F1BCB3C1CAF6B3E000EF2CB /* bspatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bspatch.h; sourceTree = "<group>"; }; 9F1BCB3C1CAF6B3E000EF2CB /* bspatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bspatch.h; sourceTree = "<group>"; };
9F1BCB4D1CAF6B68000EF2CB /* BSDiff.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BSDiff.h; sourceTree = "<group>"; }; 9F1BCB4D1CAF6B68000EF2CB /* BSDiff.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BSDiff.h; sourceTree = "<group>"; };
9F1BCB4E1CAF6B68000EF2CB /* BSDiff.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BSDiff.m; sourceTree = "<group>"; }; 9F1BCB4E1CAF6B68000EF2CB /* BSDiff.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BSDiff.m; sourceTree = "<group>"; };
9F292F7B1C7C44290095945D /* RCTHotUpdateDownloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTHotUpdateDownloader.h; sourceTree = "<group>"; }; 9F292F7B1C7C44290095945D /* RCTPushyDownloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPushyDownloader.h; sourceTree = "<group>"; };
9F292F7C1C7C44290095945D /* RCTHotUpdateDownloader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTHotUpdateDownloader.m; sourceTree = "<group>"; }; 9F292F7C1C7C44290095945D /* RCTPushyDownloader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTPushyDownloader.m; sourceTree = "<group>"; };
9F394D591C7C25DC00C794C0 /* aes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = aes.h; sourceTree = "<group>"; }; 9F394D591C7C25DC00C794C0 /* aes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = aes.h; sourceTree = "<group>"; };
9F394D5A1C7C25DC00C794C0 /* aes_via_ace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = aes_via_ace.h; sourceTree = "<group>"; }; 9F394D5A1C7C25DC00C794C0 /* aes_via_ace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = aes_via_ace.h; sourceTree = "<group>"; };
9F394D5B1C7C25DC00C794C0 /* aescrypt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = aescrypt.c; sourceTree = "<group>"; }; 9F394D5B1C7C25DC00C794C0 /* aescrypt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = aescrypt.c; sourceTree = "<group>"; };
@@ -105,7 +105,7 @@
91C5EFF61C76ECA90037E727 = { 91C5EFF61C76ECA90037E727 = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
91C5F0011C76ECA90037E727 /* RCTHotUpdate */, 91C5F0011C76ECA90037E727 /* RCTPushy */,
91C5F0001C76ECA90037E727 /* Products */, 91C5F0001C76ECA90037E727 /* Products */,
); );
sourceTree = "<group>"; sourceTree = "<group>";
@@ -113,24 +113,24 @@
91C5F0001C76ECA90037E727 /* Products */ = { 91C5F0001C76ECA90037E727 /* Products */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
91C5EFFF1C76ECA90037E727 /* libRCTHotUpdate.a */, 91C5EFFF1C76ECA90037E727 /* libRCTPushy.a */,
); );
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
91C5F0011C76ECA90037E727 /* RCTHotUpdate */ = { 91C5F0011C76ECA90037E727 /* RCTPushy */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
9F1BCB381CAF6B3E000EF2CB /* BSDiff */, 9F1BCB381CAF6B3E000EF2CB /* BSDiff */,
9F394D571C7C25DC00C794C0 /* SSZipArchive */, 9F394D571C7C25DC00C794C0 /* SSZipArchive */,
91C5F0021C76ECA90037E727 /* RCTHotUpdate.h */, 91C5F0021C76ECA90037E727 /* RCTPushy.h */,
91C5F0041C76ECA90037E727 /* RCTHotUpdate.m */, 91C5F0041C76ECA90037E727 /* RCTPushy.m */,
9F1BCB1B1CAE5937000EF2CB /* RCTHotUpdateManager.h */, 9F1BCB1B1CAE5937000EF2CB /* RCTPushyManager.h */,
9F1BCB1C1CAE5937000EF2CB /* RCTHotUpdateManager.m */, 9F1BCB1C1CAE5937000EF2CB /* RCTPushyManager.m */,
9F292F7B1C7C44290095945D /* RCTHotUpdateDownloader.h */, 9F292F7B1C7C44290095945D /* RCTPushyDownloader.h */,
9F292F7C1C7C44290095945D /* RCTHotUpdateDownloader.m */, 9F292F7C1C7C44290095945D /* RCTPushyDownloader.m */,
); );
path = RCTHotUpdate; path = RCTPushy;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
9F1BCB381CAF6B3E000EF2CB /* BSDiff */ = { 9F1BCB381CAF6B3E000EF2CB /* BSDiff */ = {
@@ -213,9 +213,9 @@
/* End PBXGroup section */ /* End PBXGroup section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
91C5EFFE1C76ECA90037E727 /* RCTHotUpdate */ = { 91C5EFFE1C76ECA90037E727 /* RCTPushy */ = {
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 91C5F0081C76ECA90037E727 /* Build configuration list for PBXNativeTarget "RCTHotUpdate" */; buildConfigurationList = 91C5F0081C76ECA90037E727 /* Build configuration list for PBXNativeTarget "RCTPushy" */;
buildPhases = ( buildPhases = (
91C5EFFB1C76ECA90037E727 /* Sources */, 91C5EFFB1C76ECA90037E727 /* Sources */,
91C5EFFC1C76ECA90037E727 /* Frameworks */, 91C5EFFC1C76ECA90037E727 /* Frameworks */,
@@ -225,9 +225,9 @@
); );
dependencies = ( dependencies = (
); );
name = RCTHotUpdate; name = RCTPushy;
productName = RCTHotUpdate; productName = RCTPushy;
productReference = 91C5EFFF1C76ECA90037E727 /* libRCTHotUpdate.a */; productReference = 91C5EFFF1C76ECA90037E727 /* libRCTPushy.a */;
productType = "com.apple.product-type.library.static"; productType = "com.apple.product-type.library.static";
}; };
/* End PBXNativeTarget section */ /* End PBXNativeTarget section */
@@ -244,11 +244,12 @@
}; };
}; };
}; };
buildConfigurationList = 91C5EFFA1C76ECA90037E727 /* Build configuration list for PBXProject "RCTHotUpdate" */; buildConfigurationList = 91C5EFFA1C76ECA90037E727 /* Build configuration list for PBXProject "RCTPushy" */;
compatibilityVersion = "Xcode 3.2"; compatibilityVersion = "Xcode 3.2";
developmentRegion = English; developmentRegion = English;
hasScannedForEncodings = 0; hasScannedForEncodings = 0;
knownRegions = ( knownRegions = (
English,
en, en,
); );
mainGroup = 91C5EFF61C76ECA90037E727; mainGroup = 91C5EFF61C76ECA90037E727;
@@ -256,7 +257,7 @@
projectDirPath = ""; projectDirPath = "";
projectRoot = ""; projectRoot = "";
targets = ( targets = (
91C5EFFE1C76ECA90037E727 /* RCTHotUpdate */, 91C5EFFE1C76ECA90037E727 /* RCTPushy */,
); );
}; };
/* End PBXProject section */ /* End PBXProject section */
@@ -271,20 +272,20 @@
9F394D871C7C25DC00C794C0 /* mztools.c in Sources */, 9F394D871C7C25DC00C794C0 /* mztools.c in Sources */,
9F394D821C7C25DC00C794C0 /* hmac.c in Sources */, 9F394D821C7C25DC00C794C0 /* hmac.c in Sources */,
9F394D881C7C25DC00C794C0 /* unzip.c in Sources */, 9F394D881C7C25DC00C794C0 /* unzip.c in Sources */,
9F1BCB1D1CAE5937000EF2CB /* RCTHotUpdateManager.m in Sources */, 9F1BCB1D1CAE5937000EF2CB /* RCTPushyManager.m in Sources */,
9F1BCB4F1CAF6B68000EF2CB /* BSDiff.m in Sources */, 9F1BCB4F1CAF6B68000EF2CB /* BSDiff.m in Sources */,
9F394D7E1C7C25DC00C794C0 /* aeskey.c in Sources */, 9F394D7E1C7C25DC00C794C0 /* aeskey.c in Sources */,
9F394D7F1C7C25DC00C794C0 /* aestab.c in Sources */, 9F394D7F1C7C25DC00C794C0 /* aestab.c in Sources */,
9F394D7D1C7C25DC00C794C0 /* aescrypt.c in Sources */, 9F394D7D1C7C25DC00C794C0 /* aescrypt.c in Sources */,
9F394D801C7C25DC00C794C0 /* entropy.c in Sources */, 9F394D801C7C25DC00C794C0 /* entropy.c in Sources */,
9F292F7D1C7C44290095945D /* RCTHotUpdateDownloader.m in Sources */, 9F292F7D1C7C44290095945D /* RCTPushyDownloader.m in Sources */,
9F394D831C7C25DC00C794C0 /* prng.c in Sources */, 9F394D831C7C25DC00C794C0 /* prng.c in Sources */,
9F394D861C7C25DC00C794C0 /* ioapi.c in Sources */, 9F394D861C7C25DC00C794C0 /* ioapi.c in Sources */,
9F1BCB461CAF6B3E000EF2CB /* bspatch.c in Sources */, 9F1BCB461CAF6B3E000EF2CB /* bspatch.c in Sources */,
9F394D8A1C7C25DC00C794C0 /* SSZipArchive.m in Sources */, 9F394D8A1C7C25DC00C794C0 /* SSZipArchive.m in Sources */,
9F394D891C7C25DC00C794C0 /* zip.c in Sources */, 9F394D891C7C25DC00C794C0 /* zip.c in Sources */,
9F394D841C7C25DC00C794C0 /* pwd2key.c in Sources */, 9F394D841C7C25DC00C794C0 /* pwd2key.c in Sources */,
91C5F0051C76ECA90037E727 /* RCTHotUpdate.m in Sources */, 91C5F0051C76ECA90037E727 /* RCTPushy.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -400,7 +401,7 @@
/* End XCBuildConfiguration section */ /* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */ /* Begin XCConfigurationList section */
91C5EFFA1C76ECA90037E727 /* Build configuration list for PBXProject "RCTHotUpdate" */ = { 91C5EFFA1C76ECA90037E727 /* Build configuration list for PBXProject "RCTPushy" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (
91C5F0061C76ECA90037E727 /* Debug */, 91C5F0061C76ECA90037E727 /* Debug */,
@@ -409,7 +410,7 @@
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release; defaultConfigurationName = Release;
}; };
91C5F0081C76ECA90037E727 /* Build configuration list for PBXNativeTarget "RCTHotUpdate" */ = { 91C5F0081C76ECA90037E727 /* Build configuration list for PBXNativeTarget "RCTPushy" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (
91C5F0091C76ECA90037E727 /* Debug */, 91C5F0091C76ECA90037E727 /* Debug */,

View File

@@ -1,6 +1,6 @@
// //
// BSDiff.h // BSDiff.h
// RCTHotUpdate // RCTPushy
// //
// Created by lvbingru on 16/4/2. // Created by lvbingru on 16/4/2.
// Copyright © 2016年 erica. All rights reserved. // Copyright © 2016年 erica. All rights reserved.

View File

@@ -1,6 +1,6 @@
// //
// BSDiff.m // BSDiff.m
// RCTHotUpdate // RCTPushy
// //
// Created by lvbingru on 16/4/2. // Created by lvbingru on 16/4/2.
// Copyright © 2016 erica. All rights reserved. // Copyright © 2016 erica. All rights reserved.

17
ios/RCTPushy/RCTPushy.h Normal file
View File

@@ -0,0 +1,17 @@
//
// RCTPushy.h
// RCTPushy
//
// Created by LvBingru on 2/19/16.
// Copyright © 2016 erica. All rights reserved.
//
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>
@interface RCTPushy : RCTEventEmitter<RCTBridgeModule>
+ (NSURL *)bundleURL;
@end

View File

@@ -1,35 +1,31 @@
// //
// RCTHotUpdate.m // RCTPushy.m
// RCTHotUpdate // RCTPushy
// //
// Created by LvBingru on 2/19/16. // Created by LvBingru on 2/19/16.
// Copyright © 2016 erica. All rights reserved. // Copyright © 2016 erica. All rights reserved.
// //
#import "RCTHotUpdate.h" #import "RCTPushy.h"
#import "RCTHotUpdateDownloader.h" #import "RCTPushyDownloader.h"
#import "RCTHotUpdateManager.h" #import "RCTPushyManager.h"
#if __has_include(<React/RCTBridge.h>)
#import "React/RCTEventDispatcher.h"
#import <React/RCTConvert.h> #import <React/RCTConvert.h>
#import <React/RCTLog.h> #import <React/RCTLog.h>
#else // #import <React/RCTReloadCommand.h>
#import "RCTEventDispatcher.h"
#import "RCTConvert.h"
#import "RCTLog.h"
#endif
// static NSString *const keyPushyInfo = @"REACTNATIVECN_PUSHY_INFO_KEY";
static NSString *const keyUpdateInfo = @"REACTNATIVECN_HOTUPDATE_INFO_KEY";
static NSString *const paramPackageVersion = @"packageVersion"; static NSString *const paramPackageVersion = @"packageVersion";
static NSString *const paramLastVersion = @"lastVersion"; static NSString *const paramLastVersion = @"lastVersion";
static NSString *const paramCurrentVersion = @"currentVersion"; static NSString *const paramCurrentVersion = @"currentVersion";
static NSString *const paramIsFirstTime = @"isFirstTime"; static NSString *const paramIsFirstTime = @"isFirstTime";
static NSString *const paramIsFirstLoadOk = @"isFirstLoadOK"; static NSString *const paramIsFirstLoadOk = @"isFirstLoadOK";
static NSString *const keyFirstLoadMarked = @"REACTNATIVECN_HOTUPDATE_FIRSTLOADMARKED_KEY"; static NSString *const keyBlockUpdate = @"REACTNATIVECN_PUSHY_BLOCKUPDATE";
static NSString *const keyRolledBackMarked = @"REACTNATIVECN_HOTUPDATE_ROLLEDBACKMARKED_KEY"; static NSString *const keyUuid = @"REACTNATIVECN_PUSHY_UUID";
static NSString *const KeyPackageUpdatedMarked = @"REACTNATIVECN_HOTUPDATE_ISPACKAGEUPDATEDMARKED_KEY"; static NSString *const keyFirstLoadMarked = @"REACTNATIVECN_PUSHY_FIRSTLOADMARKED_KEY";
static NSString *const keyRolledBackMarked = @"REACTNATIVECN_PUSHY_ROLLEDBACKMARKED_KEY";
static NSString *const KeyPackageUpdatedMarked = @"REACTNATIVECN_PUSHY_ISPACKAGEUPDATEDMARKED_KEY";
// app info // app info
static NSString * const AppVersionKey = @"appVersion"; static NSString * const AppVersionKey = @"appVersion";
@@ -46,98 +42,106 @@ static NSString * const ERROR_BSDIFF = @"bsdiff error";
static NSString * const ERROR_FILE_OPERATION = @"file operation error"; static NSString * const ERROR_FILE_OPERATION = @"file operation error";
// event def // event def
static NSString * const EVENT_PROGRESS_DOWNLOAD = @"RCTHotUpdateDownloadProgress"; static NSString * const EVENT_PROGRESS_DOWNLOAD = @"RCTPushyDownloadProgress";
static NSString * const EVENT_PROGRESS_UNZIP = @"RCTHotUpdateUnzipProgress"; // static NSString * const EVENT_PROGRESS_UNZIP = @"RCTPushyUnzipProgress";
static NSString * const PARAM_PROGRESS_HASHNAME = @"hashname"; static NSString * const PARAM_PROGRESS_HASH = @"hash";
static NSString * const PARAM_PROGRESS_RECEIVED = @"received"; static NSString * const PARAM_PROGRESS_RECEIVED = @"received";
static NSString * const PARAM_PROGRESS_TOTAL = @"total"; static NSString * const PARAM_PROGRESS_TOTAL = @"total";
typedef NS_ENUM(NSInteger, HotUpdateType) { typedef NS_ENUM(NSInteger, PushyType) {
HotUpdateTypeFullDownload = 1, PushyTypeFullDownload = 1,
HotUpdateTypePatchFromPackage = 2, PushyTypePatchFromPackage = 2,
HotUpdateTypePatchFromPpk = 3, PushyTypePatchFromPpk = 3,
}; };
static BOOL ignoreRollback = false; static BOOL ignoreRollback = false;
@implementation RCTHotUpdate { @implementation RCTPushy {
RCTHotUpdateManager *_fileManager; RCTPushyManager *_fileManager;
bool hasListeners;
} }
@synthesize bridge = _bridge;
@synthesize methodQueue = _methodQueue; @synthesize methodQueue = _methodQueue;
RCT_EXPORT_MODULE(RCTHotUpdate); RCT_EXPORT_MODULE(RCTPushy);
+ (NSURL *)bundleURL + (NSURL *)bundleURL
{ {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *updateInfo = [defaults dictionaryForKey:keyUpdateInfo]; NSDictionary *pushyInfo = [defaults dictionaryForKey:keyPushyInfo];
if (updateInfo) { if (pushyInfo) {
NSString *curPackageVersion = [RCTHotUpdate packageVersion]; NSString *curPackageVersion = [RCTPushy packageVersion];
NSString *packageVersion = [updateInfo objectForKey:paramPackageVersion]; NSString *packageVersion = [pushyInfo objectForKey:paramPackageVersion];
BOOL needClearUpdateInfo = ![curPackageVersion isEqualToString:packageVersion]; BOOL needClearPushyInfo = ![curPackageVersion isEqualToString:packageVersion];
if (needClearUpdateInfo) { if (needClearPushyInfo) {
[defaults setObject:nil forKey:keyUpdateInfo]; [defaults setObject:nil forKey:keyPushyInfo];
[defaults setObject:@(YES) forKey:KeyPackageUpdatedMarked]; [defaults setObject:@(YES) forKey:KeyPackageUpdatedMarked];
[defaults synchronize]; [defaults synchronize];
// ...need clear files later // ...need clear files later
} }
else { else {
NSString *curVersion = updateInfo[paramCurrentVersion]; NSString *curVersion = pushyInfo[paramCurrentVersion];
NSString *lastVersion = updateInfo[paramLastVersion];
BOOL isFirstTime = [updateInfo[paramIsFirstTime] boolValue]; BOOL isFirstTime = [pushyInfo[paramIsFirstTime] boolValue];
BOOL isFirstLoadOK = [updateInfo[paramIsFirstLoadOk] boolValue]; BOOL isFirstLoadOK = [pushyInfo[paramIsFirstLoadOk] boolValue];
NSString *loadVersioin = curVersion; NSString *loadVersion = curVersion;
BOOL needRollback = (!ignoreRollback && isFirstTime == NO && isFirstLoadOK == NO) || loadVersioin.length<=0; BOOL needRollback = (!ignoreRollback && isFirstTime == NO && isFirstLoadOK == NO) || loadVersion.length<=0;
if (needRollback) { if (needRollback) {
loadVersioin = lastVersion; loadVersion = [self rollback];
if (lastVersion.length) {
// roll back to last version
[defaults setObject:@{paramCurrentVersion:lastVersion,
paramIsFirstTime:@(NO),
paramIsFirstLoadOk:@(YES),
paramPackageVersion:curPackageVersion}
forKey:keyUpdateInfo];
}
else {
// roll back to bundle
[defaults setObject:nil forKey:keyUpdateInfo];
}
[defaults setObject:@(YES) forKey:keyRolledBackMarked];
[defaults synchronize];
// ...need clear files later
} }
else if (isFirstTime && !ignoreRollback){ else if (isFirstTime && !ignoreRollback){
// bundleURL may be called many times, ignore rollbacks before process restarted again. // bundleURL may be called many times, ignore rollbacks before process restarted again.
ignoreRollback = true; ignoreRollback = true;
NSMutableDictionary *newInfo = [[NSMutableDictionary alloc] initWithDictionary:updateInfo]; NSMutableDictionary *newInfo = [[NSMutableDictionary alloc] initWithDictionary:pushyInfo];
newInfo[paramIsFirstTime] = @(NO); newInfo[paramIsFirstTime] = @(NO);
[defaults setObject:newInfo forKey:keyUpdateInfo]; [defaults setObject:newInfo forKey:keyPushyInfo];
[defaults setObject:@(YES) forKey:keyFirstLoadMarked]; [defaults setObject:@(YES) forKey:keyFirstLoadMarked];
[defaults synchronize]; [defaults synchronize];
} }
if (loadVersioin.length) { NSString *downloadDir = [RCTPushy downloadDir];
NSString *downloadDir = [RCTHotUpdate downloadDir]; while (loadVersion.length) {
NSString *bundlePath = [[downloadDir stringByAppendingPathComponent:loadVersion] stringByAppendingPathComponent:BUNDLE_FILE_NAME];
NSString *bundlePath = [[downloadDir stringByAppendingPathComponent:loadVersioin] stringByAppendingPathComponent:BUNDLE_FILE_NAME];
if ([[NSFileManager defaultManager] fileExistsAtPath:bundlePath isDirectory:NULL]) { if ([[NSFileManager defaultManager] fileExistsAtPath:bundlePath isDirectory:NULL]) {
NSURL *bundleURL = [NSURL fileURLWithPath:bundlePath]; NSURL *bundleURL = [NSURL fileURLWithPath:bundlePath];
return bundleURL; return bundleURL;
} else {
RCTLogError(@"RCTPushy -- bundle version %@ not found", loadVersion);
loadVersion = [self rollback];
} }
} }
} }
} }
return [RCTHotUpdate binaryBundleURL]; return [RCTPushy binaryBundleURL];
}
+ (NSString *) rollback {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *pushyInfo = [defaults dictionaryForKey:keyPushyInfo];
NSString *lastVersion = pushyInfo[paramLastVersion];
NSString *curPackageVersion = [RCTPushy packageVersion];
if (lastVersion.length) {
// roll back to last version
[defaults setObject:@{paramCurrentVersion:lastVersion,
paramIsFirstTime:@(NO),
paramIsFirstLoadOk:@(YES),
paramPackageVersion:curPackageVersion}
forKey:keyPushyInfo];
}
else {
// roll back to bundle
[defaults setObject:nil forKey:keyPushyInfo];
}
[defaults setObject:@(YES) forKey:keyRolledBackMarked];
[defaults synchronize];
return lastVersion;
} }
+ (BOOL)requiresMainQueueSetup { + (BOOL)requiresMainQueueSetup {
@@ -150,13 +154,15 @@ RCT_EXPORT_MODULE(RCTHotUpdate);
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *ret = [NSMutableDictionary new]; NSMutableDictionary *ret = [NSMutableDictionary new];
ret[@"downloadRootDir"] = [RCTHotUpdate downloadDir]; ret[@"downloadRootDir"] = [RCTPushy downloadDir];
ret[@"packageVersion"] = [RCTHotUpdate packageVersion]; ret[@"packageVersion"] = [RCTPushy packageVersion];
ret[@"buildTime"] = [RCTHotUpdate buildTime]; ret[@"buildTime"] = [RCTPushy buildTime];
ret[@"isRolledBack"] = [defaults objectForKey:keyRolledBackMarked]; ret[@"isRolledBack"] = [defaults objectForKey:keyRolledBackMarked];
ret[@"isFirstTime"] = [defaults objectForKey:keyFirstLoadMarked]; ret[@"isFirstTime"] = [defaults objectForKey:keyFirstLoadMarked];
NSDictionary *updateInfo = [defaults dictionaryForKey:keyUpdateInfo]; ret[@"blockUpdate"] = [defaults objectForKey:keyBlockUpdate];
ret[@"currentVersion"] = [updateInfo objectForKey:paramCurrentVersion]; ret[@"uuid"] = [defaults objectForKey:keyUuid];
NSDictionary *pushyInfo = [defaults dictionaryForKey:keyPushyInfo];
ret[@"currentVersion"] = [pushyInfo objectForKey:paramCurrentVersion];
// clear isFirstTimemarked // clear isFirstTimemarked
if ([[defaults objectForKey:keyFirstLoadMarked] boolValue]) { if ([[defaults objectForKey:keyFirstLoadMarked] boolValue]) {
@@ -183,16 +189,33 @@ RCT_EXPORT_MODULE(RCTHotUpdate);
{ {
self = [super init]; self = [super init];
if (self) { if (self) {
_fileManager = [RCTHotUpdateManager new]; _fileManager = [RCTPushyManager new];
} }
return self; return self;
} }
RCT_EXPORT_METHOD(setBlockUpdate:(NSDictionary *)options)
{
// NSMutableDictionary *blockUpdateInfo = [NSMutableDictionary new];
// blockUpdateInfo[@"reason"] = options[@"reason"];
// blockUpdateInfo[@"until"] = options[@"until"];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:options forKey:keyBlockUpdate];
[defaults synchronize];
}
RCT_EXPORT_METHOD(setUuid:(NSString *)uuid)
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:uuid forKey:keyUuid];
[defaults synchronize];
}
RCT_EXPORT_METHOD(downloadUpdate:(NSDictionary *)options RCT_EXPORT_METHOD(downloadUpdate:(NSDictionary *)options
resolver:(RCTPromiseResolveBlock)resolve resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) rejecter:(RCTPromiseRejectBlock)reject)
{ {
[self hotUpdate:HotUpdateTypeFullDownload options:options callback:^(NSError *error) { [self doPushy:PushyTypeFullDownload options:options callback:^(NSError *error) {
if (error) { if (error) {
reject([NSString stringWithFormat: @"%lu", (long)error.code], error.localizedDescription, error); reject([NSString stringWithFormat: @"%lu", (long)error.code], error.localizedDescription, error);
} }
@@ -206,7 +229,7 @@ RCT_EXPORT_METHOD(downloadPatchFromPackage:(NSDictionary *)options
resolver:(RCTPromiseResolveBlock)resolve resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) rejecter:(RCTPromiseRejectBlock)reject)
{ {
[self hotUpdate:HotUpdateTypePatchFromPackage options:options callback:^(NSError *error) { [self doPushy:PushyTypePatchFromPackage options:options callback:^(NSError *error) {
if (error) { if (error) {
reject([NSString stringWithFormat: @"%lu", (long)error.code], error.localizedDescription, error); reject([NSString stringWithFormat: @"%lu", (long)error.code], error.localizedDescription, error);
} }
@@ -220,7 +243,7 @@ RCT_EXPORT_METHOD(downloadPatchFromPpk:(NSDictionary *)options
resolver:(RCTPromiseResolveBlock)resolve resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) rejecter:(RCTPromiseRejectBlock)reject)
{ {
[self hotUpdate:HotUpdateTypePatchFromPpk options:options callback:^(NSError *error) { [self doPushy:PushyTypePatchFromPpk options:options callback:^(NSError *error) {
if (error) { if (error) {
reject([NSString stringWithFormat: @"%lu", (long)error.code], error.localizedDescription, error); reject([NSString stringWithFormat: @"%lu", (long)error.code], error.localizedDescription, error);
} }
@@ -232,22 +255,25 @@ RCT_EXPORT_METHOD(downloadPatchFromPpk:(NSDictionary *)options
RCT_EXPORT_METHOD(setNeedUpdate:(NSDictionary *)options) RCT_EXPORT_METHOD(setNeedUpdate:(NSDictionary *)options)
{ {
NSString *hashName = options[@"hashName"]; NSString *hash = options[@"hash"];
if (hashName.length) { if (hash.length <= 0) {
hash = options[@"hashName"];
}
if (hash.length) {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *lastVersion = nil; NSString *lastVersion = nil;
if ([defaults objectForKey:keyUpdateInfo]) { if ([defaults objectForKey:keyPushyInfo]) {
NSDictionary *updateInfo = [defaults objectForKey:keyUpdateInfo]; NSDictionary *pushyInfo = [defaults objectForKey:keyPushyInfo];
lastVersion = updateInfo[paramCurrentVersion]; lastVersion = pushyInfo[paramCurrentVersion];
} }
NSMutableDictionary *newInfo = [[NSMutableDictionary alloc] init]; NSMutableDictionary *newInfo = [[NSMutableDictionary alloc] init];
newInfo[paramCurrentVersion] = hashName; newInfo[paramCurrentVersion] = hash;
newInfo[paramLastVersion] = lastVersion; newInfo[paramLastVersion] = lastVersion;
newInfo[paramIsFirstTime] = @(YES); newInfo[paramIsFirstTime] = @(YES);
newInfo[paramIsFirstLoadOk] = @(NO); newInfo[paramIsFirstLoadOk] = @(NO);
newInfo[paramPackageVersion] = [RCTHotUpdate packageVersion]; newInfo[paramPackageVersion] = [RCTPushy packageVersion];
[defaults setObject:newInfo forKey:keyUpdateInfo]; [defaults setObject:newInfo forKey:keyPushyInfo];
[defaults synchronize]; [defaults synchronize];
} }
@@ -255,101 +281,136 @@ RCT_EXPORT_METHOD(setNeedUpdate:(NSDictionary *)options)
RCT_EXPORT_METHOD(reloadUpdate:(NSDictionary *)options) RCT_EXPORT_METHOD(reloadUpdate:(NSDictionary *)options)
{ {
NSString *hashName = options[@"hashName"]; NSString *hash = options[@"hash"];
if (hashName.length) { if (hash.length <= 0) {
hash = options[@"hashName"];
}
if (hash.length) {
[self setNeedUpdate:options]; [self setNeedUpdate:options];
// reload // reload 0.62+
dispatch_async(dispatch_get_main_queue(), ^{ // RCTReloadCommandSetBundleURL([[self class] bundleURL]);
[_bridge setValue:[[self class] bundleURL] forKey:@"bundleURL"]; // RCTTriggerReloadCommandListeners(@"pushy reload");
[_bridge reload];
}); dispatch_async(dispatch_get_main_queue(), ^{
[self.bridge setValue:[[self class] bundleURL] forKey:@"bundleURL"];
[self.bridge reload];
});
} }
} }
RCT_EXPORT_METHOD(markSuccess) RCT_EXPORT_METHOD(markSuccess)
{ {
// update package info // up package info
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *packageInfo = [[NSMutableDictionary alloc] initWithDictionary:[defaults objectForKey:keyUpdateInfo]]; NSMutableDictionary *packageInfo = [[NSMutableDictionary alloc] initWithDictionary:[defaults objectForKey:keyPushyInfo]];
[packageInfo setObject:@(NO) forKey:paramIsFirstTime]; [packageInfo setObject:@(NO) forKey:paramIsFirstTime];
[packageInfo setObject:@(YES) forKey:paramIsFirstLoadOk]; [packageInfo setObject:@(YES) forKey:paramIsFirstLoadOk];
[defaults setObject:packageInfo forKey:keyUpdateInfo]; [defaults setObject:packageInfo forKey:keyPushyInfo];
[defaults synchronize]; [defaults synchronize];
// clear other package dir // clear other package dir
[self clearInvalidFiles]; [self clearInvalidFiles];
} }
#pragma mark - private #pragma mark - private
- (void)hotUpdate:(HotUpdateType)type options:(NSDictionary *)options callback:(void (^)(NSError *error))callback - (NSArray<NSString *> *)supportedEvents
{
return @[
EVENT_PROGRESS_DOWNLOAD,
// EVENT_PROGRESS_UNZIP
];
}
// Will be called when this module's first listener is added.
-(void)startObserving {
hasListeners = YES;
// Set up any upstream listeners or background tasks as necessary
}
// Will be called when this module's last listener is removed, or on dealloc.
-(void)stopObserving {
hasListeners = NO;
// Remove upstream listeners, stop unnecessary background tasks
}
- (void)doPushy:(PushyType)type options:(NSDictionary *)options callback:(void (^)(NSError *error))callback
{ {
NSString *updateUrl = [RCTConvert NSString:options[@"updateUrl"]]; NSString *updateUrl = [RCTConvert NSString:options[@"updateUrl"]];
NSString *hashName = [RCTConvert NSString:options[@"hashName"]]; NSString *hash = [RCTConvert NSString:options[@"hash"]];
if (updateUrl.length<=0 || hashName.length<=0) { if (hash.length <= 0) {
hash = [RCTConvert NSString:options[@"hashName"]];;
}
if (updateUrl.length <= 0 || hash.length <= 0) {
callback([self errorWithMessage:ERROR_OPTIONS]); callback([self errorWithMessage:ERROR_OPTIONS]);
return; return;
} }
NSString *originHashName = [RCTConvert NSString:options[@"originHashName"]]; NSString *originHash = [RCTConvert NSString:options[@"originHash"]];
if (type == HotUpdateTypePatchFromPpk && originHashName<=0) { if (type == PushyTypePatchFromPpk && originHash <= 0) {
callback([self errorWithMessage:ERROR_OPTIONS]); callback([self errorWithMessage:ERROR_OPTIONS]);
return; return;
} }
NSString *dir = [RCTHotUpdate downloadDir]; NSString *dir = [RCTPushy downloadDir];
BOOL success = [_fileManager createDir:dir]; BOOL success = [_fileManager createDir:dir];
if (!success) { if (!success) {
callback([self errorWithMessage:ERROR_FILE_OPERATION]); callback([self errorWithMessage:ERROR_FILE_OPERATION]);
return; return;
} }
NSString *zipFilePath = [dir stringByAppendingPathComponent:[NSString stringWithFormat:@"%@%@",hashName, [self zipExtension:type]]];
NSString *unzipDir = [dir stringByAppendingPathComponent:hashName];
RCTLogInfo(@"RNUpdate -- download file %@", updateUrl); NSString *zipFilePath = [dir stringByAppendingPathComponent:[NSString stringWithFormat:@"%@%@",hash, [self zipExtension:type]]];
[RCTHotUpdateDownloader download:updateUrl savePath:zipFilePath progressHandler:^(long long receivedBytes, long long totalBytes) { // NSString *unzipDir = [dir stringByAppendingPathComponent:hash];
[self.bridge.eventDispatcher sendAppEventWithName:EVENT_PROGRESS_DOWNLOAD
body:@{ RCTLogInfo(@"RCTPushy -- download file %@", updateUrl);
PARAM_PROGRESS_HASHNAME:hashName, [RCTPushyDownloader download:updateUrl savePath:zipFilePath progressHandler:^(long long receivedBytes, long long totalBytes) {
PARAM_PROGRESS_RECEIVED:[NSNumber numberWithLongLong:receivedBytes], if (self->hasListeners) {
PARAM_PROGRESS_TOTAL:[NSNumber numberWithLongLong:totalBytes] [self sendEventWithName:EVENT_PROGRESS_DOWNLOAD body:@{
}]; PARAM_PROGRESS_HASH:hash,
PARAM_PROGRESS_RECEIVED:[NSNumber numberWithLongLong:receivedBytes],
PARAM_PROGRESS_TOTAL:[NSNumber numberWithLongLong:totalBytes]
}];
}
} completionHandler:^(NSString *path, NSError *error) { } completionHandler:^(NSString *path, NSError *error) {
if (error) { if (error) {
callback(error); callback(error);
} }
else { else {
RCTLogInfo(@"RNUpdate -- unzip file %@", zipFilePath); RCTLogInfo(@"RCTPushy -- unzip file %@", zipFilePath);
NSString *unzipFilePath = [dir stringByAppendingPathComponent:hashName]; NSString *unzipFilePath = [dir stringByAppendingPathComponent:hash];
[_fileManager unzipFileAtPath:zipFilePath toDestination:unzipFilePath progressHandler:^(NSString *entry,long entryNumber, long total) { [self->_fileManager unzipFileAtPath:zipFilePath toDestination:unzipFilePath progressHandler:^(NSString *entry,long entryNumber, long total) {
[self.bridge.eventDispatcher sendAppEventWithName:EVENT_PROGRESS_UNZIP // if (self->hasListeners) {
body:@{ // [self sendEventWithName:EVENT_PROGRESS_UNZIP
PARAM_PROGRESS_HASHNAME:hashName, // body:@{
PARAM_PROGRESS_RECEIVED:[NSNumber numberWithLong:entryNumber], // PARAM_PROGRESS_HASH:hash,
PARAM_PROGRESS_TOTAL:[NSNumber numberWithLong:total] // PARAM_PROGRESS_RECEIVED:[NSNumber numberWithLong:entryNumber],
}]; // PARAM_PROGRESS_TOTAL:[NSNumber numberWithLong:total]
// }];
// }
} completionHandler:^(NSString *path, BOOL succeeded, NSError *error) { } completionHandler:^(NSString *path, BOOL succeeded, NSError *error) {
dispatch_async(_methodQueue, ^{ dispatch_async(self->_methodQueue, ^{
if (error) { if (error) {
callback(error); callback(error);
} }
else { else {
switch (type) { switch (type) {
case HotUpdateTypePatchFromPackage: case PushyTypePatchFromPackage:
{ {
NSString *sourceOrigin = [[NSBundle mainBundle] resourcePath]; NSString *sourceOrigin = [[NSBundle mainBundle] resourcePath];
NSString *bundleOrigin = [[RCTHotUpdate binaryBundleURL] path]; NSString *bundleOrigin = [[RCTPushy binaryBundleURL] path];
[self patch:hashName fromBundle:bundleOrigin source:sourceOrigin callback:callback]; [self patch:hash fromBundle:bundleOrigin source:sourceOrigin callback:callback];
} }
break; break;
case HotUpdateTypePatchFromPpk: case PushyTypePatchFromPpk:
{ {
NSString *lastVertionDir = [dir stringByAppendingPathComponent:originHashName]; NSString *lastVersionDir = [dir stringByAppendingPathComponent:originHash];
NSString *sourceOrigin = lastVertionDir; NSString *sourceOrigin = lastVersionDir;
NSString *bundleOrigin = [lastVertionDir stringByAppendingPathComponent:BUNDLE_FILE_NAME]; NSString *bundleOrigin = [lastVersionDir stringByAppendingPathComponent:BUNDLE_FILE_NAME];
[self patch:hashName fromBundle:bundleOrigin source:sourceOrigin callback:callback]; [self patch:hash fromBundle:bundleOrigin source:sourceOrigin callback:callback];
} }
break; break;
default: default:
@@ -363,9 +424,9 @@ RCT_EXPORT_METHOD(markSuccess)
}]; }];
} }
- (void)patch:(NSString *)hashName fromBundle:(NSString *)bundleOrigin source:(NSString *)sourceOrigin callback:(void (^)(NSError *error))callback - (void)patch:(NSString *)hash fromBundle:(NSString *)bundleOrigin source:(NSString *)sourceOrigin callback:(void (^)(NSError *error))callback
{ {
NSString *unzipDir = [[RCTHotUpdate downloadDir] stringByAppendingPathComponent:hashName]; NSString *unzipDir = [[RCTPushy downloadDir] stringByAppendingPathComponent:hash];
NSString *sourcePatch = [unzipDir stringByAppendingPathComponent:SOURCE_PATCH_NAME]; NSString *sourcePatch = [unzipDir stringByAppendingPathComponent:SOURCE_PATCH_NAME];
NSString *bundlePatch = [unzipDir stringByAppendingPathComponent:BUNDLE_PATCH_NAME]; NSString *bundlePatch = [unzipDir stringByAppendingPathComponent:BUNDLE_PATCH_NAME];
@@ -383,7 +444,7 @@ RCT_EXPORT_METHOD(markSuccess)
NSDictionary *copies = json[@"copies"]; NSDictionary *copies = json[@"copies"];
NSDictionary *deletes = json[@"deletes"]; NSDictionary *deletes = json[@"deletes"];
[_fileManager copyFiles:copies fromDir:sourceOrigin toDir:unzipDir deletes:deletes completionHandler:^(NSError *error) { [self->_fileManager copyFiles:copies fromDir:sourceOrigin toDir:unzipDir deletes:deletes completionHandler:^(NSError *error) {
if (error) { if (error) {
callback(error); callback(error);
} }
@@ -401,10 +462,10 @@ RCT_EXPORT_METHOD(markSuccess)
- (void)clearInvalidFiles - (void)clearInvalidFiles
{ {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *updateInfo = [defaults objectForKey:keyUpdateInfo]; NSDictionary *pushyInfo = [defaults objectForKey:keyPushyInfo];
NSString *curVersion = [updateInfo objectForKey:paramCurrentVersion]; NSString *curVersion = [pushyInfo objectForKey:paramCurrentVersion];
NSString *downloadDir = [RCTHotUpdate downloadDir]; NSString *downloadDir = [RCTPushy downloadDir];
NSError *error = nil; NSError *error = nil;
NSArray *list = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:downloadDir error:&error]; NSArray *list = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:downloadDir error:&error];
if (error) { if (error) {
@@ -418,14 +479,14 @@ RCT_EXPORT_METHOD(markSuccess)
} }
} }
- (NSString *)zipExtension:(HotUpdateType)type - (NSString *)zipExtension:(PushyType)type
{ {
switch (type) { switch (type) {
case HotUpdateTypeFullDownload: case PushyTypeFullDownload:
return @".ppk"; return @".ppk";
case HotUpdateTypePatchFromPackage: case PushyTypePatchFromPackage:
return @".apk.patch"; return @".ipa.patch";
case HotUpdateTypePatchFromPpk: case PushyTypePatchFromPpk:
return @".ppk.patch"; return @".ppk.patch";
default: default:
break; break;
@@ -434,7 +495,7 @@ RCT_EXPORT_METHOD(markSuccess)
- (NSError *)errorWithMessage:(NSString *)errorMessage - (NSError *)errorWithMessage:(NSString *)errorMessage
{ {
return [NSError errorWithDomain:@"cn.reactnative.hotupdate" return [NSError errorWithDomain:@"cn.reactnative.pushy"
code:-1 code:-1
userInfo:@{ NSLocalizedDescriptionKey: errorMessage}]; userInfo:@{ NSLocalizedDescriptionKey: errorMessage}];
} }
@@ -442,7 +503,7 @@ RCT_EXPORT_METHOD(markSuccess)
+ (NSString *)downloadDir + (NSString *)downloadDir
{ {
NSString *directory = [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) firstObject]; NSString *directory = [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) firstObject];
NSString *downloadDir = [directory stringByAppendingPathComponent:@"reactnativecnhotupdate"]; NSString *downloadDir = [directory stringByAppendingPathComponent:@"rctpushy"];
return downloadDir; return downloadDir;
} }

View File

@@ -1,6 +1,6 @@
// //
// RCTHotUpdateDownloader.h // RCTPushyDownloader.h
// RCTHotUpdate // RCTPushy
// //
// Created by lvbingru on 16/2/23. // Created by lvbingru on 16/2/23.
// Copyright © 2016年 erica. All rights reserved. // Copyright © 2016年 erica. All rights reserved.
@@ -8,7 +8,7 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
@interface RCTHotUpdateDownloader : NSObject @interface RCTPushyDownloader : NSObject
+ (void)download:(NSString *)downloadPath savePath:(NSString *)savePath + (void)download:(NSString *)downloadPath savePath:(NSString *)savePath
progressHandler:(void (^)(long long, long long))progressHandler progressHandler:(void (^)(long long, long long))progressHandler

View File

@@ -1,21 +1,21 @@
// //
// RCTHotUpdateDownloader.m // RCTPushyDownloader.m
// RCTHotUpdate // RCTPushy
// //
// Created by lvbingru on 16/2/23. // Created by lvbingru on 16/2/23.
// Copyright © 2016 erica. All rights reserved. // Copyright © 2016 erica. All rights reserved.
// //
#import "RCTHotUpdateDownloader.h" #import "RCTPushyDownloader.h"
@interface RCTHotUpdateDownloader()<NSURLSessionDelegate> @interface RCTPushyDownloader()<NSURLSessionDelegate>
@property (copy) void (^progressHandler)(long long, long long); @property (copy) void (^progressHandler)(long long, long long);
@property (copy) void (^completionHandler)(NSString*, NSError*); @property (copy) void (^completionHandler)(NSString*, NSError*);
@property (copy) NSString *savePath; @property (copy) NSString *savePath;
@end @end
@implementation RCTHotUpdateDownloader @implementation RCTPushyDownloader
+ (void)download:(NSString *)downloadPath savePath:(NSString *)savePath + (void)download:(NSString *)downloadPath savePath:(NSString *)savePath
progressHandler:(void (^)(long long receivedBytes, long long totalBytes))progressHandler progressHandler:(void (^)(long long receivedBytes, long long totalBytes))progressHandler
@@ -24,7 +24,7 @@ completionHandler:(void (^)(NSString *path, NSError *error))completionHandler
NSAssert(downloadPath, @"no download path"); NSAssert(downloadPath, @"no download path");
NSAssert(savePath, @"no save path"); NSAssert(savePath, @"no save path");
RCTHotUpdateDownloader *downloader = [RCTHotUpdateDownloader new]; RCTPushyDownloader *downloader = [RCTPushyDownloader new];
downloader.progressHandler = progressHandler; downloader.progressHandler = progressHandler;
downloader.completionHandler = completionHandler; downloader.completionHandler = completionHandler;
downloader.savePath = savePath; downloader.savePath = savePath;

View File

@@ -1,6 +1,6 @@
// //
// RCTHotUpdateManager.h // RCTPushyManager.h
// RCTHotUpdate // RCTPushy
// //
// Created by lvbingru on 16/4/1. // Created by lvbingru on 16/4/1.
// Copyright © 2016年 erica. All rights reserved. // Copyright © 2016年 erica. All rights reserved.
@@ -8,7 +8,7 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
@interface RCTHotUpdateManager : NSObject @interface RCTPushyManager : NSObject
- (BOOL)createDir:(NSString *)dir; - (BOOL)createDir:(NSString *)dir;

View File

@@ -1,17 +1,17 @@
// //
// RCTHotUpdateManager.m // RCTPushyManager.m
// RCTHotUpdate // RCTPushy
// //
// Created by lvbingru on 16/4/1. // Created by lvbingru on 16/4/1.
// Copyright © 2016 erica. All rights reserved. // Copyright © 2016 erica. All rights reserved.
// //
#import "RCTHotUpdateManager.h" #import "RCTPushyManager.h"
#import "ZipArchive.h" #import "ZipArchive.h"
#import "BSDiff.h" #import "BSDiff.h"
#import "bspatch.h" #import "bspatch.h"
@implementation RCTHotUpdateManager { @implementation RCTPushyManager {
dispatch_queue_t _opQueue; dispatch_queue_t _opQueue;
} }
@@ -19,7 +19,7 @@
{ {
self = [super init]; self = [super init];
if (self) { if (self) {
_opQueue = dispatch_queue_create("cn.reactnative.hotupdate", DISPATCH_QUEUE_SERIAL); _opQueue = dispatch_queue_create("cn.reactnative.pushy", DISPATCH_QUEUE_SERIAL);
} }
return self; return self;
} }
@@ -99,16 +99,9 @@ completionHandler:(void (^)(NSError *error))completionHandler
// merge old files // merge old files
if (deletes!= nil) { if (deletes!= nil) {
NSError *error = nil;
NSString *srcDir = [fromDir stringByAppendingPathComponent:@"assets"]; NSString *srcDir = [fromDir stringByAppendingPathComponent:@"assets"];
NSString *desDir = [toDir stringByAppendingPathComponent:@"assets"]; NSString *desDir = [toDir stringByAppendingPathComponent:@"assets"];
[self _mergeContentsOfPath:srcDir intoPath:desDir deletes:deletes error:&error]; [self _mergeContentsOfPath:srcDir intoPath:desDir deletes:deletes];
if (error) {
if (completionHandler) {
completionHandler(error);
}
return;
}
} }
// copy files // copy files
@@ -126,10 +119,11 @@ completionHandler:(void (^)(NSError *error))completionHandler
NSError *error = nil; NSError *error = nil;
[fm copyItemAtPath:fromPath toPath:toPath error:&error]; [fm copyItemAtPath:fromPath toPath:toPath error:&error];
if (error) { if (error) {
if (completionHandler) { NSLog(@"Pushy copy error: %@", error.localizedDescription);
completionHandler(error); // if (completionHandler) {
} // completionHandler(error);
return; // }
// return;
} }
} }
if (completionHandler) { if (completionHandler) {
@@ -150,7 +144,7 @@ completionHandler:(void (^)(NSError *error))completionHandler
}); });
} }
- (void)_mergeContentsOfPath:(NSString *)srcDir intoPath:(NSString *)dstDir deletes:(NSDictionary *)deletes error:(NSError**)err - (void)_mergeContentsOfPath:(NSString *)srcDir intoPath:(NSString *)dstDir deletes:(NSDictionary *)deletes
{ {
NSFileManager *fm = [NSFileManager defaultManager]; NSFileManager *fm = [NSFileManager defaultManager];
NSDirectoryEnumerator *srcDirEnum = [fm enumeratorAtPath:srcDir]; NSDirectoryEnumerator *srcDirEnum = [fm enumeratorAtPath:srcDir];
@@ -159,7 +153,9 @@ completionHandler:(void (^)(NSError *error))completionHandler
NSString *srcFullPath = [srcDir stringByAppendingPathComponent:subPath]; NSString *srcFullPath = [srcDir stringByAppendingPathComponent:subPath];
NSString *potentialDstPath = [dstDir stringByAppendingPathComponent:subPath]; NSString *potentialDstPath = [dstDir stringByAppendingPathComponent:subPath];
NSError *error = nil;
BOOL inDeletes = NO; BOOL inDeletes = NO;
if (deletes) { if (deletes) {
NSString *path = [@"assets" stringByAppendingPathComponent:subPath]; NSString *path = [@"assets" stringByAppendingPathComponent:subPath];
@@ -171,18 +167,19 @@ completionHandler:(void (^)(NSError *error))completionHandler
BOOL isDirectory = ([fm fileExistsAtPath:srcFullPath isDirectory:&isDirectory] && isDirectory); BOOL isDirectory = ([fm fileExistsAtPath:srcFullPath isDirectory:&isDirectory] && isDirectory);
if (isDirectory) { if (isDirectory) {
if (![fm fileExistsAtPath:potentialDstPath isDirectory:nil]) { if (![fm fileExistsAtPath:potentialDstPath isDirectory:nil]) {
[fm createDirectoryAtPath:potentialDstPath withIntermediateDirectories:YES attributes:nil error:err]; [fm createDirectoryAtPath:potentialDstPath withIntermediateDirectories:YES attributes:nil error:&error];
if (err && *err) { if (error) {
return; NSLog(@"Pushy merge error: %@", error.localizedDescription);
// return;
} }
} }
} }
else { else {
if (![fm fileExistsAtPath:potentialDstPath]) { if (![fm fileExistsAtPath:potentialDstPath]) {
[fm copyItemAtPath:srcFullPath toPath:potentialDstPath error:err]; [fm copyItemAtPath:srcFullPath toPath:potentialDstPath error:&error];
if (err && *err) { if (error) {
return; NSLog(@"Pushy merge error: %@", error.localizedDescription);
// return;
} }
} }
} }

View File

@@ -1 +1 @@
1570371472 1574665292

89
lib/endpoint.js Normal file
View File

@@ -0,0 +1,89 @@
let currentEndpoint = 'https://update.react-native.cn/api';
function ping(url, rejectImmediate) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = (e) => {
if (xhr.readyState !== 4) {
return;
}
if (xhr.status === 200) {
resolve(url);
} else {
rejectImmediate ? reject() : setTimeout(reject, 5000);
}
};
xhr.open('HEAD', url);
xhr.send();
xhr.timeout = 5000;
xhr.ontimeout = reject;
});
}
function logger(...args) {
// console.warn('pushy', ...args);
}
let backupEndpoints = [];
let backupEndpointsQueryUrl =
'https://cdn.jsdelivr.net/gh/reactnativecn/react-native-pushy@master/endpoints.json';
export async function tryBackupEndpoints() {
if (!backupEndpoints.length && !backupEndpointsQueryUrl) {
return;
}
try {
await ping(getStatusUrl(), true);
logger('current endpoint ok');
return;
} catch (e) {
logger('current endpoint failed');
}
if (!backupEndpoints.length && backupEndpointsQueryUrl) {
try {
const resp = await fetch(backupEndpointsQueryUrl);
backupEndpoints = await resp.json();
logger('get remote endpoints:', backupEndpoints);
} catch (e) {
logger('get remote endpoints failed');
return;
}
}
await pickFatestAvailableEndpoint();
}
async function pickFatestAvailableEndpoint(endpoints = backupEndpoints) {
const fastestEndpoint = await Promise.race(
endpoints.map(pingAndReturnEndpoint),
);
if (typeof fastestEndpoint === 'string') {
logger(`pick endpoint: ${fastestEndpoint}`);
currentEndpoint = fastestEndpoint;
} else {
logger('all remote endpoints failed');
}
}
async function pingAndReturnEndpoint(endpoint = currentEndpoint) {
return ping(getStatusUrl(endpoint)).then(() => endpoint);
}
function getStatusUrl(endpoint = currentEndpoint) {
return `${endpoint}/status`;
}
export function getCheckUrl(APPKEY, endpoint = currentEndpoint) {
return `${endpoint}/checkUpdate/${APPKEY}`;
}
export function setCustomEndpoints({ main, backups, backupQueryUrl }) {
currentEndpoint = main;
backupEndpointsQueryUrl = null;
if (Array.isArray(backups) && backups.length > 0) {
backupEndpoints = backups;
pickFatestAvailableEndpoint();
}
if (typeof backupQueryUrl === 'string') {
backupEndpointsQueryUrl = backupQueryUrl;
}
}

49
lib/index.d.ts vendored
View File

@@ -5,33 +5,74 @@ export const isFirstTime: boolean;
export const isRolledBack: boolean; export const isRolledBack: boolean;
export interface ExpiredResult { export interface ExpiredResult {
upToDate?: false;
expired: true; expired: true;
downloadUrl: string; downloadUrl: string;
} }
export interface UpTodateResult { export interface UpTodateResult {
expired?: false;
upToDate: true; upToDate: true;
} }
export interface UpdateAvailableResult { export interface UpdateAvailableResult {
expired?: false;
upToDate?: false;
update: true; update: true;
name: string; // version name name: string; // version name
hash: string; hash: string;
description: string; description: string;
metaInfo: string; metaInfo: string;
updateUrl: string;
pdiffUrl: string; pdiffUrl: string;
diffUrl: string; diffUrl?: string;
} }
export type CheckResult = Partial<ExpiredResult & UpTodateResult & UpdateAvailableResult>; export type CheckResult =
| ExpiredResult
| UpTodateResult
| UpdateAvailableResult;
export function checkUpdate(appkey: string): Promise<CheckResult>; export function checkUpdate(appkey: string): Promise<CheckResult>;
export function downloadUpdate(options: UpdateAvailableResult): Promise<undefined | string>; export function downloadUpdate(
info: UpdateAvailableResult,
eventListeners?: {
onDownloadProgress?: (data: ProgressData) => void;
},
): Promise<undefined | string>;
export function switchVersion(hash: string): void; export function switchVersion(hash: string): void;
export function switchVersionLater(hash: string): void; export function switchVersionLater(hash: string): void;
export function markSuccess(): void; export function markSuccess(): void;
export function downloadAndInstallApk({
url,
onDownloadProgress,
}: {
url: string;
onDownloadProgress?: (data: ProgressData) => void;
}): Promise<void>;
/**
* @param {string} main - The main api endpoint
* @param {string[]} [backups] - The back up endpoints.
* @param {string} [backupQueryUrl] - An url that return a json file containing an array of endpoint.
* like: ["https://backup.api/1", "https://backup.api/2"]
*/
export function setCustomEndpoints({
main,
backups,
backupQueryUrl,
}: {
main: string;
backups?: string[];
backupQueryUrl?: string;
}): void;
interface ProgressData {
hash: string;
received: number;
total: number;
}

View File

@@ -1,18 +1,52 @@
import { NativeAppEventEmitter, NativeModules } from 'react-native'; import {
const { HotUpdate = {} } = NativeModules; tryBackupEndpoints,
getCheckUrl,
setCustomEndpoints,
} from './endpoint';
import { NativeEventEmitter, NativeModules, Platform } from 'react-native';
export { setCustomEndpoints };
const {
version: v,
} = require('react-native/Libraries/Core/ReactNativeVersion');
const RNVersion = `${v.major}.${v.minor}.${v.patch}`;
const host = 'https://update.reactnative.cn/api'; let Pushy = NativeModules.Pushy;
export const downloadRootDir = HotUpdate.downloadRootDir; if (!Pushy) {
export const packageVersion = HotUpdate.packageVersion; throw new Error('react-native-update模块无法加载请对照安装文档检查配置。');
export const currentVersion = HotUpdate.currentVersion; }
export const isFirstTime = HotUpdate.isFirstTime;
export const isRolledBack = HotUpdate.isRolledBack; export const downloadRootDir = Pushy.downloadRootDir;
export const buildTime = HotUpdate.buildTime; export const packageVersion = Pushy.packageVersion;
export const currentVersion = Pushy.currentVersion;
export const isFirstTime = Pushy.isFirstTime;
export const isRolledBack = Pushy.isRolledBack;
export const buildTime = Pushy.buildTime;
let blockUpdate = Pushy.blockUpdate;
let uuid = Pushy.uuid;
if (Platform.OS === 'android' && !Pushy.isUsingBundleUrl) {
throw new Error(
'react-native-update模块无法加载请对照文档检查Bundle URL的配置',
);
}
const eventEmitter = new NativeEventEmitter(Pushy);
if (!uuid) {
uuid = require('uuid/v4')();
Pushy.setUuid(uuid);
}
function logger(text) {
console.log(`Pushy: ${text}`);
}
logger('uuid: ' + uuid);
/* /*
Return json: Return json:
Package was expired: Package expired:
{ {
expired: true, expired: true,
downloadUrl: 'http://appstore/downloadUrl', downloadUrl: 'http://appstore/downloadUrl',
@@ -28,7 +62,6 @@ There is available update:
hash: 'hash', hash: 'hash',
description: '添加聊天功能\n修复商城页面BUG', description: '添加聊天功能\n修复商城页面BUG',
metaInfo: '{"silent":true}', metaInfo: '{"silent":true}',
updateUrl: 'http://update-packages.reactnative.cn/hash',
pdiffUrl: 'http://update-packages.reactnative.cn/hash', pdiffUrl: 'http://update-packages.reactnative.cn/hash',
diffUrl: 'http://update-packages.reactnative.cn/hash', diffUrl: 'http://update-packages.reactnative.cn/hash',
} }
@@ -40,70 +73,138 @@ function assertRelease() {
} }
} }
export async function checkUpdate(APPKEY, isRetry) {
export async function checkUpdate(APPKEY) {
assertRelease(); assertRelease();
const resp = await fetch(`${host}/checkUpdate/${APPKEY}`, { if (blockUpdate && blockUpdate.until > Date.now() / 1000) {
method: 'POST', throw new Error(
headers: { `热更新已暂停,原因:${blockUpdate.reason}。请在"${new Date(
Accept: 'application/json', blockUpdate.until * 1000,
'Content-Type': 'application/json', ).toLocaleString()}"之后重试。`,
}, );
body: JSON.stringify({ }
packageVersion, if (typeof APPKEY !== 'string') {
hash: currentVersion, throw new Error('未检查到合法的APPKEY请查看update.json文件是否正确生成');
buildTime, }
}), logger('checking update');
}); let resp;
try {
resp = await fetch(getCheckUrl(APPKEY), {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
packageVersion,
hash: currentVersion,
buildTime,
cInfo: {
pushy: require('../package.json').version,
rn: RNVersion,
os: Platform.OS + ' ' + Platform.Version,
uuid,
},
}),
});
} catch (e) {
if (isRetry) {
throw new Error('Could not connect to pushy server');
}
await tryBackupEndpoints(APPKEY);
return checkUpdate(APPKEY, true);
}
const result = await resp.json();
checkOperation(result.op);
if (resp.status !== 200) { if (resp.status !== 200) {
throw new Error((await resp.json()).message); throw new Error(result.message);
} }
return resp.json(); return result;
} }
export async function downloadUpdate(options) { function checkOperation(op) {
if (!Array.isArray(op)) {
return;
}
op.forEach((action) => {
if (action.type === 'block') {
blockUpdate = {
reason: action.reason,
until: (Date.now() + action.duration) / 1000,
};
Pushy.setBlockUpdate(blockUpdate);
}
});
}
export async function downloadUpdate(options, eventListeners) {
assertRelease(); assertRelease();
if (!options.update) { if (!options.update) {
return; return;
} }
let progressHandler;
if (eventListeners) {
if (eventListeners.onDownloadProgress) {
const downloadCallback = eventListeners.onDownloadProgress;
progressHandler = eventEmitter.addListener('RCTPushyDownloadProgress', (progressData) => {
if (progressData.hash === options.hash) {
downloadCallback(progressData);
}
});
}
}
if (options.diffUrl) { if (options.diffUrl) {
await HotUpdate.downloadPatchFromPpk({ logger('downloading diff');
await Pushy.downloadPatchFromPpk({
updateUrl: options.diffUrl, updateUrl: options.diffUrl,
hashName: options.hash, hash: options.hash,
originHashName: currentVersion, originHash: currentVersion,
}); });
} else if (options.pdiffUrl) { } else if (options.pdiffUrl) {
await HotUpdate.downloadPatchFromPackage({ logger('downloading pdiff');
await Pushy.downloadPatchFromPackage({
updateUrl: options.pdiffUrl, updateUrl: options.pdiffUrl,
hashName: options.hash, hash: options.hash,
});
} else {
await HotUpdate.downloadUpdate({
updateUrl: options.updateUrl,
hashName: options.hash,
}); });
} }
progressHandler && progressHandler.remove();
return options.hash; return options.hash;
} }
export function switchVersion(hash) { export function switchVersion(hash) {
assertRelease(); assertRelease();
HotUpdate.reloadUpdate({ hashName: hash }); logger('switchVersion');
Pushy.reloadUpdate({ hash });
} }
export function switchVersionLater(hash) { export function switchVersionLater(hash) {
assertRelease(); assertRelease();
HotUpdate.setNeedUpdate({ hashName: hash }); logger('switchVersionLater');
Pushy.setNeedUpdate({ hash });
} }
export function markSuccess() { export function markSuccess() {
assertRelease(); assertRelease();
HotUpdate.markSuccess(); logger('markSuccess');
Pushy.markSuccess();
} }
NativeAppEventEmitter.addListener('RCTHotUpdateDownloadProgress', params => {}); export async function downloadAndInstallApk({ url, onDownloadProgress }) {
logger('downloadAndInstallApk');
NativeAppEventEmitter.addListener('RCTHotUpdateUnzipProgress', params => {}); let hash = Date.now().toString();
let progressHandler;
if (onDownloadProgress) {
progressHandler = eventEmitter.addListener('RCTPushyDownloadProgress', (progressData) => {
if (progressData.hash === hash) {
onDownloadProgress(progressData);
}
});
}
await Pushy.downloadAndInstallApk({
url,
target: 'update.apk',
hash,
});
progressHandler && progressHandler.remove();
}

View File

@@ -1,14 +0,0 @@
{
"plugins": [
"syntax-object-rest-spread",
"syntax-async-functions",
"transform-es2015-arrow-functions",
"transform-async-to-generator",
"transform-es2015-modules-commonjs",
"transform-es2015-destructuring",
"transform-es2015-spread",
"transform-object-rest-spread",
"transform-es2015-parameters",
"transform-strict-mode"
]
}

View File

@@ -1,162 +0,0 @@
{
"useCommand": true,
"defaultCommand": "help",
"commands": {
"help": {
},
"login":{
},
"logout": {
},
"me": {
},
"createApp": {
"options": {
"name": {
"hasValue": true
},
"platform": {
"hasValue": true
}
}
},
"apps": {
"options": {
"platform": {
"hasValue": true
}
}
},
"deleteApp": {
},
"selectApp": {
"options": {
"platform": {
"hasValue": true
}
}
},
"uploadIpa": {
},
"uploadApk": {
},
"packages": {
"options": {
"platform": {
"hasValue": true
}
}
},
"publish": {
"options": {
"platform": {
"hasValue": true
},
"name": {
"hasValue": true
},
"description": {
"hasValue": true
},
"metaInfo": {
"hasValue": true
}
}
},
"versions": {
"options": {
"platform": {
"hasValue": true
}
}
},
"update": {
"options": {
"platform": {
"hasValue": true
},
"versionId": {
"hasValue": true
},
"packageId": {
"hasValue": true
}
}
},
"build": {
"description": "Bundle javascript and copy assets."
},
"bundle": {
"description": "Bundle javascript code only.",
"options": {
"dev": {
"default": "false",
"hasValue": true
},
"platform": {
"hasValue": true
},
"bundleName":{
"default": "index.bundlejs",
"hasValue": true
},
"entryFile": {
"default": "index.js",
"hasValue": true
},
"intermediaDir": {
"default": "build/intermedia/${platform}",
"hasValue": true
},
"output": {
"default": "build/output/${platform}.${time}.ppk",
"hasValue": true
},
"verbose": {
}
}
},
"release": {
"description": "Push builded file to server."
},
"diff": {
"description": "Create diff patch",
"options": {
"output": {
"default": "build/output/diff",
"hasValue": true
}
}
},
"diffFromApk": {
"description": "Create diff patch from a Android package(.apk)",
"options": {
"output": {
"default": "build/output/diff-${time}.apk-patch",
"hasValue": true
}
}
},
"diffFromIpa": {
"description": "Create diff patch from a iOS package(.ipa)",
"options": {
"output": {
"default": "build/output/diff-${time}.ipa-patch",
"hasValue": true
}
}
}
},
"globalOptions":{
"no-interactive": {
"default": false
}
}
}

Some files were not shown because too many files have changed in this diff Show More