Skip to content

Commit dfe8734

Browse files
huhuanmingclaude
andcommitted
feat: add buildNumber change detection, getBuiltinBundleVersion API, and bump to 1.1.40
- Detect native buildNumber changes and clear hot-update bundle data to fall back to builtin bundle - Add getNativeBuildNumber() and getBuiltinBundleVersion() TS interfaces with iOS/Android implementations - Clear both nativeVersion and nativeBuildNumber prefs on version or buildNumber change - Extract clearNativeVersionPrefs helper to avoid hardcoded strings - Add BUNDLE_VERSION env var injection in example app (Android meta-data + iOS Info.plist) - Add example buttons for new APIs in BundleUpdateTestPage Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 2c0348a commit dfe8734

9 files changed

Lines changed: 160 additions & 5 deletions

File tree

example/react-native/android/app/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ android {
8888
targetSdkVersion rootProject.ext.targetSdkVersion
8989
versionCode 1
9090
versionName "1.0"
91+
manifestPlaceholders += [BUNDLE_VERSION: System.getenv('BUNDLE_VERSION') ?: '1']
9192
}
9293
signingConfigs {
9394
debug {

example/react-native/android/app/src/main/AndroidManifest.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
android:theme="@style/AppTheme"
1212
android:usesCleartextTraffic="${usesCleartextTraffic}"
1313
android:supportsRtl="true">
14+
<meta-data
15+
android:name="BUNDLE_VERSION"
16+
android:value="${BUNDLE_VERSION}" />
1417
<activity
1518
android:name=".MainActivity"
1619
android:label="@string/app_name"

example/react-native/ios/Podfile.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2839,7 +2839,7 @@ PODS:
28392839
- ReactNativeNativeLogger
28402840
- SocketRocket
28412841
- Yoga
2842-
- ReactNativeBundleUpdate (1.1.39):
2842+
- ReactNativeBundleUpdate (1.1.40):
28432843
- boost
28442844
- DoubleConversion
28452845
- fast_float
@@ -3609,7 +3609,7 @@ SPEC CHECKSUMS:
36093609
ReactCodegen: 554b421c45b7df35ac791da1b734335470b55fcc
36103610
ReactCommon: 424cc34cf5055d69a3dcf02f3436481afb8b0f6f
36113611
ReactNativeAppUpdate: 07053378aecfdaaa23877f75c9c067fef7d58415
3612-
ReactNativeBundleUpdate: 752f40fd03fa3a6701318ab83de69f420d550053
3612+
ReactNativeBundleUpdate: 6b4bccc4975bd20ad6b2dfbca1f21da62d2b2e8d
36133613
ReactNativeCheckBiometricAuthChanged: 3fbd6bdc758ccc059ad2feaf3214b871c0ccf31e
36143614
ReactNativeDeviceUtils: cb1fa6e62e1ab4743fd9654f0124b23637ba62c5
36153615
ReactNativeGetRandomValues: 7b24b5b17f6a676724bb673d76a8f9b0871aa4c7

example/react-native/ios/example/Info.plist

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
<string>????</string>
2525
<key>CFBundleVersion</key>
2626
<string>$(CURRENT_PROJECT_VERSION)</string>
27+
<key>BUNDLE_VERSION</key>
28+
<string>$(BUNDLE_VERSION)</string>
2729
<key>LSRequiresIPhoneOS</key>
2830
<true/>
2931
<key>NSAppTransportSecurity</key>

example/react-native/pages/BundleUpdateTestPage.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,22 @@ H3bEFZ8=
497497
setUtilError(err instanceof Error ? err.message : 'Unknown error');
498498
}
499499
};
500+
const utilGetNativeBuildNumber = async () => {
501+
clearUtil();
502+
try {
503+
setUtilResult({ nativeBuildNumber: await ReactNativeBundleUpdate.getNativeBuildNumber() });
504+
} catch (err) {
505+
setUtilError(err instanceof Error ? err.message : 'Unknown error');
506+
}
507+
};
508+
const utilGetBuiltinBundleVersion = async () => {
509+
clearUtil();
510+
try {
511+
setUtilResult({ builtinBundleVersion: await ReactNativeBundleUpdate.getBuiltinBundleVersion() });
512+
} catch (err) {
513+
setUtilError(err instanceof Error ? err.message : 'Unknown error');
514+
}
515+
};
500516
const utilGetFallbackData = async () => {
501517
clearUtil();
502518
try {
@@ -691,6 +707,8 @@ H3bEFZ8=
691707
<TestButton title="Get Web Embed Path" onPress={utilGetWebEmbedPath} />
692708
<TestButton title="Get JS Bundle Path" onPress={utilGetJsBundlePath} />
693709
<TestButton title="Get Native App Version" onPress={utilGetNativeAppVersion} />
710+
<TestButton title="Get Native Build Number" onPress={utilGetNativeBuildNumber} />
711+
<TestButton title="Get Builtin Bundle Version" onPress={utilGetBuiltinBundleVersion} />
694712
<TestButton title="Get Fallback Bundle Data" onPress={utilGetFallbackData} />
695713
<TestButton title="List Local Bundles" onPress={utilListLocal} />
696714
<TestButton title="List ASC Files" onPress={utilListAscFiles} />

native-modules/react-native-bundle-update/android/src/main/java/com/margelo/nitro/reactnativebundleupdate/ReactNativeBundleUpdate.kt

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ P2Q5dClenjjjVA==
9595
object BundleUpdateStoreAndroid {
9696
private val bcProvider = BouncyCastleProvider()
9797
private const val PREFS_NAME = "BundleUpdatePrefs"
98-
private const val NATIVE_VERSION_PREFS_NAME = "NativeVersionPrefs"
98+
internal const val NATIVE_VERSION_PREFS_NAME = "NativeVersionPrefs"
9999
private const val CURRENT_BUNDLE_VERSION_KEY = "currentBundleVersion"
100100

101101
fun getDownloadBundleDir(context: Context): String {
@@ -207,6 +207,47 @@ object BundleUpdateStoreAndroid {
207207
prefs.edit().putString("nativeVersion", nativeVersion).apply()
208208
}
209209

210+
fun getBuildNumber(context: Context): String {
211+
return try {
212+
val pm = context.packageManager
213+
val pi = pm.getPackageInfo(context.packageName, 0)
214+
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) {
215+
pi.longVersionCode.toString()
216+
} else {
217+
@Suppress("DEPRECATION")
218+
pi.versionCode.toString()
219+
}
220+
} catch (e: Exception) {
221+
OneKeyLog.error("BundleUpdate", "Error getting build number: ${e.message}")
222+
""
223+
}
224+
}
225+
226+
fun getNativeBuildNumber(context: Context): String {
227+
val prefs = context.getSharedPreferences(NATIVE_VERSION_PREFS_NAME, Context.MODE_PRIVATE)
228+
return prefs.getString("nativeBuildNumber", "") ?: ""
229+
}
230+
231+
fun setNativeBuildNumber(context: Context, buildNumber: String) {
232+
val prefs = context.getSharedPreferences(NATIVE_VERSION_PREFS_NAME, Context.MODE_PRIVATE)
233+
prefs.edit().putString("nativeBuildNumber", buildNumber).apply()
234+
}
235+
236+
fun clearNativeVersionPrefs(context: Context) {
237+
val prefs = context.getSharedPreferences(NATIVE_VERSION_PREFS_NAME, Context.MODE_PRIVATE)
238+
prefs.edit().remove("nativeVersion").remove("nativeBuildNumber").apply()
239+
}
240+
241+
fun getBuiltinBundleVersion(context: Context): String {
242+
return try {
243+
val appInfo = context.packageManager.getApplicationInfo(context.packageName, android.content.pm.PackageManager.GET_META_DATA)
244+
appInfo.metaData?.getString("BUNDLE_VERSION") ?: ""
245+
} catch (e: Exception) {
246+
OneKeyLog.error("BundleUpdate", "Error getting builtin bundle version: ${e.message}")
247+
""
248+
}
249+
}
250+
210251
fun calculateSHA256(filePath: String): String? {
211252
return try {
212253
val digest = MessageDigest.getInstance("SHA-256")
@@ -513,6 +554,16 @@ object BundleUpdateStoreAndroid {
513554
if (currentAppVersion != prevNativeVersion) {
514555
OneKeyLog.info("BundleUpdate", "currentAppVersion is not equal to prevNativeVersion $currentAppVersion $prevNativeVersion")
515556
clearUpdateBundleData(context)
557+
clearNativeVersionPrefs(context)
558+
return null
559+
}
560+
561+
val currentBuildNumber = getBuildNumber(context)
562+
val prevBuildNumber = getNativeBuildNumber(context)
563+
if (currentBuildNumber.isNotEmpty() && prevBuildNumber.isNotEmpty() && currentBuildNumber != prevBuildNumber) {
564+
OneKeyLog.info("BundleUpdate", "buildNumber changed from $prevBuildNumber to $currentBuildNumber, clearing bundle data")
565+
clearUpdateBundleData(context)
566+
clearNativeVersionPrefs(context)
516567
return null
517568
}
518569

@@ -1008,6 +1059,8 @@ class ReactNativeBundleUpdate : HybridReactNativeBundleUpdateSpec() {
10081059
BundleUpdateStoreAndroid.setCurrentBundleVersionAndSignature(context, folderName, signature)
10091060
val nativeVersion = BundleUpdateStoreAndroid.getAppVersion(context) ?: ""
10101061
BundleUpdateStoreAndroid.setNativeVersion(context, nativeVersion)
1062+
val buildNumber = BundleUpdateStoreAndroid.getBuildNumber(context)
1063+
BundleUpdateStoreAndroid.setNativeBuildNumber(context, buildNumber)
10111064

10121065
// Manage fallback data
10131066
try {
@@ -1097,6 +1150,7 @@ class ReactNativeBundleUpdate : HybridReactNativeBundleUpdateSpec() {
10971150
OneKeyLog.info("BundleUpdate", "clearAllJSBundleData: deleted bundle dir")
10981151
}
10991152
BundleUpdateStoreAndroid.clearUpdateBundleData(context)
1153+
BundleUpdateStoreAndroid.clearNativeVersionPrefs(context)
11001154

11011155
OneKeyLog.info("BundleUpdate", "clearAllJSBundleData: completed successfully")
11021156
TestResult(success = true, message = "Successfully cleared all JS bundle data")
@@ -1197,6 +1251,24 @@ class ReactNativeBundleUpdate : HybridReactNativeBundleUpdateSpec() {
11971251
}
11981252
}
11991253

1254+
override fun getNativeBuildNumber(): Promise<String> {
1255+
return Promise.async {
1256+
val context = getContext()
1257+
val buildNumber = BundleUpdateStoreAndroid.getBuildNumber(context)
1258+
OneKeyLog.info("BundleUpdate", "getNativeBuildNumber: $buildNumber")
1259+
buildNumber
1260+
}
1261+
}
1262+
1263+
override fun getBuiltinBundleVersion(): Promise<String> {
1264+
return Promise.async {
1265+
val context = getContext()
1266+
val version = BundleUpdateStoreAndroid.getBuiltinBundleVersion(context)
1267+
OneKeyLog.info("BundleUpdate", "getBuiltinBundleVersion: $version")
1268+
version
1269+
}
1270+
}
1271+
12001272
override fun testVerification(): Promise<Boolean> {
12011273
return Promise.async {
12021274
val testSignature = """-----BEGIN PGP SIGNED MESSAGE-----

native-modules/react-native-bundle-update/ios/ReactNativeBundleUpdate.swift

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ P2Q5dClenjjjVA==
6767
public class BundleUpdateStore: NSObject {
6868
private static let bundlePrefsKey = "currentBundleVersion"
6969
private static let nativeVersionKey = "nativeVersion"
70+
private static let nativeBuildNumberKey = "nativeBuildNumber"
7071

7172
public static func documentDirectory() -> String {
7273
NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
@@ -178,6 +179,29 @@ public class BundleUpdateStore: NSObject {
178179
UserDefaults.standard.synchronize()
179180
}
180181

182+
public static func getCurrentNativeBuildNumber() -> String {
183+
Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? ""
184+
}
185+
186+
public static func getNativeBuildNumber() -> String? {
187+
UserDefaults.standard.string(forKey: nativeBuildNumberKey)
188+
}
189+
190+
public static func setNativeBuildNumber(_ buildNumber: String) {
191+
UserDefaults.standard.set(buildNumber, forKey: nativeBuildNumberKey)
192+
UserDefaults.standard.synchronize()
193+
}
194+
195+
public static func clearNativeVersionPrefs() {
196+
let ud = UserDefaults.standard
197+
ud.removeObject(forKey: nativeVersionKey)
198+
ud.removeObject(forKey: nativeBuildNumberKey)
199+
}
200+
201+
public static func getBuiltinBundleVersion() -> String {
202+
Bundle.main.infoDictionary?["BUNDLE_VERSION"] as? String ?? ""
203+
}
204+
181205
public static func getMetadataFilePath(_ currentBundleVersion: String) -> String? {
182206
let path = (bundleDir() as NSString)
183207
.appendingPathComponent(currentBundleVersion)
@@ -402,6 +426,21 @@ public class BundleUpdateStore: NSObject {
402426
deleteSignatureFile(cbv)
403427
ud.removeObject(forKey: "currentBundleVersion")
404428
}
429+
clearNativeVersionPrefs()
430+
ud.synchronize()
431+
return nil
432+
}
433+
434+
let currentBuildNumber = getCurrentNativeBuildNumber()
435+
let prevBuildNumber = getNativeBuildNumber()
436+
if !currentBuildNumber.isEmpty && prevBuildNumber != nil && currentBuildNumber != prevBuildNumber {
437+
OneKeyLog.info("BundleUpdate", "buildNumber changed from \(prevBuildNumber ?? "") to \(currentBuildNumber), clearing bundle data")
438+
let ud = UserDefaults.standard
439+
if let cbv = ud.string(forKey: "currentBundleVersion") {
440+
deleteSignatureFile(cbv)
441+
ud.removeObject(forKey: "currentBundleVersion")
442+
}
443+
clearNativeVersionPrefs()
405444
ud.synchronize()
406445
return nil
407446
}
@@ -972,6 +1011,8 @@ class ReactNativeBundleUpdate: HybridReactNativeBundleUpdateSpec {
9721011
}
9731012
let currentNativeVersion = BundleUpdateStore.getCurrentNativeVersion()
9741013
BundleUpdateStore.setNativeVersion(currentNativeVersion)
1014+
let currentBuildNumber = BundleUpdateStore.getCurrentNativeBuildNumber()
1015+
BundleUpdateStore.setNativeBuildNumber(currentBuildNumber)
9751016
ud.synchronize()
9761017

9771018
// Manage fallback data
@@ -1059,7 +1100,7 @@ class ReactNativeBundleUpdate: HybridReactNativeBundleUpdateSpec {
10591100
ud.removeObject(forKey: "currentBundleVersion")
10601101
OneKeyLog.info("BundleUpdate", "clearAllJSBundleData: removed currentBundleVersion=\(cbv)")
10611102
}
1062-
ud.removeObject(forKey: "nativeVersion")
1103+
BundleUpdateStore.clearNativeVersionPrefs()
10631104
ud.synchronize()
10641105

10651106
OneKeyLog.info("BundleUpdate", "clearAllJSBundleData: completed successfully")
@@ -1157,6 +1198,22 @@ class ReactNativeBundleUpdate: HybridReactNativeBundleUpdateSpec {
11571198
}
11581199
}
11591200

1201+
func getNativeBuildNumber() throws -> Promise<String> {
1202+
return Promise.async {
1203+
let buildNumber = BundleUpdateStore.getCurrentNativeBuildNumber()
1204+
OneKeyLog.info("BundleUpdate", "getNativeBuildNumber: \(buildNumber)")
1205+
return buildNumber
1206+
}
1207+
}
1208+
1209+
func getBuiltinBundleVersion() throws -> Promise<String> {
1210+
return Promise.async {
1211+
let version = BundleUpdateStore.getBuiltinBundleVersion()
1212+
OneKeyLog.info("BundleUpdate", "getBuiltinBundleVersion: \(version)")
1213+
return version
1214+
}
1215+
}
1216+
11601217
func testVerification() throws -> Promise<Bool> {
11611218
return Promise.async {
11621219
let testSignature = """

native-modules/react-native-bundle-update/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@onekeyfe/react-native-bundle-update",
3-
"version": "1.1.39",
3+
"version": "1.1.40",
44
"description": "react-native-bundle-update",
55
"main": "./lib/module/index.js",
66
"types": "./lib/typescript/src/index.d.ts",

native-modules/react-native-bundle-update/src/ReactNativeBundleUpdate.nitro.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ export interface ReactNativeBundleUpdate
109109
getJsBundlePath(): string;
110110
getJsBundlePathAsync(): Promise<string>;
111111
getNativeAppVersion(): Promise<string>;
112+
getNativeBuildNumber(): Promise<string>;
113+
getBuiltinBundleVersion(): Promise<string>;
112114

113115
// Verification & testing
114116
testVerification(): Promise<boolean>;

0 commit comments

Comments
 (0)