native-update 1.0.0
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/CapacitorNativeUpdate.podspec +18 -0
- package/LICENSE +21 -0
- package/Readme.md +451 -0
- package/android/build.gradle +92 -0
- package/android/gradle/wrapper/gradle-wrapper.properties +8 -0
- package/android/gradle.properties +17 -0
- package/android/proguard-rules.pro +29 -0
- package/android/settings.gradle +2 -0
- package/android/src/main/AndroidManifest.xml +34 -0
- package/android/src/main/java/com/aoneahsan/nativeupdate/AppReviewPlugin.kt +153 -0
- package/android/src/main/java/com/aoneahsan/nativeupdate/AppUpdatePlugin.kt +275 -0
- package/android/src/main/java/com/aoneahsan/nativeupdate/BackgroundNotificationManager.kt +390 -0
- package/android/src/main/java/com/aoneahsan/nativeupdate/BackgroundUpdateManager.kt +46 -0
- package/android/src/main/java/com/aoneahsan/nativeupdate/BackgroundUpdatePlugin.kt +333 -0
- package/android/src/main/java/com/aoneahsan/nativeupdate/BackgroundUpdateWorker.kt +251 -0
- package/android/src/main/java/com/aoneahsan/nativeupdate/CapacitorNativeUpdatePlugin.kt +265 -0
- package/android/src/main/java/com/aoneahsan/nativeupdate/LiveUpdatePlugin.kt +526 -0
- package/android/src/main/java/com/aoneahsan/nativeupdate/NotificationActionReceiver.kt +99 -0
- package/android/src/main/java/com/aoneahsan/nativeupdate/SecurityManager.kt +249 -0
- package/dist/esm/__tests__/bundle-manager.test.d.ts +1 -0
- package/dist/esm/__tests__/bundle-manager.test.js +123 -0
- package/dist/esm/__tests__/bundle-manager.test.js.map +1 -0
- package/dist/esm/__tests__/config.test.d.ts +1 -0
- package/dist/esm/__tests__/config.test.js +69 -0
- package/dist/esm/__tests__/config.test.js.map +1 -0
- package/dist/esm/__tests__/integration.test.d.ts +1 -0
- package/dist/esm/__tests__/integration.test.js +78 -0
- package/dist/esm/__tests__/integration.test.js.map +1 -0
- package/dist/esm/__tests__/security.test.d.ts +1 -0
- package/dist/esm/__tests__/security.test.js +54 -0
- package/dist/esm/__tests__/security.test.js.map +1 -0
- package/dist/esm/__tests__/version-manager.test.d.ts +1 -0
- package/dist/esm/__tests__/version-manager.test.js +45 -0
- package/dist/esm/__tests__/version-manager.test.js.map +1 -0
- package/dist/esm/app-review/app-review-manager.d.ts +24 -0
- package/dist/esm/app-review/app-review-manager.js +195 -0
- package/dist/esm/app-review/app-review-manager.js.map +1 -0
- package/dist/esm/app-review/index.d.ts +5 -0
- package/dist/esm/app-review/index.js +6 -0
- package/dist/esm/app-review/index.js.map +1 -0
- package/dist/esm/app-review/platform-review-handler.d.ts +20 -0
- package/dist/esm/app-review/platform-review-handler.js +138 -0
- package/dist/esm/app-review/platform-review-handler.js.map +1 -0
- package/dist/esm/app-review/review-conditions-checker.d.ts +22 -0
- package/dist/esm/app-review/review-conditions-checker.js +155 -0
- package/dist/esm/app-review/review-conditions-checker.js.map +1 -0
- package/dist/esm/app-review/review-rate-limiter.d.ts +23 -0
- package/dist/esm/app-review/review-rate-limiter.js +164 -0
- package/dist/esm/app-review/review-rate-limiter.js.map +1 -0
- package/dist/esm/app-review/types.d.ts +41 -0
- package/dist/esm/app-review/types.js +2 -0
- package/dist/esm/app-review/types.js.map +1 -0
- package/dist/esm/app-update/app-update-checker.d.ts +13 -0
- package/dist/esm/app-update/app-update-checker.js +104 -0
- package/dist/esm/app-update/app-update-checker.js.map +1 -0
- package/dist/esm/app-update/app-update-installer.d.ts +19 -0
- package/dist/esm/app-update/app-update-installer.js +123 -0
- package/dist/esm/app-update/app-update-installer.js.map +1 -0
- package/dist/esm/app-update/app-update-manager.d.ts +28 -0
- package/dist/esm/app-update/app-update-manager.js +199 -0
- package/dist/esm/app-update/app-update-manager.js.map +1 -0
- package/dist/esm/app-update/app-update-notifier.d.ts +14 -0
- package/dist/esm/app-update/app-update-notifier.js +100 -0
- package/dist/esm/app-update/app-update-notifier.js.map +1 -0
- package/dist/esm/app-update/index.d.ts +6 -0
- package/dist/esm/app-update/index.js +7 -0
- package/dist/esm/app-update/index.js.map +1 -0
- package/dist/esm/app-update/platform-app-update.d.ts +19 -0
- package/dist/esm/app-update/platform-app-update.js +129 -0
- package/dist/esm/app-update/platform-app-update.js.map +1 -0
- package/dist/esm/app-update/types.d.ts +58 -0
- package/dist/esm/app-update/types.js +12 -0
- package/dist/esm/app-update/types.js.map +1 -0
- package/dist/esm/background-update/background-scheduler.d.ts +17 -0
- package/dist/esm/background-update/background-scheduler.js +195 -0
- package/dist/esm/background-update/background-scheduler.js.map +1 -0
- package/dist/esm/background-update/index.d.ts +3 -0
- package/dist/esm/background-update/index.js +3 -0
- package/dist/esm/background-update/index.js.map +1 -0
- package/dist/esm/background-update/notification-manager.d.ts +29 -0
- package/dist/esm/background-update/notification-manager.js +89 -0
- package/dist/esm/background-update/notification-manager.js.map +1 -0
- package/dist/esm/core/analytics.d.ts +70 -0
- package/dist/esm/core/analytics.js +137 -0
- package/dist/esm/core/analytics.js.map +1 -0
- package/dist/esm/core/cache-manager.d.ts +72 -0
- package/dist/esm/core/cache-manager.js +275 -0
- package/dist/esm/core/cache-manager.js.map +1 -0
- package/dist/esm/core/config.d.ts +48 -0
- package/dist/esm/core/config.js +83 -0
- package/dist/esm/core/config.js.map +1 -0
- package/dist/esm/core/errors.d.ts +51 -0
- package/dist/esm/core/errors.js +80 -0
- package/dist/esm/core/errors.js.map +1 -0
- package/dist/esm/core/logger.d.ts +21 -0
- package/dist/esm/core/logger.js +109 -0
- package/dist/esm/core/logger.js.map +1 -0
- package/dist/esm/core/performance.d.ts +53 -0
- package/dist/esm/core/performance.js +140 -0
- package/dist/esm/core/performance.js.map +1 -0
- package/dist/esm/core/plugin-manager.d.ts +66 -0
- package/dist/esm/core/plugin-manager.js +148 -0
- package/dist/esm/core/plugin-manager.js.map +1 -0
- package/dist/esm/core/security.d.ts +93 -0
- package/dist/esm/core/security.js +315 -0
- package/dist/esm/core/security.js.map +1 -0
- package/dist/esm/definitions.d.ts +639 -0
- package/dist/esm/definitions.js +103 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +12 -0
- package/dist/esm/index.js +16 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/live-update/bundle-manager.d.ts +94 -0
- package/dist/esm/live-update/bundle-manager.js +310 -0
- package/dist/esm/live-update/bundle-manager.js.map +1 -0
- package/dist/esm/live-update/certificate-pinning.d.ts +38 -0
- package/dist/esm/live-update/certificate-pinning.js +78 -0
- package/dist/esm/live-update/certificate-pinning.js.map +1 -0
- package/dist/esm/live-update/download-manager.d.ts +67 -0
- package/dist/esm/live-update/download-manager.js +319 -0
- package/dist/esm/live-update/download-manager.js.map +1 -0
- package/dist/esm/live-update/update-manager.d.ts +52 -0
- package/dist/esm/live-update/update-manager.js +294 -0
- package/dist/esm/live-update/update-manager.js.map +1 -0
- package/dist/esm/live-update/version-manager.d.ts +84 -0
- package/dist/esm/live-update/version-manager.js +335 -0
- package/dist/esm/live-update/version-manager.js.map +1 -0
- package/dist/esm/plugin.d.ts +6 -0
- package/dist/esm/plugin.js +283 -0
- package/dist/esm/plugin.js.map +1 -0
- package/dist/esm/security/crypto.d.ts +25 -0
- package/dist/esm/security/crypto.js +70 -0
- package/dist/esm/security/crypto.js.map +1 -0
- package/dist/esm/security/validator.d.ts +60 -0
- package/dist/esm/security/validator.js +143 -0
- package/dist/esm/security/validator.js.map +1 -0
- package/dist/esm/web.d.ts +74 -0
- package/dist/esm/web.js +595 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +2 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.esm.js +2 -0
- package/dist/plugin.esm.js.map +1 -0
- package/dist/plugin.js +3 -0
- package/dist/plugin.js.map +1 -0
- package/docs/APP_REVIEW_GUIDE.md +768 -0
- package/docs/BUNDLE_SIGNING.md +264 -0
- package/docs/LIVE_UPDATES_GUIDE.md +650 -0
- package/docs/MIGRATION.md +192 -0
- package/docs/NATIVE_UPDATES_GUIDE.md +694 -0
- package/docs/QUICK_START.md +606 -0
- package/docs/README.md +111 -0
- package/docs/REMAINING_FEATURES.md +139 -0
- package/docs/api/app-review-api.md +259 -0
- package/docs/api/app-update-api.md +238 -0
- package/docs/api/events-api.md +451 -0
- package/docs/api/live-update-api.md +265 -0
- package/docs/background-updates.md +392 -0
- package/docs/examples/advanced-scenarios.md +410 -0
- package/docs/examples/basic-usage.md +185 -0
- package/docs/features/app-reviews.md +975 -0
- package/docs/features/app-updates.md +785 -0
- package/docs/features/live-updates.md +633 -0
- package/docs/getting-started/configuration.md +468 -0
- package/docs/getting-started/installation.md +209 -0
- package/docs/getting-started/quick-start.md +379 -0
- package/docs/guides/deployment-guide.md +333 -0
- package/docs/guides/migration-from-codepush.md +142 -0
- package/docs/guides/security-best-practices.md +1057 -0
- package/docs/guides/testing-guide.md +373 -0
- package/docs/production-readiness.md +478 -0
- package/docs/security/certificate-pinning.md +122 -0
- package/docs/server-requirements.md +147 -0
- package/ios/Plugin/AppReview/AppReviewPlugin.swift +158 -0
- package/ios/Plugin/AppUpdate/AppUpdatePlugin.swift +234 -0
- package/ios/Plugin/BackgroundUpdate/BackgroundNotificationManager.swift +329 -0
- package/ios/Plugin/BackgroundUpdate/BackgroundUpdatePlugin.swift +396 -0
- package/ios/Plugin/CapacitorNativeUpdatePlugin.m +45 -0
- package/ios/Plugin/CapacitorNativeUpdatePlugin.swift +190 -0
- package/ios/Plugin/Info.plist +43 -0
- package/ios/Plugin/LiveUpdate/LiveUpdatePlugin.swift +689 -0
- package/ios/Plugin/LiveUpdate/WebViewConfiguration.swift +45 -0
- package/ios/Plugin/Security/SecurityManager.swift +289 -0
- package/package.json +90 -0
|
@@ -0,0 +1,633 @@
|
|
|
1
|
+
# Live Updates (OTA)
|
|
2
|
+
|
|
3
|
+
> **⚠️ Backend Infrastructure Required**
|
|
4
|
+
>
|
|
5
|
+
> Live Updates require a complete backend infrastructure that you must build:
|
|
6
|
+
> - **Update Server**: Host and serve update bundles
|
|
7
|
+
> - **Bundle Generation**: CI/CD pipeline to create and sign bundles
|
|
8
|
+
> - **Version Management**: Track versions, channels, and rollbacks
|
|
9
|
+
> - **Security Infrastructure**: Implement signing and verification
|
|
10
|
+
> - **CDN/Storage**: Distribute bundles globally
|
|
11
|
+
>
|
|
12
|
+
> This plugin provides the client-side implementation only. Without the backend, Live Updates will not function.
|
|
13
|
+
|
|
14
|
+
Live Updates, also known as Over-The-Air (OTA) updates, allow you to deploy JavaScript, HTML, and CSS changes to your app instantly without going through app store review processes. This feature is perfect for bug fixes, content updates, and minor feature additions.
|
|
15
|
+
|
|
16
|
+
## How Live Updates Work
|
|
17
|
+
|
|
18
|
+
1. **Bundle Creation**: Your web assets are packaged into a bundle with metadata
|
|
19
|
+
2. **Version Check**: The app checks for new versions on your update server
|
|
20
|
+
3. **Download**: New bundles are downloaded in the background
|
|
21
|
+
4. **Verification**: Checksums and signatures are validated for security
|
|
22
|
+
5. **Installation**: The update is applied based on your configured strategy
|
|
23
|
+
6. **Activation**: The app reloads with the new bundle
|
|
24
|
+
|
|
25
|
+
## Key Features
|
|
26
|
+
|
|
27
|
+
### 🚀 Instant Deployment
|
|
28
|
+
|
|
29
|
+
- Deploy updates within minutes
|
|
30
|
+
- No app store review delays
|
|
31
|
+
- Fix critical bugs immediately
|
|
32
|
+
|
|
33
|
+
### 🔒 Secure Updates
|
|
34
|
+
|
|
35
|
+
- Cryptographic signature verification
|
|
36
|
+
- Checksum validation
|
|
37
|
+
- HTTPS enforcement
|
|
38
|
+
- Certificate pinning support
|
|
39
|
+
|
|
40
|
+
### 📦 Smart Bundle Management
|
|
41
|
+
|
|
42
|
+
- Automatic cleanup of old bundles
|
|
43
|
+
- Storage usage optimization
|
|
44
|
+
- Version history tracking
|
|
45
|
+
- Rollback capabilities
|
|
46
|
+
|
|
47
|
+
### 🎯 Targeted Deployments
|
|
48
|
+
|
|
49
|
+
- Multiple update channels
|
|
50
|
+
- Gradual rollouts
|
|
51
|
+
- A/B testing support
|
|
52
|
+
- User segmentation
|
|
53
|
+
|
|
54
|
+
## Implementation Guide
|
|
55
|
+
|
|
56
|
+
### Basic Implementation
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
// 1. Configure live updates
|
|
60
|
+
await CapacitorNativeUpdate.configure({
|
|
61
|
+
liveUpdate: {
|
|
62
|
+
appId: 'com.myapp.example',
|
|
63
|
+
serverUrl: 'https://updates.myserver.com',
|
|
64
|
+
autoUpdate: true,
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// 2. Sync updates (automatic with autoUpdate: true)
|
|
69
|
+
const result = await CapacitorNativeUpdate.LiveUpdate.sync();
|
|
70
|
+
|
|
71
|
+
// 3. Handle the result
|
|
72
|
+
switch (result.status) {
|
|
73
|
+
case 'UPDATE_INSTALLED':
|
|
74
|
+
console.log('Update installed:', result.bundle.version);
|
|
75
|
+
break;
|
|
76
|
+
case 'UP_TO_DATE':
|
|
77
|
+
console.log('App is up to date');
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Advanced Implementation
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
class LiveUpdateManager {
|
|
86
|
+
private updateAvailable = false;
|
|
87
|
+
|
|
88
|
+
async initialize() {
|
|
89
|
+
// Configure with advanced options
|
|
90
|
+
await CapacitorNativeUpdate.configure({
|
|
91
|
+
liveUpdate: {
|
|
92
|
+
appId: 'com.myapp.example',
|
|
93
|
+
serverUrl: 'https://updates.myserver.com',
|
|
94
|
+
channel: this.getUpdateChannel(),
|
|
95
|
+
updateStrategy: 'BACKGROUND',
|
|
96
|
+
publicKey: this.getPublicKey(),
|
|
97
|
+
requireSignature: true,
|
|
98
|
+
checksumAlgorithm: 'SHA-512',
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// Set up listeners
|
|
103
|
+
this.setupUpdateListeners();
|
|
104
|
+
|
|
105
|
+
// Initial check
|
|
106
|
+
await this.checkForUpdates();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private setupUpdateListeners() {
|
|
110
|
+
// Download progress
|
|
111
|
+
CapacitorNativeUpdate.LiveUpdate.addListener(
|
|
112
|
+
'downloadProgress',
|
|
113
|
+
(progress) => {
|
|
114
|
+
this.updateDownloadProgress(progress.percent);
|
|
115
|
+
}
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
// State changes
|
|
119
|
+
CapacitorNativeUpdate.LiveUpdate.addListener(
|
|
120
|
+
'updateStateChanged',
|
|
121
|
+
(event) => {
|
|
122
|
+
this.handleStateChange(event);
|
|
123
|
+
}
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async checkForUpdates() {
|
|
128
|
+
try {
|
|
129
|
+
const latest = await CapacitorNativeUpdate.LiveUpdate.getLatest();
|
|
130
|
+
const current = await CapacitorNativeUpdate.LiveUpdate.current();
|
|
131
|
+
|
|
132
|
+
if (this.isNewerVersion(latest.version, current.version)) {
|
|
133
|
+
this.updateAvailable = true;
|
|
134
|
+
this.notifyUpdateAvailable(latest);
|
|
135
|
+
}
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.error('Update check failed:', error);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async downloadUpdate() {
|
|
142
|
+
if (!this.updateAvailable) return;
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
const bundle = await CapacitorNativeUpdate.LiveUpdate.download({
|
|
146
|
+
version: 'latest',
|
|
147
|
+
onProgress: (progress) => {
|
|
148
|
+
console.log(`Download: ${progress.percent}%`);
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Validate the bundle
|
|
153
|
+
const validation = await CapacitorNativeUpdate.LiveUpdate.validateUpdate({
|
|
154
|
+
bundleId: bundle.bundleId,
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
if (validation.valid) {
|
|
158
|
+
await this.installUpdate(bundle);
|
|
159
|
+
} else {
|
|
160
|
+
throw new Error('Bundle validation failed');
|
|
161
|
+
}
|
|
162
|
+
} catch (error) {
|
|
163
|
+
this.handleUpdateError(error);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async installUpdate(bundle: BundleInfo) {
|
|
168
|
+
// Set the bundle as active
|
|
169
|
+
await CapacitorNativeUpdate.LiveUpdate.set(bundle);
|
|
170
|
+
|
|
171
|
+
// Notify app is ready (important for rollback mechanism)
|
|
172
|
+
await CapacitorNativeUpdate.LiveUpdate.notifyAppReady();
|
|
173
|
+
|
|
174
|
+
// Schedule reload based on user preference
|
|
175
|
+
this.scheduleReload();
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private scheduleReload() {
|
|
179
|
+
// Option 1: Immediate reload
|
|
180
|
+
// await CapacitorNativeUpdate.LiveUpdate.reload();
|
|
181
|
+
|
|
182
|
+
// Option 2: Reload on next app resume
|
|
183
|
+
App.addListener('appStateChange', ({ isActive }) => {
|
|
184
|
+
if (isActive) {
|
|
185
|
+
CapacitorNativeUpdate.LiveUpdate.reload();
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// Option 3: User-triggered reload
|
|
190
|
+
this.showUpdateReadyDialog();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Update Strategies
|
|
196
|
+
|
|
197
|
+
### Immediate Updates
|
|
198
|
+
|
|
199
|
+
Updates are downloaded and applied immediately when detected.
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
{
|
|
203
|
+
updateStrategy: 'IMMEDIATE',
|
|
204
|
+
mandatoryInstallMode: 'IMMEDIATE'
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
**Use when:**
|
|
209
|
+
|
|
210
|
+
- Critical bug fixes
|
|
211
|
+
- Security patches
|
|
212
|
+
- Time-sensitive content
|
|
213
|
+
|
|
214
|
+
### Background Updates
|
|
215
|
+
|
|
216
|
+
Updates download in the background and install based on install mode.
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
{
|
|
220
|
+
updateStrategy: 'BACKGROUND',
|
|
221
|
+
mandatoryInstallMode: 'ON_NEXT_RESTART',
|
|
222
|
+
optionalInstallMode: 'ON_NEXT_RESUME'
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**Use when:**
|
|
227
|
+
|
|
228
|
+
- Regular feature updates
|
|
229
|
+
- Non-critical improvements
|
|
230
|
+
- Large bundle sizes
|
|
231
|
+
|
|
232
|
+
### Manual Updates
|
|
233
|
+
|
|
234
|
+
Full control over download and installation timing.
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
{
|
|
238
|
+
updateStrategy: 'MANUAL',
|
|
239
|
+
autoUpdate: false
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
**Use when:**
|
|
244
|
+
|
|
245
|
+
- User-initiated updates
|
|
246
|
+
- Specific update windows
|
|
247
|
+
- Custom update UI
|
|
248
|
+
|
|
249
|
+
## Version Management
|
|
250
|
+
|
|
251
|
+
### Semantic Versioning
|
|
252
|
+
|
|
253
|
+
The plugin uses semantic versioning (MAJOR.MINOR.PATCH):
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
// Version comparison
|
|
257
|
+
const current = await CapacitorNativeUpdate.LiveUpdate.current();
|
|
258
|
+
console.log(current.version); // "1.2.3"
|
|
259
|
+
|
|
260
|
+
// Check if update is major/minor/patch
|
|
261
|
+
function getUpdateType(oldVersion: string, newVersion: string) {
|
|
262
|
+
const [oldMajor, oldMinor, oldPatch] = oldVersion.split('.').map(Number);
|
|
263
|
+
const [newMajor, newMinor, newPatch] = newVersion.split('.').map(Number);
|
|
264
|
+
|
|
265
|
+
if (newMajor > oldMajor) return 'major';
|
|
266
|
+
if (newMinor > oldMinor) return 'minor';
|
|
267
|
+
if (newPatch > oldPatch) return 'patch';
|
|
268
|
+
return 'none';
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### Bundle History
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
// List all downloaded bundles
|
|
276
|
+
const bundles = await CapacitorNativeUpdate.LiveUpdate.list();
|
|
277
|
+
|
|
278
|
+
bundles.forEach((bundle) => {
|
|
279
|
+
console.log(`Version: ${bundle.version}`);
|
|
280
|
+
console.log(`Downloaded: ${new Date(bundle.downloadTime)}`);
|
|
281
|
+
console.log(`Size: ${bundle.size} bytes`);
|
|
282
|
+
console.log(`Status: ${bundle.status}`);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// Clean up old bundles
|
|
286
|
+
await CapacitorNativeUpdate.LiveUpdate.delete({
|
|
287
|
+
keepNewest: 3, // Keep only 3 most recent bundles
|
|
288
|
+
});
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## Rollback Mechanism
|
|
292
|
+
|
|
293
|
+
The plugin includes automatic rollback for failed updates.
|
|
294
|
+
|
|
295
|
+
### Automatic Rollback
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
// Mark app as ready after successful startup
|
|
299
|
+
async function onAppReady() {
|
|
300
|
+
try {
|
|
301
|
+
// Verify app is working correctly
|
|
302
|
+
await performHealthCheck();
|
|
303
|
+
|
|
304
|
+
// Notify that app started successfully
|
|
305
|
+
await CapacitorNativeUpdate.LiveUpdate.notifyAppReady();
|
|
306
|
+
} catch (error) {
|
|
307
|
+
// Don't call notifyAppReady() - automatic rollback will occur
|
|
308
|
+
console.error('App startup failed:', error);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Manual Rollback
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
// Reset to original bundle
|
|
317
|
+
await CapacitorNativeUpdate.LiveUpdate.reset();
|
|
318
|
+
|
|
319
|
+
// Or rollback to previous version
|
|
320
|
+
const bundles = await CapacitorNativeUpdate.LiveUpdate.list();
|
|
321
|
+
const previousBundle = bundles[bundles.length - 2];
|
|
322
|
+
if (previousBundle) {
|
|
323
|
+
await CapacitorNativeUpdate.LiveUpdate.set(previousBundle);
|
|
324
|
+
await CapacitorNativeUpdate.LiveUpdate.reload();
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
## Update Channels
|
|
329
|
+
|
|
330
|
+
Use channels to manage different release tracks.
|
|
331
|
+
|
|
332
|
+
### Channel Configuration
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
// Set channel based on user preference
|
|
336
|
+
const channel = getUserPreference('updateChannel') || 'production';
|
|
337
|
+
await CapacitorNativeUpdate.LiveUpdate.setChannel(channel);
|
|
338
|
+
|
|
339
|
+
// Available channels examples:
|
|
340
|
+
// - 'production': Stable releases
|
|
341
|
+
// - 'beta': Beta testing
|
|
342
|
+
// - 'alpha': Early access
|
|
343
|
+
// - 'development': Dev builds
|
|
344
|
+
// - 'staging': Pre-production
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Channel-Based Features
|
|
348
|
+
|
|
349
|
+
```typescript
|
|
350
|
+
// Enable features based on channel
|
|
351
|
+
async function getFeatureFlags() {
|
|
352
|
+
const bundle = await CapacitorNativeUpdate.LiveUpdate.current();
|
|
353
|
+
|
|
354
|
+
switch (bundle.metadata?.channel) {
|
|
355
|
+
case 'alpha':
|
|
356
|
+
return { experimentalFeatures: true, debugMode: true };
|
|
357
|
+
case 'beta':
|
|
358
|
+
return { experimentalFeatures: true, debugMode: false };
|
|
359
|
+
default:
|
|
360
|
+
return { experimentalFeatures: false, debugMode: false };
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
## Performance Optimization
|
|
366
|
+
|
|
367
|
+
### Bundle Size Optimization
|
|
368
|
+
|
|
369
|
+
1. **Minimize bundle size**:
|
|
370
|
+
|
|
371
|
+
```bash
|
|
372
|
+
# Use production builds
|
|
373
|
+
npm run build -- --mode production
|
|
374
|
+
|
|
375
|
+
# Enable compression
|
|
376
|
+
gzip -9 bundle.js
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
2. **Implement delta updates** (coming soon):
|
|
380
|
+
```typescript
|
|
381
|
+
// Future API
|
|
382
|
+
const delta = await CapacitorNativeUpdate.LiveUpdate.downloadDelta({
|
|
383
|
+
fromVersion: current.version,
|
|
384
|
+
toVersion: latest.version,
|
|
385
|
+
});
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### Download Optimization
|
|
389
|
+
|
|
390
|
+
```typescript
|
|
391
|
+
// Configure optimal download settings
|
|
392
|
+
{
|
|
393
|
+
liveUpdate: {
|
|
394
|
+
// Download only on WiFi
|
|
395
|
+
downloadOnWifiOnly: true,
|
|
396
|
+
|
|
397
|
+
// Limit concurrent downloads
|
|
398
|
+
maxConcurrentDownloads: 1,
|
|
399
|
+
|
|
400
|
+
// Set reasonable timeout
|
|
401
|
+
timeout: 30000,
|
|
402
|
+
|
|
403
|
+
// Enable resume capability
|
|
404
|
+
resumableDownloads: true
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
### Storage Management
|
|
410
|
+
|
|
411
|
+
```typescript
|
|
412
|
+
// Monitor storage usage
|
|
413
|
+
const storage = await CapacitorNativeUpdate.LiveUpdate.getStorageInfo();
|
|
414
|
+
console.log(`Used: ${storage.usedBytes} / ${storage.totalBytes}`);
|
|
415
|
+
|
|
416
|
+
// Clean up when needed
|
|
417
|
+
if (storage.usedBytes > storage.totalBytes * 0.8) {
|
|
418
|
+
await CapacitorNativeUpdate.LiveUpdate.delete({
|
|
419
|
+
olderThan: Date.now() - 30 * 24 * 60 * 60 * 1000, // 30 days
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
## Error Handling
|
|
425
|
+
|
|
426
|
+
### Common Errors and Solutions
|
|
427
|
+
|
|
428
|
+
```typescript
|
|
429
|
+
try {
|
|
430
|
+
await CapacitorNativeUpdate.LiveUpdate.sync();
|
|
431
|
+
} catch (error) {
|
|
432
|
+
switch (error.code) {
|
|
433
|
+
case 'NETWORK_ERROR':
|
|
434
|
+
// No internet connection
|
|
435
|
+
showOfflineMessage();
|
|
436
|
+
break;
|
|
437
|
+
|
|
438
|
+
case 'SERVER_ERROR':
|
|
439
|
+
// Update server is down
|
|
440
|
+
scheduleRetry();
|
|
441
|
+
break;
|
|
442
|
+
|
|
443
|
+
case 'CHECKSUM_ERROR':
|
|
444
|
+
// Bundle corrupted
|
|
445
|
+
await CapacitorNativeUpdate.LiveUpdate.delete({
|
|
446
|
+
bundleId: error.bundleId,
|
|
447
|
+
});
|
|
448
|
+
break;
|
|
449
|
+
|
|
450
|
+
case 'SIGNATURE_ERROR':
|
|
451
|
+
// Security validation failed
|
|
452
|
+
logSecurityEvent(error);
|
|
453
|
+
break;
|
|
454
|
+
|
|
455
|
+
case 'STORAGE_ERROR':
|
|
456
|
+
// Not enough space
|
|
457
|
+
await cleanupStorage();
|
|
458
|
+
break;
|
|
459
|
+
|
|
460
|
+
case 'VERSION_MISMATCH':
|
|
461
|
+
// Incompatible update
|
|
462
|
+
promptForAppUpdate();
|
|
463
|
+
break;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
### Retry Logic
|
|
469
|
+
|
|
470
|
+
```typescript
|
|
471
|
+
class UpdateRetryManager {
|
|
472
|
+
private retryCount = 0;
|
|
473
|
+
private maxRetries = 3;
|
|
474
|
+
|
|
475
|
+
async syncWithRetry() {
|
|
476
|
+
try {
|
|
477
|
+
await CapacitorNativeUpdate.LiveUpdate.sync();
|
|
478
|
+
this.retryCount = 0; // Reset on success
|
|
479
|
+
} catch (error) {
|
|
480
|
+
if (this.shouldRetry(error)) {
|
|
481
|
+
this.retryCount++;
|
|
482
|
+
const delay = this.getRetryDelay();
|
|
483
|
+
|
|
484
|
+
console.log(
|
|
485
|
+
`Retry ${this.retryCount}/${this.maxRetries} in ${delay}ms`
|
|
486
|
+
);
|
|
487
|
+
|
|
488
|
+
setTimeout(() => {
|
|
489
|
+
this.syncWithRetry();
|
|
490
|
+
}, delay);
|
|
491
|
+
} else {
|
|
492
|
+
throw error; // Don't retry
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
private shouldRetry(error: any): boolean {
|
|
498
|
+
return (
|
|
499
|
+
this.retryCount < this.maxRetries &&
|
|
500
|
+
['NETWORK_ERROR', 'TIMEOUT_ERROR', 'SERVER_ERROR'].includes(error.code)
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
private getRetryDelay(): number {
|
|
505
|
+
// Exponential backoff
|
|
506
|
+
return Math.min(1000 * Math.pow(2, this.retryCount), 30000);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
## Testing Live Updates
|
|
512
|
+
|
|
513
|
+
### Development Testing
|
|
514
|
+
|
|
515
|
+
```typescript
|
|
516
|
+
// Enable development mode
|
|
517
|
+
const devConfig = {
|
|
518
|
+
liveUpdate: {
|
|
519
|
+
appId: 'com.myapp.dev',
|
|
520
|
+
serverUrl: 'http://localhost:3000',
|
|
521
|
+
channel: 'development',
|
|
522
|
+
allowEmulator: true,
|
|
523
|
+
requireSignature: false, // Disable in dev
|
|
524
|
+
autoUpdate: false, // Manual control
|
|
525
|
+
},
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
// Force update check
|
|
529
|
+
await CapacitorNativeUpdate.LiveUpdate.sync({ forceCheck: true });
|
|
530
|
+
|
|
531
|
+
// Simulate different scenarios
|
|
532
|
+
await testUpdateScenarios();
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
### Update Server Testing
|
|
536
|
+
|
|
537
|
+
Set up a local update server for testing:
|
|
538
|
+
|
|
539
|
+
```bash
|
|
540
|
+
# See server-example directory
|
|
541
|
+
cd server-example
|
|
542
|
+
npm install
|
|
543
|
+
npm run dev
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
### Test Scenarios
|
|
547
|
+
|
|
548
|
+
1. **Successful update**
|
|
549
|
+
2. **Network interruption**
|
|
550
|
+
3. **Corrupted bundle**
|
|
551
|
+
4. **Version rollback**
|
|
552
|
+
5. **Storage full**
|
|
553
|
+
6. **Signature mismatch**
|
|
554
|
+
|
|
555
|
+
## Best Practices
|
|
556
|
+
|
|
557
|
+
### 1. Progressive Rollout
|
|
558
|
+
|
|
559
|
+
```typescript
|
|
560
|
+
// Roll out to percentage of users
|
|
561
|
+
function shouldReceiveUpdate(userId: string, percentage: number): boolean {
|
|
562
|
+
const hash = hashCode(userId);
|
|
563
|
+
return hash % 100 < percentage;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
if (shouldReceiveUpdate(user.id, 10)) {
|
|
567
|
+
// 10% rollout
|
|
568
|
+
await CapacitorNativeUpdate.LiveUpdate.setChannel('beta');
|
|
569
|
+
}
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
### 2. Update Notifications
|
|
573
|
+
|
|
574
|
+
```typescript
|
|
575
|
+
// Notify users about updates
|
|
576
|
+
CapacitorNativeUpdate.LiveUpdate.addListener('updateStateChanged', (event) => {
|
|
577
|
+
if (event.status === 'READY') {
|
|
578
|
+
showNotification({
|
|
579
|
+
title: 'Update Ready',
|
|
580
|
+
body: 'A new version is available. Restart to apply.',
|
|
581
|
+
actions: [
|
|
582
|
+
{ id: 'restart', title: 'Restart Now' },
|
|
583
|
+
{ id: 'later', title: 'Later' },
|
|
584
|
+
],
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
### 3. Monitoring
|
|
591
|
+
|
|
592
|
+
```typescript
|
|
593
|
+
// Track update metrics
|
|
594
|
+
async function trackUpdateMetrics(result: SyncResult) {
|
|
595
|
+
const metrics = {
|
|
596
|
+
event: 'live_update',
|
|
597
|
+
status: result.status,
|
|
598
|
+
version: result.bundle?.version,
|
|
599
|
+
downloadTime: result.downloadTime,
|
|
600
|
+
installTime: result.installTime,
|
|
601
|
+
bundleSize: result.bundle?.size,
|
|
602
|
+
};
|
|
603
|
+
|
|
604
|
+
await analytics.track(metrics);
|
|
605
|
+
}
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
### 4. Fallback Strategy
|
|
609
|
+
|
|
610
|
+
```typescript
|
|
611
|
+
// Implement fallback for critical features
|
|
612
|
+
async function loadFeature(featureName: string) {
|
|
613
|
+
try {
|
|
614
|
+
// Try loading from update bundle
|
|
615
|
+
return await import(`./features/${featureName}`);
|
|
616
|
+
} catch (error) {
|
|
617
|
+
// Fall back to shipped version
|
|
618
|
+
console.warn(`Loading fallback for ${featureName}`);
|
|
619
|
+
return await import(`./features-fallback/${featureName}`);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
## Next Steps
|
|
625
|
+
|
|
626
|
+
- Set up your [Update Server](../examples/server-setup.md)
|
|
627
|
+
- Implement [Security Best Practices](../guides/security-best-practices.md)
|
|
628
|
+
- Configure [App Updates](./app-updates.md) for native changes
|
|
629
|
+
- Explore [API Reference](../api/live-update-api.md)
|
|
630
|
+
|
|
631
|
+
---
|
|
632
|
+
|
|
633
|
+
Made with ❤️ by Ahsan Mahmood
|