@otaupdate/react-native 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -22,6 +22,26 @@ rollback-on-failure; JS handles the update check and the app-facing API.
|
|
|
22
22
|
npm install @otaupdate/react-native
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
+
> **Bare React Native on Android — pin your version to match your RN version.**
|
|
26
|
+
> `npm install` with no version pin gets you the latest (currently 1.0.5).
|
|
27
|
+
> That version relies on an internal React Native API whose shape changed
|
|
28
|
+
> between RN releases — it requires **RN 0.80+**. If your app is on an
|
|
29
|
+
> **older RN version (0.7x)**, pin `@otaupdate/react-native@1.0.4` instead:
|
|
30
|
+
>
|
|
31
|
+
> ```bash
|
|
32
|
+
> npm install @otaupdate/react-native@1.0.4 # RN 0.7x
|
|
33
|
+
> npm install @otaupdate/react-native@1.0.5 # RN 0.80+ (or just @otaupdate/react-native)
|
|
34
|
+
> ```
|
|
35
|
+
>
|
|
36
|
+
> Both versions are fully functional and behave identically from the JS API
|
|
37
|
+
> you call (`sync`, `checkForUpdate`, `withOtaUpdate`, etc. — none of that
|
|
38
|
+
> changes). The only difference is *how* Android applies a mandatory/resume
|
|
39
|
+
> install: 1.0.4 restarts the process (a brief visible flash during the
|
|
40
|
+
> switch); 1.0.5 swaps the bundle in place with no restart and no flash, the
|
|
41
|
+
> same way Expo's own update mechanism does, but that technique only exists
|
|
42
|
+
> on newer RN. Expo and iOS are unaffected by any of this — both already
|
|
43
|
+
> update seamlessly regardless of which SDK version you're on.
|
|
44
|
+
|
|
25
45
|
### Bare React Native
|
|
26
46
|
|
|
27
47
|
**iOS**
|
|
@@ -320,3 +340,5 @@ compatible with. Devices on other binary versions never receive it.
|
|
|
320
340
|
| `no_matching_binary_version` | The release's `targetBinaryVersion` does not cover the installed native version. |
|
|
321
341
|
| Works in debug, not release | Debug builds load from Metro; the OTA path only runs in release builds. |
|
|
322
342
|
| Android: mandatory update reload-loops (bare RN) | Fixed in 1.0.2. Before that, `IMMEDIATE`/`ON_NEXT_RESUME` tried an in-place JS reload that reused a bundle path fixed at process start, so the same mandatory update kept re-triggering forever. 1.0.2 does a real process restart instead — update to it. |
|
|
343
|
+
| A newer update never shows up, app stays on an older-than-expected release | Fixed in 1.0.3. `notifyApplicationReady()` used to clear *any* pending hash unconditionally — if a second update was downloaded and queued for a future restart while the first one was still being confirmed, its pending state was silently wiped and the device never advanced to it. |
|
|
344
|
+
| Android: old screen briefly visible during a mandatory/resume install | Improved in 1.0.3. The process-restart fix in 1.0.2 is correct but showed Android's default activity-transition animation (old screen sliding/fading out). 1.0.3 suppresses it via `overridePendingTransition(0, 0)` so the switch is instant. |
|
|
@@ -246,7 +246,25 @@ class OtaUpdateModule(private val reactContext: ReactApplicationContext) :
|
|
|
246
246
|
try {
|
|
247
247
|
val launchIntent = context.packageManager.getLaunchIntentForPackage(context.packageName)
|
|
248
248
|
?: throw IllegalStateException("no launch intent for ${context.packageName}")
|
|
249
|
-
|
|
249
|
+
val restartIntent = Intent.makeRestartActivityTask(launchIntent.component)
|
|
250
|
+
|
|
251
|
+
// Starting the restart from the current Activity (when one is attached)
|
|
252
|
+
// instead of the bare application context lets us suppress the default
|
|
253
|
+
// activity-transition animation right after — otherwise Android
|
|
254
|
+
// animates the old screen sliding/fading out while the new one comes
|
|
255
|
+
// in, so the old content stays visibly on screen for the transition's
|
|
256
|
+
// duration. overridePendingTransition(0, 0) makes the switch instant
|
|
257
|
+
// instead. Falls back to starting from the application context if no
|
|
258
|
+
// activity is currently attached — the restart still works, just with
|
|
259
|
+
// the default (visible) transition.
|
|
260
|
+
val activity = reactContext.currentActivity
|
|
261
|
+
if (activity != null) {
|
|
262
|
+
activity.startActivity(restartIntent)
|
|
263
|
+
@Suppress("DEPRECATION")
|
|
264
|
+
activity.overridePendingTransition(0, 0)
|
|
265
|
+
} else {
|
|
266
|
+
context.startActivity(restartIntent)
|
|
267
|
+
}
|
|
250
268
|
Runtime.getRuntime().exit(0)
|
|
251
269
|
} catch (e: Exception) {
|
|
252
270
|
Log.e(OtaUpdateStore.TAG, "failed to restart the app process", e)
|
|
@@ -147,10 +147,21 @@ class OtaUpdateStore(private val context: Context) {
|
|
|
147
147
|
return true
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
-
/**
|
|
150
|
+
/**
|
|
151
|
+
* Confirms the running bundle. After this it can never be rolled back.
|
|
152
|
+
*
|
|
153
|
+
* Only confirms when `pendingIsLoading` is true — meaning this session
|
|
154
|
+
* actually booted into `pendingHash` via `promotePendingIfAny()`. A
|
|
155
|
+
* `pendingHash` set by `markPending()` describes a *different*, newer
|
|
156
|
+
* package downloaded and queued for a *future* restart, not something
|
|
157
|
+
* running right now. Clearing it here — as an earlier version of this
|
|
158
|
+
* method did unconditionally — silently discarded that queued update: the
|
|
159
|
+
* next restart would find nothing pending and keep loading the older
|
|
160
|
+
* package, i.e. "the newly downloaded update never shows up."
|
|
161
|
+
*/
|
|
151
162
|
@Synchronized
|
|
152
163
|
fun notifyApplicationReady() {
|
|
153
|
-
if (pendingHash == null) return
|
|
164
|
+
if (pendingHash == null || !pendingIsLoading) return
|
|
154
165
|
Log.i(TAG, "update ${pendingHash} confirmed healthy")
|
|
155
166
|
lastConfirmedHash = currentHash
|
|
156
167
|
pendingHash = null
|
package/ios/OtaUpdateStore.m
CHANGED
|
@@ -143,9 +143,19 @@ static NSString *const kLogTag = @"[OtaUpdate]";
|
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Confirms the running bundle. After this it can never be rolled back.
|
|
148
|
+
*
|
|
149
|
+
* Only confirms when pendingIsLoading is YES — meaning this session actually
|
|
150
|
+
* booted into pendingHash via promotePendingIfAny. A pendingHash set by
|
|
151
|
+
* markPending: describes a *different*, newer package downloaded and queued
|
|
152
|
+
* for a *future* restart, not something running right now. Clearing it here
|
|
153
|
+
* unconditionally (as an earlier version of this method did) silently
|
|
154
|
+
* discarded that queued update.
|
|
155
|
+
*/
|
|
146
156
|
- (void)notifyApplicationReady {
|
|
147
157
|
@synchronized(self) {
|
|
148
|
-
if (!self.pendingHash) return;
|
|
158
|
+
if (!self.pendingHash || !self.pendingIsLoading) return;
|
|
149
159
|
NSLog(@"%@ update %@ confirmed healthy", kLogTag, self.pendingHash);
|
|
150
160
|
self.lastConfirmedHash = self.currentHash;
|
|
151
161
|
self.pendingHash = nil;
|