native-update 1.0.4 → 1.0.6
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 +3 -3
- package/android/build.gradle +20 -15
- package/docs/APP_REVIEW_GUIDE.md +1 -1
- package/docs/BUNDLE_SIGNING.md +10 -4
- package/docs/LIVE_UPDATES_GUIDE.md +127 -102
- package/docs/MIGRATION.md +1 -1
- package/docs/NATIVE_UPDATES_GUIDE.md +9 -8
- package/docs/QUICK_START.md +23 -23
- package/docs/api/app-review-api.md +1 -21
- package/docs/api/live-update-api.md +36 -28
- package/docs/examples/advanced-scenarios.md +25 -11
- package/docs/examples/basic-usage.md +1 -0
- package/docs/features/app-reviews.md +1 -1
- package/docs/features/app-updates.md +1 -1
- package/docs/features/live-updates.md +23 -14
- package/docs/getting-started/configuration.md +1 -1
- package/docs/getting-started/installation.md +2 -2
- package/docs/getting-started/quick-start.md +3 -3
- package/docs/guides/migration-from-codepush.md +1 -1
- package/docs/guides/security-best-practices.md +3 -3
- package/docs/guides/testing-guide.md +2 -2
- package/package.json +1 -1
package/Readme.md
CHANGED
|
@@ -191,14 +191,14 @@ export class AppComponent implements OnInit {
|
|
|
191
191
|
|
|
192
192
|
async checkAllUpdates() {
|
|
193
193
|
// 1. Check live updates first (fastest)
|
|
194
|
-
const liveUpdate = await NativeUpdate.
|
|
195
|
-
if (liveUpdate.
|
|
194
|
+
const liveUpdate = await NativeUpdate.sync();
|
|
195
|
+
if (liveUpdate.status === 'UPDATE_AVAILABLE' || liveUpdate.status === 'UPDATE_INSTALLED') {
|
|
196
196
|
await this.promptLiveUpdate(liveUpdate);
|
|
197
197
|
return; // Don't check native if live update is available
|
|
198
198
|
}
|
|
199
199
|
|
|
200
200
|
// 2. Check native updates
|
|
201
|
-
const nativeUpdate = await NativeUpdate.
|
|
201
|
+
const nativeUpdate = await NativeUpdate.getAppUpdateInfo();
|
|
202
202
|
if (nativeUpdate.updateAvailable) {
|
|
203
203
|
await this.promptNativeUpdate(nativeUpdate);
|
|
204
204
|
}
|
package/android/build.gradle
CHANGED
|
@@ -1,17 +1,7 @@
|
|
|
1
|
-
ext {
|
|
2
|
-
compileSdkVersion = 34
|
|
3
|
-
targetSdkVersion = 34
|
|
4
|
-
minSdkVersion = 21
|
|
5
|
-
androidxAppCompatVersion = '1.6.1'
|
|
6
|
-
androidxCoreVersion = '1.13.1'
|
|
7
|
-
androidxWorkVersion = '2.9.0'
|
|
8
|
-
playServicesVersion = '18.2.0'
|
|
9
|
-
playReviewVersion = '2.0.1'
|
|
10
|
-
okhttpVersion = '4.12.0'
|
|
11
|
-
kotlinVersion = '2.0.21'
|
|
12
|
-
}
|
|
13
|
-
|
|
14
1
|
buildscript {
|
|
2
|
+
ext {
|
|
3
|
+
kotlinVersion = '1.9.22'
|
|
4
|
+
}
|
|
15
5
|
repositories {
|
|
16
6
|
google()
|
|
17
7
|
mavenCentral()
|
|
@@ -22,6 +12,18 @@ buildscript {
|
|
|
22
12
|
}
|
|
23
13
|
}
|
|
24
14
|
|
|
15
|
+
ext {
|
|
16
|
+
compileSdkVersion = 34
|
|
17
|
+
targetSdkVersion = 34
|
|
18
|
+
minSdkVersion = 21
|
|
19
|
+
androidxAppCompatVersion = '1.6.1'
|
|
20
|
+
androidxCoreVersion = '1.13.1'
|
|
21
|
+
androidxWorkVersion = '2.9.0'
|
|
22
|
+
playAppUpdateVersion = '2.1.0'
|
|
23
|
+
playReviewVersion = '2.0.1'
|
|
24
|
+
okhttpVersion = '4.12.0'
|
|
25
|
+
}
|
|
26
|
+
|
|
25
27
|
apply plugin: 'com.android.library'
|
|
26
28
|
apply plugin: 'kotlin-android'
|
|
27
29
|
|
|
@@ -67,14 +69,17 @@ dependencies {
|
|
|
67
69
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
|
68
70
|
implementation project(':capacitor-android')
|
|
69
71
|
|
|
72
|
+
// Kotlin
|
|
73
|
+
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
|
|
74
|
+
|
|
70
75
|
// AndroidX
|
|
71
76
|
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
|
|
72
77
|
implementation "androidx.core:core-ktx:$androidxCoreVersion"
|
|
73
78
|
implementation "androidx.work:work-runtime-ktx:$androidxWorkVersion"
|
|
74
79
|
|
|
75
80
|
// Google Play Services
|
|
76
|
-
implementation "com.google.android.play:app-update:$
|
|
77
|
-
implementation "com.google.android.play:app-update-ktx:$
|
|
81
|
+
implementation "com.google.android.play:app-update:$playAppUpdateVersion"
|
|
82
|
+
implementation "com.google.android.play:app-update-ktx:$playAppUpdateVersion"
|
|
78
83
|
implementation "com.google.android.play:review:$playReviewVersion"
|
|
79
84
|
implementation "com.google.android.play:review-ktx:$playReviewVersion"
|
|
80
85
|
|
package/docs/APP_REVIEW_GUIDE.md
CHANGED
|
@@ -765,4 +765,4 @@ Key takeaways for implementing app reviews:
|
|
|
765
765
|
|
|
766
766
|
- Review the [Quick Start Guide](./QUICK_START.md)
|
|
767
767
|
- Check the [API Reference](./api/app-review-api.md)
|
|
768
|
-
- See
|
|
768
|
+
- See example implementation in the `/example` directory
|
package/docs/BUNDLE_SIGNING.md
CHANGED
|
@@ -240,12 +240,18 @@ Response: {
|
|
|
240
240
|
### Plugin Methods
|
|
241
241
|
|
|
242
242
|
```typescript
|
|
243
|
-
//
|
|
244
|
-
|
|
243
|
+
// Signature verification is handled automatically during sync/download
|
|
244
|
+
// Use validateUpdate for manual verification
|
|
245
|
+
const result = await NativeUpdate.LiveUpdate.validateUpdate({
|
|
245
246
|
bundlePath: '/path/to/bundle.zip',
|
|
246
|
-
|
|
247
|
-
|
|
247
|
+
checksum: 'expected-sha256-checksum',
|
|
248
|
+
signature: 'base64-signature' // Optional
|
|
248
249
|
});
|
|
250
|
+
|
|
251
|
+
console.log('Validation result:', result.isValid);
|
|
252
|
+
if (!result.isValid) {
|
|
253
|
+
console.log('Validation details:', result.details);
|
|
254
|
+
}
|
|
249
255
|
```
|
|
250
256
|
|
|
251
257
|
## Compliance
|
|
@@ -168,41 +168,41 @@ export class UpdateService {
|
|
|
168
168
|
|
|
169
169
|
async checkForUpdates(silent = false) {
|
|
170
170
|
try {
|
|
171
|
-
const
|
|
172
|
-
await NativeUpdate.sync();
|
|
171
|
+
const result = await NativeUpdate.sync();
|
|
173
172
|
|
|
174
|
-
if (
|
|
173
|
+
if (result.status === 'UP_TO_DATE') {
|
|
175
174
|
if (!silent) {
|
|
176
175
|
await this.showAlert('No Updates', 'Your app is up to date!');
|
|
177
176
|
}
|
|
178
177
|
return;
|
|
179
178
|
}
|
|
180
179
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
180
|
+
if (result.status === 'UPDATE_AVAILABLE' || result.status === 'UPDATE_INSTALLED') {
|
|
181
|
+
// Show update dialog
|
|
182
|
+
const alert = await this.alertCtrl.create({
|
|
183
|
+
header: 'Update Available',
|
|
184
|
+
message: `Version ${result.version} is available.\n\n${result.description || 'Bug fixes and improvements'}`,
|
|
185
|
+
buttons: [
|
|
186
|
+
{
|
|
187
|
+
text: result.mandatory ? 'Update Now' : 'Later',
|
|
188
|
+
role: 'cancel',
|
|
189
|
+
handler: () => {
|
|
190
|
+
if (result.mandatory) {
|
|
191
|
+
// Force update for mandatory updates
|
|
192
|
+
this.downloadAndApplyUpdate();
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
},
|
|
195
196
|
},
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
197
|
+
{
|
|
198
|
+
text: 'Update',
|
|
199
|
+
handler: () => {
|
|
200
|
+
this.downloadAndApplyUpdate();
|
|
201
|
+
},
|
|
201
202
|
},
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
});
|
|
203
|
+
],
|
|
204
|
+
backdropDismiss: !result.mandatory,
|
|
205
|
+
});
|
|
206
206
|
|
|
207
207
|
await alert.present();
|
|
208
208
|
} catch (error) {
|
|
@@ -220,11 +220,14 @@ export class UpdateService {
|
|
|
220
220
|
|
|
221
221
|
try {
|
|
222
222
|
// Download with progress
|
|
223
|
-
|
|
224
|
-
|
|
223
|
+
// The sync method already handles downloading
|
|
224
|
+
// Progress tracking would be done via event listeners
|
|
225
|
+
const downloadListener = NativeUpdate.addListener(
|
|
226
|
+
'downloadProgress',
|
|
227
|
+
(progress) => {
|
|
225
228
|
loading.message = `Downloading... ${Math.round(progress.percent)}%`;
|
|
226
|
-
}
|
|
227
|
-
|
|
229
|
+
}
|
|
230
|
+
);
|
|
228
231
|
|
|
229
232
|
loading.message = 'Applying update...';
|
|
230
233
|
|
|
@@ -291,31 +294,25 @@ export class AppComponent implements OnInit {
|
|
|
291
294
|
export class UpdateStrategies {
|
|
292
295
|
// Immediate update (default)
|
|
293
296
|
async immediateUpdate() {
|
|
294
|
-
const
|
|
295
|
-
if (
|
|
296
|
-
await NativeUpdate.download();
|
|
297
|
+
const result = await NativeUpdate.sync();
|
|
298
|
+
if (result.status === 'UPDATE_INSTALLED') {
|
|
297
299
|
await NativeUpdate.reload(); // Restarts immediately
|
|
298
300
|
}
|
|
299
301
|
}
|
|
300
302
|
|
|
301
303
|
// Update on next restart
|
|
302
304
|
async updateOnRestart() {
|
|
303
|
-
const
|
|
304
|
-
if (
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
reloadStrategy: 'on-next-restart',
|
|
308
|
-
});
|
|
309
|
-
// Update will be applied next time app starts
|
|
305
|
+
const result = await NativeUpdate.sync();
|
|
306
|
+
if (result.status === 'UPDATE_INSTALLED') {
|
|
307
|
+
// Update is already installed, just don't reload immediately
|
|
308
|
+
// Update will be applied on next app restart
|
|
310
309
|
}
|
|
311
310
|
}
|
|
312
311
|
|
|
313
312
|
// Update with confirmation
|
|
314
313
|
async updateWithConfirmation() {
|
|
315
|
-
const
|
|
316
|
-
if (
|
|
317
|
-
await NativeUpdate.download();
|
|
318
|
-
|
|
314
|
+
const result = await NativeUpdate.sync();
|
|
315
|
+
if (result.status === 'UPDATE_INSTALLED') {
|
|
319
316
|
// Show confirmation dialog
|
|
320
317
|
const confirmed = await this.showUpdateReadyDialog();
|
|
321
318
|
if (confirmed) {
|
|
@@ -429,45 +426,51 @@ await NativeUpdate.setChannel({ channel: 'beta' });
|
|
|
429
426
|
### 2. Delta Updates
|
|
430
427
|
|
|
431
428
|
```typescript
|
|
432
|
-
//
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
threshold: 0.3, // Use delta if size < 30% of full bundle
|
|
436
|
-
});
|
|
429
|
+
// Delta updates would be handled server-side
|
|
430
|
+
// The sync() method will automatically use delta updates if available
|
|
431
|
+
// Configure your server to provide delta updates when appropriate
|
|
437
432
|
```
|
|
438
433
|
|
|
439
434
|
### 3. Rollback Support
|
|
440
435
|
|
|
441
436
|
```typescript
|
|
442
|
-
// List
|
|
443
|
-
const
|
|
437
|
+
// List all downloaded bundles
|
|
438
|
+
const bundles = await NativeUpdate.list();
|
|
444
439
|
|
|
445
|
-
// Rollback to
|
|
446
|
-
|
|
447
|
-
await NativeUpdate.reset();
|
|
448
|
-
}
|
|
440
|
+
// Rollback to original app bundle
|
|
441
|
+
await NativeUpdate.reset();
|
|
449
442
|
|
|
450
|
-
// Rollback to specific
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
443
|
+
// Rollback to a specific bundle
|
|
444
|
+
if (bundles.length > 1) {
|
|
445
|
+
const previousBundle = bundles[bundles.length - 2];
|
|
446
|
+
await NativeUpdate.set(previousBundle);
|
|
447
|
+
await NativeUpdate.reload();
|
|
448
|
+
}
|
|
454
449
|
```
|
|
455
450
|
|
|
456
451
|
### 4. Update Metrics
|
|
457
452
|
|
|
458
453
|
```typescript
|
|
459
|
-
//
|
|
460
|
-
|
|
461
|
-
version:
|
|
462
|
-
|
|
463
|
-
|
|
454
|
+
// Implement your own metrics tracking
|
|
455
|
+
class UpdateMetrics {
|
|
456
|
+
async trackSuccess(version: string, duration: number) {
|
|
457
|
+
// Send to your analytics service
|
|
458
|
+
await analytics.track('update_success', {
|
|
459
|
+
version,
|
|
460
|
+
duration,
|
|
461
|
+
timestamp: Date.now()
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
464
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
465
|
+
async trackFailure(version: string, error: string) {
|
|
466
|
+
// Send to your analytics service
|
|
467
|
+
await analytics.track('update_failure', {
|
|
468
|
+
version,
|
|
469
|
+
error,
|
|
470
|
+
timestamp: Date.now()
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
}
|
|
471
474
|
```
|
|
472
475
|
|
|
473
476
|
### 5. Custom Update UI
|
|
@@ -484,7 +487,7 @@ class CustomUpdateUI {
|
|
|
484
487
|
// Custom check
|
|
485
488
|
const update = await NativeUpdate.sync();
|
|
486
489
|
|
|
487
|
-
if (update.
|
|
490
|
+
if (update.status === 'UPDATE_AVAILABLE' || update.status === 'UPDATE_INSTALLED') {
|
|
488
491
|
// Show custom UI
|
|
489
492
|
const modal = await this.modalCtrl.create({
|
|
490
493
|
component: UpdateModalComponent,
|
|
@@ -561,15 +564,13 @@ await NativeUpdate.setChannel({
|
|
|
561
564
|
// Download during off-peak hours
|
|
562
565
|
const now = new Date().getHours();
|
|
563
566
|
if (now >= 2 && now <= 6) {
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
});
|
|
567
|
+
// Download priority would be configured during sync
|
|
568
|
+
await NativeUpdate.sync();
|
|
567
569
|
}
|
|
568
570
|
|
|
569
|
-
//
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
await NativeUpdate.resumeDownload({ id: downloadId });
|
|
571
|
+
// Download management would be handled by the sync() method
|
|
572
|
+
// For custom download control, implement your own download manager
|
|
573
|
+
// that uses the download() method with proper retry logic
|
|
573
574
|
```
|
|
574
575
|
|
|
575
576
|
## Troubleshooting
|
|
@@ -579,42 +580,56 @@ await NativeUpdate.resumeDownload({ id: downloadId });
|
|
|
579
580
|
1. **Update not applying**
|
|
580
581
|
|
|
581
582
|
```typescript
|
|
582
|
-
// Check
|
|
583
|
-
const
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
583
|
+
// Check current bundle status
|
|
584
|
+
const current = await NativeUpdate.current();
|
|
585
|
+
console.log('Current bundle:', current);
|
|
586
|
+
|
|
587
|
+
// List all bundles to see if update was downloaded
|
|
588
|
+
const bundles = await NativeUpdate.list();
|
|
589
|
+
console.log('Available bundles:', bundles);
|
|
590
|
+
|
|
591
|
+
// If update bundle exists but not active, set it
|
|
592
|
+
const updateBundle = bundles.find(b => b.version === 'new-version');
|
|
593
|
+
if (updateBundle && updateBundle.bundleId !== current.bundleId) {
|
|
594
|
+
await NativeUpdate.set(updateBundle);
|
|
595
|
+
await NativeUpdate.reload();
|
|
587
596
|
}
|
|
588
597
|
```
|
|
589
598
|
|
|
590
599
|
2. **Signature verification fails**
|
|
591
600
|
|
|
592
601
|
```typescript
|
|
593
|
-
//
|
|
594
|
-
|
|
595
|
-
console.log('
|
|
602
|
+
// Check current configuration by examining the plugin setup
|
|
603
|
+
// Configuration is set during plugin initialization
|
|
604
|
+
console.log('Verify your capacitor.config.json for publicKey setting');
|
|
596
605
|
```
|
|
597
606
|
|
|
598
607
|
3. **Storage issues**
|
|
599
608
|
```typescript
|
|
600
|
-
//
|
|
601
|
-
await NativeUpdate.
|
|
602
|
-
keepVersions:
|
|
609
|
+
// Delete old bundles, keeping only the latest N versions
|
|
610
|
+
await NativeUpdate.delete({
|
|
611
|
+
keepVersions: 2 // Keep only 2 most recent versions
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
// Or delete bundles older than a certain date
|
|
615
|
+
const thirtyDaysAgo = Date.now() - (30 * 24 * 60 * 60 * 1000);
|
|
616
|
+
await NativeUpdate.delete({
|
|
617
|
+
olderThan: thirtyDaysAgo
|
|
603
618
|
});
|
|
604
619
|
```
|
|
605
620
|
|
|
606
621
|
### Debug Mode
|
|
607
622
|
|
|
608
623
|
```typescript
|
|
609
|
-
// Enable debug logging
|
|
610
|
-
|
|
624
|
+
// Enable debug logging through your app's logging system
|
|
625
|
+
// The plugin will log errors and important events automatically
|
|
611
626
|
|
|
612
627
|
// Monitor update events
|
|
613
|
-
NativeUpdate.addListener('
|
|
628
|
+
NativeUpdate.addListener('downloadProgress', (progress) => {
|
|
614
629
|
console.log('Download progress:', progress);
|
|
615
630
|
});
|
|
616
631
|
|
|
617
|
-
NativeUpdate.addListener('
|
|
632
|
+
NativeUpdate.addListener('updateStateChanged', (state) => {
|
|
618
633
|
console.log('Update state:', state);
|
|
619
634
|
});
|
|
620
635
|
```
|
|
@@ -622,15 +637,25 @@ NativeUpdate.addListener('updateStateChange', (state) => {
|
|
|
622
637
|
### Health Checks
|
|
623
638
|
|
|
624
639
|
```typescript
|
|
625
|
-
//
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
640
|
+
// Implement your own health check system
|
|
641
|
+
class UpdateHealthCheck {
|
|
642
|
+
async checkHealth() {
|
|
643
|
+
const current = await NativeUpdate.current();
|
|
644
|
+
const bundles = await NativeUpdate.list();
|
|
645
|
+
|
|
646
|
+
const health = {
|
|
647
|
+
currentVersion: current.version,
|
|
648
|
+
currentBundleId: current.bundleId,
|
|
649
|
+
bundleStatus: current.status,
|
|
650
|
+
totalBundles: bundles.length,
|
|
651
|
+
lastUpdateTime: current.downloadTime,
|
|
652
|
+
isVerified: current.verified
|
|
653
|
+
};
|
|
654
|
+
|
|
655
|
+
console.log('Update system health:', health);
|
|
656
|
+
return health;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
634
659
|
```
|
|
635
660
|
|
|
636
661
|
## Security Considerations
|
package/docs/MIGRATION.md
CHANGED
|
@@ -187,6 +187,6 @@ await NativeUpdate.configure(config);
|
|
|
187
187
|
|
|
188
188
|
### Getting Help
|
|
189
189
|
|
|
190
|
-
- Check our
|
|
190
|
+
- Check our example app in the `/example` directory for implementation patterns
|
|
191
191
|
- Review the [API documentation](./api/live-update-api.md)
|
|
192
192
|
- File issues on GitHub for migration problems
|
|
@@ -201,10 +201,12 @@ export class AndroidFlexibleUpdate {
|
|
|
201
201
|
);
|
|
202
202
|
|
|
203
203
|
// Listen for download completion
|
|
204
|
-
NativeUpdate.addListener('
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
204
|
+
NativeUpdate.addListener('updateStateChanged', (event) => {
|
|
205
|
+
if (event.status === 'DOWNLOADED') {
|
|
206
|
+
console.log('Update downloaded');
|
|
207
|
+
this.updateDownloaded = true;
|
|
208
|
+
this.showInstallPrompt();
|
|
209
|
+
}
|
|
208
210
|
});
|
|
209
211
|
} catch (error) {
|
|
210
212
|
console.error('Flexible update failed:', error);
|
|
@@ -411,7 +413,7 @@ export class UpdateStatusManager {
|
|
|
411
413
|
|
|
412
414
|
private setupUpdateListeners() {
|
|
413
415
|
// Installation status
|
|
414
|
-
NativeUpdate.addListener('
|
|
416
|
+
NativeUpdate.addListener('updateStateChanged', (status) => {
|
|
415
417
|
switch (status.status) {
|
|
416
418
|
case 'PENDING':
|
|
417
419
|
console.log('Update pending');
|
|
@@ -650,9 +652,8 @@ if (state.status === 'FAILED') {
|
|
|
650
652
|
#### 3. iOS App Store Not Opening
|
|
651
653
|
|
|
652
654
|
```typescript
|
|
653
|
-
//
|
|
654
|
-
|
|
655
|
-
console.log('App Store ID:', config.appStoreId);
|
|
655
|
+
// App Store ID is configured in capacitor.config.json
|
|
656
|
+
// Check your configuration file for the appStoreId setting
|
|
656
657
|
|
|
657
658
|
// Manual fallback
|
|
658
659
|
if (!config.appStoreId) {
|
package/docs/QUICK_START.md
CHANGED
|
@@ -238,19 +238,21 @@ export class AndroidUpdateProgress {
|
|
|
238
238
|
);
|
|
239
239
|
|
|
240
240
|
// Handle completion
|
|
241
|
-
NativeUpdate.addListener('
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
241
|
+
NativeUpdate.addListener('updateStateChanged', async (event) => {
|
|
242
|
+
if (event.status === 'DOWNLOADED') {
|
|
243
|
+
const alert = await this.alertController.create({
|
|
244
|
+
header: 'Update Ready',
|
|
245
|
+
message: 'Update has been downloaded. Install now?',
|
|
246
|
+
buttons: [
|
|
247
|
+
{ text: 'Later' },
|
|
248
|
+
{
|
|
249
|
+
text: 'Install',
|
|
250
|
+
handler: () => NativeUpdate.completeFlexibleUpdate(),
|
|
251
|
+
},
|
|
252
|
+
],
|
|
253
|
+
});
|
|
254
|
+
await alert.present();
|
|
255
|
+
}
|
|
254
256
|
});
|
|
255
257
|
}
|
|
256
258
|
}
|
|
@@ -533,11 +535,10 @@ curl -X POST http://localhost:3000/api/v1/bundles \
|
|
|
533
535
|
### 3. Test App Reviews
|
|
534
536
|
|
|
535
537
|
```typescript
|
|
536
|
-
//
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
await NativeUpdate.requestReview({ force: true });
|
|
538
|
+
// Test review prompts
|
|
539
|
+
// Note: Reviews may not show in development builds
|
|
540
|
+
// Test with TestFlight (iOS) or Internal Test Track (Android)
|
|
541
|
+
await NativeUpdate.requestReview();
|
|
541
542
|
```
|
|
542
543
|
|
|
543
544
|
## What's Next?
|
|
@@ -566,12 +567,11 @@ Now that you have the basics working:
|
|
|
566
567
|
### Live Updates Not Working?
|
|
567
568
|
|
|
568
569
|
```typescript
|
|
569
|
-
// Enable debug logging
|
|
570
|
-
|
|
570
|
+
// Enable debug logging in your app
|
|
571
|
+
console.log('Checking update configuration...');
|
|
571
572
|
|
|
572
|
-
//
|
|
573
|
-
|
|
574
|
-
console.log('Update URL:', config.updateUrl);
|
|
573
|
+
// Configuration is set in capacitor.config.json
|
|
574
|
+
// Check your config file for updateUrl and other settings
|
|
575
575
|
```
|
|
576
576
|
|
|
577
577
|
### Native Updates Not Detected?
|
|
@@ -92,27 +92,7 @@ await NativeUpdate.trackSignificantEvent(eventName: string);
|
|
|
92
92
|
|
|
93
93
|
## Events
|
|
94
94
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
Fired when review prompt is shown.
|
|
98
|
-
|
|
99
|
-
```typescript
|
|
100
|
-
NativeUpdate.addListener('reviewPromptDisplayed', (event) => {
|
|
101
|
-
console.log('Review prompt shown on:', event.platform);
|
|
102
|
-
analytics.track('review_prompt_displayed');
|
|
103
|
-
});
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
### reviewPromptDismissed
|
|
107
|
-
|
|
108
|
-
Fired when review prompt is dismissed.
|
|
109
|
-
|
|
110
|
-
```typescript
|
|
111
|
-
NativeUpdate.addListener('reviewPromptDismissed', (event) => {
|
|
112
|
-
console.log('Review prompt dismissed');
|
|
113
|
-
analytics.track('review_prompt_dismissed');
|
|
114
|
-
});
|
|
115
|
-
```
|
|
95
|
+
Note: App review events are handled natively by the platform's review system. The plugin does not expose custom events for review prompts as these are controlled by iOS StoreKit and Android Play Core.
|
|
116
96
|
|
|
117
97
|
## Platform Implementation
|
|
118
98
|
|
|
@@ -25,20 +25,22 @@ await NativeUpdate.configure({
|
|
|
25
25
|
});
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
### sync()
|
|
28
|
+
### sync(options?)
|
|
29
29
|
|
|
30
30
|
Sync with update server and apply updates based on strategy.
|
|
31
31
|
|
|
32
32
|
```typescript
|
|
33
33
|
const result = await NativeUpdate.sync({
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
channel?: string; // Update channel (optional)
|
|
35
|
+
updateMode?: UpdateMode; // Update mode (optional)
|
|
36
36
|
});
|
|
37
37
|
// Returns:
|
|
38
38
|
{
|
|
39
|
-
status: 'UP_TO_DATE' | 'UPDATE_AVAILABLE' | 'UPDATE_INSTALLED' | 'ERROR'
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
status: SyncStatus; // 'UP_TO_DATE' | 'UPDATE_AVAILABLE' | 'UPDATE_INSTALLED' | 'ERROR'
|
|
40
|
+
version?: string; // Version if update available
|
|
41
|
+
description?: string; // Update description
|
|
42
|
+
mandatory?: boolean; // Is update mandatory
|
|
43
|
+
error?: UpdateError; // Error details if status is ERROR
|
|
42
44
|
}
|
|
43
45
|
```
|
|
44
46
|
|
|
@@ -48,15 +50,25 @@ Download a specific bundle version.
|
|
|
48
50
|
|
|
49
51
|
```typescript
|
|
50
52
|
const result = await NativeUpdate.download({
|
|
51
|
-
|
|
53
|
+
url: string; // Bundle URL (required)
|
|
54
|
+
version: string; // Version to download (required)
|
|
55
|
+
checksum: string; // Expected SHA-256 checksum (required)
|
|
56
|
+
signature?: string; // Optional bundle signature
|
|
57
|
+
maxRetries?: number; // Max download retries
|
|
58
|
+
timeout?: number; // Download timeout in ms
|
|
52
59
|
});
|
|
53
60
|
// Returns:
|
|
54
61
|
{
|
|
55
62
|
bundleId: string; // Unique bundle ID
|
|
56
63
|
version: string; // Bundle version
|
|
57
64
|
path: string; // Local path
|
|
65
|
+
downloadTime: number; // Download timestamp
|
|
58
66
|
size: number; // File size
|
|
67
|
+
status: BundleStatus; // Bundle status
|
|
59
68
|
checksum: string; // SHA-256 checksum
|
|
69
|
+
signature?: string; // Bundle signature if provided
|
|
70
|
+
verified: boolean; // Signature verification status
|
|
71
|
+
metadata?: Record<string, unknown>; // Optional metadata
|
|
60
72
|
}
|
|
61
73
|
```
|
|
62
74
|
|
|
@@ -115,7 +127,8 @@ Delete bundles.
|
|
|
115
127
|
```typescript
|
|
116
128
|
await NativeUpdate.delete({
|
|
117
129
|
bundleId?: string; // Delete specific bundle
|
|
118
|
-
|
|
130
|
+
keepVersions?: number; // Keep N most recent versions
|
|
131
|
+
olderThan?: number; // Delete bundles older than timestamp
|
|
119
132
|
});
|
|
120
133
|
```
|
|
121
134
|
|
|
@@ -135,21 +148,6 @@ Notify that the app has successfully started with the new bundle.
|
|
|
135
148
|
await NativeUpdate.notifyAppReady();
|
|
136
149
|
```
|
|
137
150
|
|
|
138
|
-
### pauseAutoUpdates()
|
|
139
|
-
|
|
140
|
-
Temporarily pause automatic update checks.
|
|
141
|
-
|
|
142
|
-
```typescript
|
|
143
|
-
await NativeUpdate.pauseAutoUpdates();
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
### resumeAutoUpdates()
|
|
147
|
-
|
|
148
|
-
Resume automatic update checks.
|
|
149
|
-
|
|
150
|
-
```typescript
|
|
151
|
-
await NativeUpdate.resumeAutoUpdates();
|
|
152
|
-
```
|
|
153
151
|
|
|
154
152
|
### setChannel(channel)
|
|
155
153
|
|
|
@@ -172,7 +170,7 @@ await NativeUpdate.setUpdateUrl(url: string);
|
|
|
172
170
|
Validate a bundle's integrity.
|
|
173
171
|
|
|
174
172
|
```typescript
|
|
175
|
-
const result = await NativeUpdate.validateUpdate({
|
|
173
|
+
const result = await NativeUpdate.LiveUpdate.validateUpdate({
|
|
176
174
|
bundlePath: string;
|
|
177
175
|
checksum: string;
|
|
178
176
|
signature?: string;
|
|
@@ -210,13 +208,23 @@ NativeUpdate.addListener('downloadProgress', (progress) => {
|
|
|
210
208
|
});
|
|
211
209
|
```
|
|
212
210
|
|
|
213
|
-
###
|
|
211
|
+
### backgroundUpdateProgress
|
|
212
|
+
|
|
213
|
+
Fired during background update operations.
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
NativeUpdate.addListener('backgroundUpdateProgress', (progress) => {
|
|
217
|
+
console.log('Background update:', progress.status);
|
|
218
|
+
});
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### backgroundUpdateNotification
|
|
214
222
|
|
|
215
|
-
Fired when
|
|
223
|
+
Fired when background update notifications are triggered.
|
|
216
224
|
|
|
217
225
|
```typescript
|
|
218
|
-
NativeUpdate.addListener('
|
|
219
|
-
console.
|
|
226
|
+
NativeUpdate.addListener('backgroundUpdateNotification', (notification) => {
|
|
227
|
+
console.log('Update notification:', notification.type);
|
|
220
228
|
});
|
|
221
229
|
```
|
|
222
230
|
|
|
@@ -23,8 +23,11 @@ async function checkForDeltaUpdate() {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
async function downloadDeltaUpdate(deltaUrl: string) {
|
|
26
|
+
// Download requires url, version, and checksum parameters
|
|
26
27
|
const download = await NativeUpdate.download({
|
|
27
|
-
|
|
28
|
+
url: result.deltaUrl,
|
|
29
|
+
version: result.version,
|
|
30
|
+
checksum: result.checksum
|
|
28
31
|
});
|
|
29
32
|
|
|
30
33
|
// Set the bundle as active
|
|
@@ -108,12 +111,14 @@ async function setupBackgroundUpdates() {
|
|
|
108
111
|
}
|
|
109
112
|
|
|
110
113
|
// Handle background update events
|
|
111
|
-
NativeUpdate.addListener('
|
|
114
|
+
NativeUpdate.addListener('backgroundUpdateNotification', (event) => {
|
|
112
115
|
// Notify user that update is ready
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
116
|
+
if (event.updateAvailable) {
|
|
117
|
+
showUpdateNotification({
|
|
118
|
+
version: event.version,
|
|
119
|
+
type: event.type
|
|
120
|
+
});
|
|
121
|
+
}
|
|
117
122
|
});
|
|
118
123
|
```
|
|
119
124
|
|
|
@@ -190,7 +195,9 @@ async function setupABTest(config: ABTestConfig) {
|
|
|
190
195
|
: config.variants.control;
|
|
191
196
|
|
|
192
197
|
const download = await NativeUpdate.download({
|
|
193
|
-
|
|
198
|
+
url: bundleUrl,
|
|
199
|
+
version: variant,
|
|
200
|
+
checksum: 'bundle-checksum' // This should come from server
|
|
194
201
|
});
|
|
195
202
|
|
|
196
203
|
// Set the bundle as active
|
|
@@ -247,8 +254,11 @@ class UpdateManager {
|
|
|
247
254
|
);
|
|
248
255
|
|
|
249
256
|
try {
|
|
257
|
+
// Download requires url, version, and checksum
|
|
250
258
|
const download = await NativeUpdate.download({
|
|
251
|
-
|
|
259
|
+
url: updateInfo.url,
|
|
260
|
+
version: updateInfo.version,
|
|
261
|
+
checksum: updateInfo.checksum
|
|
252
262
|
});
|
|
253
263
|
|
|
254
264
|
this.updateState = { status: 'installing' };
|
|
@@ -296,11 +306,13 @@ async function secureUpdateCheck() {
|
|
|
296
306
|
|
|
297
307
|
async function downloadWithIntegrityCheck(url: string, expectedHash: string) {
|
|
298
308
|
const download = await NativeUpdate.download({
|
|
299
|
-
|
|
309
|
+
url: url,
|
|
310
|
+
version: 'secure-version',
|
|
311
|
+
checksum: expectedHash
|
|
300
312
|
});
|
|
301
313
|
|
|
302
314
|
// Additional verification
|
|
303
|
-
const verified = await NativeUpdate.validateUpdate({
|
|
315
|
+
const verified = await NativeUpdate.LiveUpdate.validateUpdate({
|
|
304
316
|
bundlePath: download.path,
|
|
305
317
|
checksum: download.checksum,
|
|
306
318
|
signature: 'bundle-signature'
|
|
@@ -344,7 +356,9 @@ class UpdateMetrics {
|
|
|
344
356
|
// Download phase
|
|
345
357
|
metrics.downloadStarted = Date.now();
|
|
346
358
|
const download = await NativeUpdate.download({
|
|
347
|
-
|
|
359
|
+
url: result.url,
|
|
360
|
+
version: result.version,
|
|
361
|
+
checksum: result.checksum
|
|
348
362
|
});
|
|
349
363
|
metrics.downloadCompleted = Date.now();
|
|
350
364
|
metrics.downloadSize = download.size;
|
|
@@ -966,7 +966,7 @@ async function monitorReviewSuccess() {
|
|
|
966
966
|
## Next Steps
|
|
967
967
|
|
|
968
968
|
- Implement [Security Best Practices](../guides/security-best-practices.md)
|
|
969
|
-
-
|
|
969
|
+
- Review the [API Reference](../api/app-review-api.md)
|
|
970
970
|
- Configure [Live Updates](./live-updates.md)
|
|
971
971
|
- Review [API Reference](../api/app-review-api.md)
|
|
972
972
|
|
|
@@ -777,7 +777,7 @@ async function trackUpdateMetrics(event: string, data: any) {
|
|
|
777
777
|
|
|
778
778
|
- Configure [App Reviews](./app-reviews.md) for user feedback
|
|
779
779
|
- Implement [Security Best Practices](../guides/security-best-practices.md)
|
|
780
|
-
-
|
|
780
|
+
- Review [Testing Guide](../guides/testing-guide.md)
|
|
781
781
|
- Review [API Reference](../api/app-update-api.md)
|
|
782
782
|
|
|
783
783
|
---
|
|
@@ -379,9 +379,10 @@ async function getFeatureFlags() {
|
|
|
379
379
|
2. **Implement delta updates** (coming soon):
|
|
380
380
|
```typescript
|
|
381
381
|
// Future API
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
382
|
+
// Note: Delta updates are handled automatically by the sync() method
|
|
383
|
+
// when configured on the server. Direct delta download is not available
|
|
384
|
+
const bundle = await NativeUpdate.LiveUpdate.download({
|
|
385
|
+
version: latest.version,
|
|
385
386
|
});
|
|
386
387
|
```
|
|
387
388
|
|
|
@@ -409,15 +410,23 @@ async function getFeatureFlags() {
|
|
|
409
410
|
### Storage Management
|
|
410
411
|
|
|
411
412
|
```typescript
|
|
412
|
-
// Monitor
|
|
413
|
-
const
|
|
414
|
-
console.log(`
|
|
415
|
-
|
|
416
|
-
// Clean up
|
|
417
|
-
if (
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
413
|
+
// Monitor bundle count and clean up old versions
|
|
414
|
+
const bundles = await NativeUpdate.LiveUpdate.list();
|
|
415
|
+
console.log(`Total bundles: ${bundles.length}`);
|
|
416
|
+
|
|
417
|
+
// Clean up old bundles (keep only recent 5)
|
|
418
|
+
if (bundles.length > 5) {
|
|
419
|
+
// Sort by downloadTime and keep the 5 most recent
|
|
420
|
+
const sortedBundles = bundles.sort((a, b) =>
|
|
421
|
+
new Date(b.downloadTime).getTime() - new Date(a.downloadTime).getTime()
|
|
422
|
+
);
|
|
423
|
+
|
|
424
|
+
const toDelete = sortedBundles.slice(5);
|
|
425
|
+
for (const bundle of toDelete) {
|
|
426
|
+
await NativeUpdate.LiveUpdate.delete({
|
|
427
|
+
bundleId: bundle.bundleId,
|
|
428
|
+
});
|
|
429
|
+
}
|
|
421
430
|
}
|
|
422
431
|
```
|
|
423
432
|
|
|
@@ -526,7 +535,7 @@ const devConfig = {
|
|
|
526
535
|
};
|
|
527
536
|
|
|
528
537
|
// Force update check
|
|
529
|
-
await NativeUpdate.LiveUpdate.sync(
|
|
538
|
+
await NativeUpdate.LiveUpdate.sync();
|
|
530
539
|
|
|
531
540
|
// Simulate different scenarios
|
|
532
541
|
await testUpdateScenarios();
|
|
@@ -623,7 +632,7 @@ async function loadFeature(featureName: string) {
|
|
|
623
632
|
|
|
624
633
|
## Next Steps
|
|
625
634
|
|
|
626
|
-
- Set up your
|
|
635
|
+
- Set up your update server (see backend-template folder)
|
|
627
636
|
- Implement [Security Best Practices](../guides/security-best-practices.md)
|
|
628
637
|
- Configure [App Updates](./app-updates.md) for native changes
|
|
629
638
|
- Explore [API Reference](../api/live-update-api.md)
|
|
@@ -460,7 +460,7 @@ try {
|
|
|
460
460
|
## Next Steps
|
|
461
461
|
|
|
462
462
|
- Implement [Security Best Practices](../guides/security-best-practices.md)
|
|
463
|
-
- Set up your
|
|
463
|
+
- Set up your update server (see backend-template folder)
|
|
464
464
|
- Explore [Advanced Features](../features/live-updates.md)
|
|
465
465
|
|
|
466
466
|
---
|
|
@@ -194,13 +194,13 @@ After successful installation:
|
|
|
194
194
|
1. Read the [Quick Start Guide](./quick-start.md) for basic usage
|
|
195
195
|
2. Configure the plugin with your [update server settings](./configuration.md)
|
|
196
196
|
3. Implement [security best practices](../guides/security-best-practices.md)
|
|
197
|
-
4. Set up your
|
|
197
|
+
4. Set up your update server (see backend-template folder)
|
|
198
198
|
|
|
199
199
|
## Support
|
|
200
200
|
|
|
201
201
|
If you encounter any issues during installation:
|
|
202
202
|
|
|
203
|
-
- Check our [
|
|
203
|
+
- Check our [Testing Guide](../guides/testing-guide.md)
|
|
204
204
|
- Search existing [GitHub Issues](https://github.com/aoneahsan/native-update/issues)
|
|
205
205
|
- Create a new issue with detailed information about your setup
|
|
206
206
|
|
|
@@ -53,7 +53,7 @@ initializeUpdates();
|
|
|
53
53
|
// Sync with server and apply updates if available
|
|
54
54
|
async function syncUpdates() {
|
|
55
55
|
try {
|
|
56
|
-
const result = await NativeUpdate.
|
|
56
|
+
const result = await NativeUpdate.sync();
|
|
57
57
|
|
|
58
58
|
if (result.status === 'UPDATE_INSTALLED') {
|
|
59
59
|
console.log('Update installed:', result.bundle.version);
|
|
@@ -259,7 +259,7 @@ class UpdateManager {
|
|
|
259
259
|
|
|
260
260
|
async checkForUpdates() {
|
|
261
261
|
try {
|
|
262
|
-
const result = await NativeUpdate.
|
|
262
|
+
const result = await NativeUpdate.sync();
|
|
263
263
|
|
|
264
264
|
if (result.status === 'UPDATE_INSTALLED') {
|
|
265
265
|
// Notify user about update
|
|
@@ -349,7 +349,7 @@ async function safeUpdateCheck() {
|
|
|
349
349
|
|
|
350
350
|
Now that you have the basics working:
|
|
351
351
|
|
|
352
|
-
1. Set up your
|
|
352
|
+
1. Set up your update server (see backend-template folder)
|
|
353
353
|
2. Implement [Security Best Practices](../guides/security-best-practices.md)
|
|
354
354
|
3. Configure [Advanced Options](./configuration.md)
|
|
355
355
|
4. Explore [API Reference](../api/live-update-api.md) for all available methods
|
|
@@ -136,6 +136,6 @@ node tools/bundle-signer.js sign bundle.zip private-key.pem
|
|
|
136
136
|
|
|
137
137
|
## Need Help?
|
|
138
138
|
|
|
139
|
-
- See [
|
|
139
|
+
- See [Testing Guide](../guides/testing-guide.md)
|
|
140
140
|
- Check [Server Requirements](../server-requirements.md)
|
|
141
141
|
- Review [Security Best Practices](./security-best-practices.md)
|
|
@@ -1048,9 +1048,9 @@ const productionSecurityConfig = {
|
|
|
1048
1048
|
## Next Steps
|
|
1049
1049
|
|
|
1050
1050
|
- Review [Production Readiness](../production-readiness.md) checklist
|
|
1051
|
-
- Implement
|
|
1052
|
-
- Set up
|
|
1053
|
-
- Configure
|
|
1051
|
+
- Implement proper monitoring and analytics
|
|
1052
|
+
- Set up incident response procedures
|
|
1053
|
+
- Configure update server security (see backend-template folder)
|
|
1054
1054
|
|
|
1055
1055
|
---
|
|
1056
1056
|
|
|
@@ -123,7 +123,7 @@ try {
|
|
|
123
123
|
node tools/bundle-signer.js sign test-bundle.zip private-key.pem
|
|
124
124
|
|
|
125
125
|
# Verify in app
|
|
126
|
-
const isValid = await NativeUpdate.validateUpdate({
|
|
126
|
+
const isValid = await NativeUpdate.LiveUpdate.validateUpdate({
|
|
127
127
|
bundlePath: 'bundle-path',
|
|
128
128
|
checksum: 'bundle-checksum',
|
|
129
129
|
signature: signature
|
|
@@ -135,7 +135,7 @@ const isValid = await NativeUpdate.validateUpdate({
|
|
|
135
135
|
#### Download Performance
|
|
136
136
|
```typescript
|
|
137
137
|
// Monitor download speed
|
|
138
|
-
NativeUpdate.addListener('downloadProgress', (progress) => {
|
|
138
|
+
NativeUpdate.LiveUpdate.addListener('downloadProgress', (progress) => {
|
|
139
139
|
console.log(`Speed: ${progress.speed} MB/s`);
|
|
140
140
|
console.log(`Progress: ${progress.percent}%`);
|
|
141
141
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "native-update",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "Foundation package for building a comprehensive update system for Capacitor apps. Provides architecture and interfaces but requires backend implementation.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/plugin.cjs.js",
|