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,785 @@
|
|
|
1
|
+
# App Updates
|
|
2
|
+
|
|
3
|
+
The App Updates feature provides a seamless way to manage native app store updates, ensuring users always have the latest version of your app with critical native changes, new permissions, or platform-specific features.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
While Live Updates handle web assets, App Updates manage the native app binary itself. This feature integrates with:
|
|
8
|
+
|
|
9
|
+
- **Google Play Store** on Android (using Play Core Library)
|
|
10
|
+
- **Apple App Store** on iOS (using manual version checking)
|
|
11
|
+
- **Web platforms** with fallback notifications
|
|
12
|
+
|
|
13
|
+
## Key Features
|
|
14
|
+
|
|
15
|
+
### 🎯 Update Priority Management
|
|
16
|
+
|
|
17
|
+
- Configure update urgency (0-5 scale)
|
|
18
|
+
- Force critical updates
|
|
19
|
+
- Flexible update scheduling
|
|
20
|
+
|
|
21
|
+
### 📱 Native UI Integration
|
|
22
|
+
|
|
23
|
+
- Platform-specific update dialogs
|
|
24
|
+
- In-app download progress
|
|
25
|
+
- Seamless installation flow
|
|
26
|
+
|
|
27
|
+
### 🔄 Update Types
|
|
28
|
+
|
|
29
|
+
- **Immediate Updates**: Blocking updates for critical fixes
|
|
30
|
+
- **Flexible Updates**: Background downloads with user control
|
|
31
|
+
- **Manual Updates**: Direct app store navigation
|
|
32
|
+
|
|
33
|
+
### 📊 Version Intelligence
|
|
34
|
+
|
|
35
|
+
- Semantic version comparison
|
|
36
|
+
- Minimum version enforcement
|
|
37
|
+
- Update availability detection
|
|
38
|
+
|
|
39
|
+
## Implementation Guide
|
|
40
|
+
|
|
41
|
+
### Basic Implementation
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
// Check for app updates on startup
|
|
45
|
+
async function checkForAppUpdates() {
|
|
46
|
+
try {
|
|
47
|
+
const updateInfo = await CapacitorNativeUpdate.AppUpdate.getAppUpdateInfo();
|
|
48
|
+
|
|
49
|
+
if (updateInfo.updateAvailable) {
|
|
50
|
+
console.log(`Update available: ${updateInfo.availableVersion}`);
|
|
51
|
+
|
|
52
|
+
// Handle based on priority
|
|
53
|
+
if (updateInfo.updatePriority >= 4) {
|
|
54
|
+
// High priority - immediate update
|
|
55
|
+
await CapacitorNativeUpdate.AppUpdate.performImmediateUpdate();
|
|
56
|
+
} else {
|
|
57
|
+
// Optional update - show custom UI
|
|
58
|
+
showUpdateDialog(updateInfo);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
} catch (error) {
|
|
62
|
+
console.error('Update check failed:', error);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Advanced Implementation
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
class AppUpdateManager {
|
|
71
|
+
private updateInfo: AppUpdateInfo | null = null;
|
|
72
|
+
private downloadProgress = 0;
|
|
73
|
+
|
|
74
|
+
async initialize() {
|
|
75
|
+
// Configure app updates
|
|
76
|
+
await CapacitorNativeUpdate.configure({
|
|
77
|
+
appUpdate: {
|
|
78
|
+
checkOnAppStart: true,
|
|
79
|
+
minimumVersion: '2.0.0',
|
|
80
|
+
updatePriority: 3,
|
|
81
|
+
storeUrl: {
|
|
82
|
+
android: 'https://play.google.com/store/apps/details?id=com.myapp',
|
|
83
|
+
ios: 'https://apps.apple.com/app/id123456789',
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Set up listeners
|
|
89
|
+
this.setupUpdateListeners();
|
|
90
|
+
|
|
91
|
+
// Check for updates
|
|
92
|
+
await this.checkForUpdates();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private setupUpdateListeners() {
|
|
96
|
+
// Android flexible update state changes
|
|
97
|
+
CapacitorNativeUpdate.AppUpdate.addListener(
|
|
98
|
+
'flexibleUpdateStateChanged',
|
|
99
|
+
(state) => {
|
|
100
|
+
this.handleFlexibleUpdateState(state);
|
|
101
|
+
}
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
// Download progress for flexible updates
|
|
105
|
+
CapacitorNativeUpdate.AppUpdate.addListener(
|
|
106
|
+
'flexibleUpdateProgress',
|
|
107
|
+
(progress) => {
|
|
108
|
+
this.downloadProgress = progress.percent;
|
|
109
|
+
this.updateProgressUI(progress);
|
|
110
|
+
}
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async checkForUpdates() {
|
|
115
|
+
try {
|
|
116
|
+
this.updateInfo =
|
|
117
|
+
await CapacitorNativeUpdate.AppUpdate.getAppUpdateInfo();
|
|
118
|
+
|
|
119
|
+
if (!this.updateInfo.updateAvailable) {
|
|
120
|
+
console.log('App is up to date');
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Analyze update type needed
|
|
125
|
+
const updateType = this.determineUpdateType(this.updateInfo);
|
|
126
|
+
|
|
127
|
+
switch (updateType) {
|
|
128
|
+
case 'IMMEDIATE':
|
|
129
|
+
await this.performImmediateUpdate();
|
|
130
|
+
break;
|
|
131
|
+
case 'FLEXIBLE':
|
|
132
|
+
await this.startFlexibleUpdate();
|
|
133
|
+
break;
|
|
134
|
+
case 'OPTIONAL':
|
|
135
|
+
this.showOptionalUpdateUI();
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
} catch (error) {
|
|
139
|
+
this.handleUpdateError(error);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private determineUpdateType(info: AppUpdateInfo): string {
|
|
144
|
+
// Force immediate update for critical priority
|
|
145
|
+
if (info.updatePriority === 5) {
|
|
146
|
+
return 'IMMEDIATE';
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Check if current version is below minimum
|
|
150
|
+
if (this.isVersionBelowMinimum(info.currentVersion, info.minimumVersion)) {
|
|
151
|
+
return 'IMMEDIATE';
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// High priority gets flexible update
|
|
155
|
+
if (info.updatePriority >= 3) {
|
|
156
|
+
return 'FLEXIBLE';
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Low priority is optional
|
|
160
|
+
return 'OPTIONAL';
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async performImmediateUpdate() {
|
|
164
|
+
try {
|
|
165
|
+
// Show blocking update UI
|
|
166
|
+
this.showImmediateUpdateUI();
|
|
167
|
+
|
|
168
|
+
// Start immediate update
|
|
169
|
+
await CapacitorNativeUpdate.AppUpdate.performImmediateUpdate();
|
|
170
|
+
|
|
171
|
+
// App will restart automatically after update
|
|
172
|
+
} catch (error) {
|
|
173
|
+
if (error.code === 'UPDATE_CANCELLED') {
|
|
174
|
+
// User cancelled - handle based on requirements
|
|
175
|
+
if (this.updateInfo?.updatePriority === 5) {
|
|
176
|
+
// Critical update - exit app
|
|
177
|
+
App.exitApp();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async startFlexibleUpdate() {
|
|
184
|
+
try {
|
|
185
|
+
// Start background download
|
|
186
|
+
await CapacitorNativeUpdate.AppUpdate.startFlexibleUpdate();
|
|
187
|
+
|
|
188
|
+
// Show download progress UI
|
|
189
|
+
this.showFlexibleUpdateUI();
|
|
190
|
+
} catch (error) {
|
|
191
|
+
this.handleUpdateError(error);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private handleFlexibleUpdateState(state: FlexibleUpdateState) {
|
|
196
|
+
switch (state.status) {
|
|
197
|
+
case 'PENDING':
|
|
198
|
+
console.log('Update pending');
|
|
199
|
+
break;
|
|
200
|
+
|
|
201
|
+
case 'DOWNLOADING':
|
|
202
|
+
console.log('Downloading update...');
|
|
203
|
+
break;
|
|
204
|
+
|
|
205
|
+
case 'DOWNLOADED':
|
|
206
|
+
// Update ready to install
|
|
207
|
+
this.showInstallPrompt();
|
|
208
|
+
break;
|
|
209
|
+
|
|
210
|
+
case 'INSTALLING':
|
|
211
|
+
console.log('Installing update...');
|
|
212
|
+
break;
|
|
213
|
+
|
|
214
|
+
case 'INSTALLED':
|
|
215
|
+
console.log('Update installed successfully');
|
|
216
|
+
break;
|
|
217
|
+
|
|
218
|
+
case 'FAILED':
|
|
219
|
+
this.handleUpdateError(new Error(state.error));
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async completeFlexibleUpdate() {
|
|
225
|
+
try {
|
|
226
|
+
await CapacitorNativeUpdate.AppUpdate.completeFlexibleUpdate();
|
|
227
|
+
// App will restart with new version
|
|
228
|
+
} catch (error) {
|
|
229
|
+
console.error('Failed to complete update:', error);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
private showInstallPrompt() {
|
|
234
|
+
// Show snackbar or dialog
|
|
235
|
+
this.showDialog({
|
|
236
|
+
title: 'Update Ready',
|
|
237
|
+
message: 'An update has been downloaded. Restart to apply?',
|
|
238
|
+
buttons: [
|
|
239
|
+
{
|
|
240
|
+
text: 'Restart',
|
|
241
|
+
handler: () => this.completeFlexibleUpdate(),
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
text: 'Later',
|
|
245
|
+
handler: () => {
|
|
246
|
+
// Schedule reminder
|
|
247
|
+
this.scheduleUpdateReminder();
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
],
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Platform-Specific Behavior
|
|
257
|
+
|
|
258
|
+
### Android (Google Play)
|
|
259
|
+
|
|
260
|
+
Android uses the Google Play Core Library for in-app updates:
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
// Android-specific features
|
|
264
|
+
const androidUpdate = {
|
|
265
|
+
// Immediate update flow
|
|
266
|
+
immediate: async () => {
|
|
267
|
+
// Blocks UI until update is installed
|
|
268
|
+
await CapacitorNativeUpdate.AppUpdate.performImmediateUpdate();
|
|
269
|
+
// App restarts automatically
|
|
270
|
+
},
|
|
271
|
+
|
|
272
|
+
// Flexible update flow
|
|
273
|
+
flexible: async () => {
|
|
274
|
+
// Download in background
|
|
275
|
+
await CapacitorNativeUpdate.AppUpdate.startFlexibleUpdate();
|
|
276
|
+
|
|
277
|
+
// Monitor progress
|
|
278
|
+
const listener = await CapacitorNativeUpdate.AppUpdate.addListener(
|
|
279
|
+
'flexibleUpdateProgress',
|
|
280
|
+
(progress) => {
|
|
281
|
+
console.log(
|
|
282
|
+
`Downloaded: ${progress.bytesDownloaded}/${progress.totalBytes}`
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
// Complete when ready
|
|
288
|
+
await CapacitorNativeUpdate.AppUpdate.completeFlexibleUpdate();
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### iOS (App Store)
|
|
294
|
+
|
|
295
|
+
iOS requires manual version checking and App Store redirection:
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
// iOS-specific implementation
|
|
299
|
+
const iosUpdate = {
|
|
300
|
+
// Check version manually
|
|
301
|
+
check: async () => {
|
|
302
|
+
const info = await CapacitorNativeUpdate.AppUpdate.getAppUpdateInfo();
|
|
303
|
+
|
|
304
|
+
if (info.updateAvailable) {
|
|
305
|
+
// Show custom update dialog
|
|
306
|
+
const shouldUpdate = await showUpdateDialog(info);
|
|
307
|
+
|
|
308
|
+
if (shouldUpdate) {
|
|
309
|
+
// Open App Store
|
|
310
|
+
await CapacitorNativeUpdate.AppUpdate.openAppStore();
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
},
|
|
314
|
+
};
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### Web Platform
|
|
318
|
+
|
|
319
|
+
Web platforms show update notifications:
|
|
320
|
+
|
|
321
|
+
```typescript
|
|
322
|
+
// Web fallback
|
|
323
|
+
const webUpdate = {
|
|
324
|
+
notify: async () => {
|
|
325
|
+
const info = await CapacitorNativeUpdate.AppUpdate.getAppUpdateInfo();
|
|
326
|
+
|
|
327
|
+
if (info.updateAvailable) {
|
|
328
|
+
// Show notification
|
|
329
|
+
new Notification('Update Available', {
|
|
330
|
+
body: `Version ${info.availableVersion} is available`,
|
|
331
|
+
icon: '/icon.png',
|
|
332
|
+
actions: [
|
|
333
|
+
{
|
|
334
|
+
action: 'update',
|
|
335
|
+
title: 'Update Now',
|
|
336
|
+
},
|
|
337
|
+
],
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
};
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
## Update Priority Levels
|
|
345
|
+
|
|
346
|
+
Configure update behavior based on priority:
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
const updatePriorities = {
|
|
350
|
+
0: {
|
|
351
|
+
// Low priority
|
|
352
|
+
name: 'Optional',
|
|
353
|
+
behavior: 'Show notification only',
|
|
354
|
+
userControl: 'full',
|
|
355
|
+
},
|
|
356
|
+
1: {
|
|
357
|
+
// Minor improvements
|
|
358
|
+
name: 'Recommended',
|
|
359
|
+
behavior: 'Show dialog on app start',
|
|
360
|
+
userControl: 'dismissible',
|
|
361
|
+
},
|
|
362
|
+
2: {
|
|
363
|
+
// Important features
|
|
364
|
+
name: 'Important',
|
|
365
|
+
behavior: 'Prompt after key actions',
|
|
366
|
+
userControl: 'postponable',
|
|
367
|
+
},
|
|
368
|
+
3: {
|
|
369
|
+
// Significant changes
|
|
370
|
+
name: 'High',
|
|
371
|
+
behavior: 'Flexible update with reminders',
|
|
372
|
+
userControl: 'limited',
|
|
373
|
+
},
|
|
374
|
+
4: {
|
|
375
|
+
// Critical fixes
|
|
376
|
+
name: 'Critical',
|
|
377
|
+
behavior: 'Immediate update prompt',
|
|
378
|
+
userControl: 'minimal',
|
|
379
|
+
},
|
|
380
|
+
5: {
|
|
381
|
+
// Security updates
|
|
382
|
+
name: 'Mandatory',
|
|
383
|
+
behavior: 'Force immediate update',
|
|
384
|
+
userControl: 'none',
|
|
385
|
+
},
|
|
386
|
+
};
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
## Version Management
|
|
390
|
+
|
|
391
|
+
### Semantic Version Comparison
|
|
392
|
+
|
|
393
|
+
```typescript
|
|
394
|
+
class VersionManager {
|
|
395
|
+
// Compare semantic versions
|
|
396
|
+
compareVersions(v1: string, v2: string): number {
|
|
397
|
+
const parts1 = v1.split('.').map(Number);
|
|
398
|
+
const parts2 = v2.split('.').map(Number);
|
|
399
|
+
|
|
400
|
+
for (let i = 0; i < 3; i++) {
|
|
401
|
+
if (parts1[i] > parts2[i]) return 1;
|
|
402
|
+
if (parts1[i] < parts2[i]) return -1;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
return 0;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Check if update is major/minor/patch
|
|
409
|
+
getUpdateType(oldVersion: string, newVersion: string): string {
|
|
410
|
+
const [oldMajor, oldMinor, oldPatch] = oldVersion.split('.').map(Number);
|
|
411
|
+
const [newMajor, newMinor, newPatch] = newVersion.split('.').map(Number);
|
|
412
|
+
|
|
413
|
+
if (newMajor > oldMajor) return 'major';
|
|
414
|
+
if (newMinor > oldMinor) return 'minor';
|
|
415
|
+
if (newPatch > oldPatch) return 'patch';
|
|
416
|
+
return 'none';
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Determine update strategy based on version change
|
|
420
|
+
async determineStrategy(info: AppUpdateInfo) {
|
|
421
|
+
const updateType = this.getUpdateType(
|
|
422
|
+
info.currentVersion,
|
|
423
|
+
info.availableVersion
|
|
424
|
+
);
|
|
425
|
+
|
|
426
|
+
switch (updateType) {
|
|
427
|
+
case 'major':
|
|
428
|
+
// Major updates might need immediate update
|
|
429
|
+
return info.updatePriority >= 3 ? 'immediate' : 'flexible';
|
|
430
|
+
|
|
431
|
+
case 'minor':
|
|
432
|
+
// Minor updates are usually flexible
|
|
433
|
+
return 'flexible';
|
|
434
|
+
|
|
435
|
+
case 'patch':
|
|
436
|
+
// Patches can be optional unless critical
|
|
437
|
+
return info.updatePriority >= 4 ? 'flexible' : 'optional';
|
|
438
|
+
|
|
439
|
+
default:
|
|
440
|
+
return 'none';
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
### Minimum Version Enforcement
|
|
447
|
+
|
|
448
|
+
```typescript
|
|
449
|
+
// Enforce minimum version requirements
|
|
450
|
+
async function enforceMinimumVersion() {
|
|
451
|
+
const config = {
|
|
452
|
+
appUpdate: {
|
|
453
|
+
minimumVersion: '2.0.0',
|
|
454
|
+
forceUpdateBelow: '1.5.0', // Force update for very old versions
|
|
455
|
+
},
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
const info = await CapacitorNativeUpdate.AppUpdate.getAppUpdateInfo();
|
|
459
|
+
|
|
460
|
+
// Check if current version is below minimum
|
|
461
|
+
if (isVersionBelow(info.currentVersion, config.appUpdate.minimumVersion)) {
|
|
462
|
+
// Force immediate update
|
|
463
|
+
try {
|
|
464
|
+
await CapacitorNativeUpdate.AppUpdate.performImmediateUpdate();
|
|
465
|
+
} catch (error) {
|
|
466
|
+
// If update fails or is cancelled, restrict app usage
|
|
467
|
+
showMinimumVersionRequired();
|
|
468
|
+
disableAppFeatures();
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
## Custom Update UI
|
|
475
|
+
|
|
476
|
+
### Update Dialog Examples
|
|
477
|
+
|
|
478
|
+
```typescript
|
|
479
|
+
// Custom update dialog
|
|
480
|
+
class UpdateUI {
|
|
481
|
+
showUpdateDialog(info: AppUpdateInfo) {
|
|
482
|
+
const dialog = {
|
|
483
|
+
title: this.getUpdateTitle(info),
|
|
484
|
+
message: this.getUpdateMessage(info),
|
|
485
|
+
buttons: this.getUpdateButtons(info),
|
|
486
|
+
};
|
|
487
|
+
|
|
488
|
+
return this.presentDialog(dialog);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
private getUpdateTitle(info: AppUpdateInfo): string {
|
|
492
|
+
switch (info.updatePriority) {
|
|
493
|
+
case 5:
|
|
494
|
+
return '🚨 Critical Update Required';
|
|
495
|
+
case 4:
|
|
496
|
+
return '⚠️ Important Update Available';
|
|
497
|
+
case 3:
|
|
498
|
+
return '🎯 Recommended Update';
|
|
499
|
+
default:
|
|
500
|
+
return '✨ New Version Available';
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
private getUpdateMessage(info: AppUpdateInfo): string {
|
|
505
|
+
const size = this.formatBytes(info.updateSize);
|
|
506
|
+
const features = info.releaseNotes?.slice(0, 3).join('\n• ');
|
|
507
|
+
|
|
508
|
+
return `Version ${info.availableVersion} is available (${size})
|
|
509
|
+
|
|
510
|
+
What's new:
|
|
511
|
+
• ${features}
|
|
512
|
+
|
|
513
|
+
${this.getUpdateUrgency(info)}`;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
private getUpdateButtons(info: AppUpdateInfo): DialogButton[] {
|
|
517
|
+
if (info.updatePriority === 5) {
|
|
518
|
+
// Mandatory update - no cancel option
|
|
519
|
+
return [
|
|
520
|
+
{
|
|
521
|
+
text: 'Update Now',
|
|
522
|
+
handler: () => this.startUpdate(info),
|
|
523
|
+
},
|
|
524
|
+
];
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
return [
|
|
528
|
+
{
|
|
529
|
+
text: 'Update',
|
|
530
|
+
handler: () => this.startUpdate(info),
|
|
531
|
+
},
|
|
532
|
+
{
|
|
533
|
+
text: 'Later',
|
|
534
|
+
role: 'cancel',
|
|
535
|
+
handler: () => this.scheduleReminder(info),
|
|
536
|
+
},
|
|
537
|
+
];
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
### Progress Indicators
|
|
543
|
+
|
|
544
|
+
```typescript
|
|
545
|
+
// Update progress UI
|
|
546
|
+
class UpdateProgressUI {
|
|
547
|
+
private progressBar: HTMLElement;
|
|
548
|
+
private statusText: HTMLElement;
|
|
549
|
+
|
|
550
|
+
showProgress() {
|
|
551
|
+
this.createProgressUI();
|
|
552
|
+
|
|
553
|
+
CapacitorNativeUpdate.AppUpdate.addListener(
|
|
554
|
+
'flexibleUpdateProgress',
|
|
555
|
+
(progress) => {
|
|
556
|
+
this.updateProgress(progress);
|
|
557
|
+
}
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
private updateProgress(progress: UpdateProgress) {
|
|
562
|
+
// Update progress bar
|
|
563
|
+
this.progressBar.style.width = `${progress.percent}%`;
|
|
564
|
+
|
|
565
|
+
// Update status text
|
|
566
|
+
const downloaded = this.formatBytes(progress.bytesDownloaded);
|
|
567
|
+
const total = this.formatBytes(progress.totalBytes);
|
|
568
|
+
this.statusText.textContent = `Downloading: ${downloaded} / ${total}`;
|
|
569
|
+
|
|
570
|
+
// Show completion
|
|
571
|
+
if (progress.percent === 100) {
|
|
572
|
+
this.showCompletionUI();
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
private showCompletionUI() {
|
|
577
|
+
this.statusText.textContent = 'Update ready to install';
|
|
578
|
+
this.showInstallButton();
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
## Error Handling
|
|
584
|
+
|
|
585
|
+
### Common Errors and Solutions
|
|
586
|
+
|
|
587
|
+
```typescript
|
|
588
|
+
async function handleAppUpdateErrors() {
|
|
589
|
+
try {
|
|
590
|
+
await CapacitorNativeUpdate.AppUpdate.performImmediateUpdate();
|
|
591
|
+
} catch (error) {
|
|
592
|
+
switch (error.code) {
|
|
593
|
+
case 'UPDATE_NOT_AVAILABLE':
|
|
594
|
+
console.log('No update available');
|
|
595
|
+
break;
|
|
596
|
+
|
|
597
|
+
case 'UPDATE_CANCELLED':
|
|
598
|
+
// User cancelled the update
|
|
599
|
+
if (isCriticalUpdate()) {
|
|
600
|
+
showMandatoryUpdateMessage();
|
|
601
|
+
}
|
|
602
|
+
break;
|
|
603
|
+
|
|
604
|
+
case 'UPDATE_FAILED':
|
|
605
|
+
// Update failed to install
|
|
606
|
+
showRetryDialog();
|
|
607
|
+
break;
|
|
608
|
+
|
|
609
|
+
case 'PLATFORM_NOT_SUPPORTED':
|
|
610
|
+
// Feature not available on this platform
|
|
611
|
+
fallbackToManualUpdate();
|
|
612
|
+
break;
|
|
613
|
+
|
|
614
|
+
case 'PLAY_STORE_NOT_FOUND':
|
|
615
|
+
// Google Play Store not installed
|
|
616
|
+
showAlternativeUpdateMethod();
|
|
617
|
+
break;
|
|
618
|
+
|
|
619
|
+
case 'NETWORK_ERROR':
|
|
620
|
+
// No internet connection
|
|
621
|
+
showOfflineMessage();
|
|
622
|
+
scheduleRetryWhenOnline();
|
|
623
|
+
break;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
### Retry Strategies
|
|
630
|
+
|
|
631
|
+
```typescript
|
|
632
|
+
class UpdateRetryStrategy {
|
|
633
|
+
private retryCount = 0;
|
|
634
|
+
private maxRetries = 3;
|
|
635
|
+
|
|
636
|
+
async retryUpdate() {
|
|
637
|
+
try {
|
|
638
|
+
await CapacitorNativeUpdate.AppUpdate.startFlexibleUpdate();
|
|
639
|
+
this.retryCount = 0;
|
|
640
|
+
} catch (error) {
|
|
641
|
+
if (this.shouldRetry(error)) {
|
|
642
|
+
this.retryCount++;
|
|
643
|
+
const delay = this.calculateBackoff();
|
|
644
|
+
|
|
645
|
+
setTimeout(() => {
|
|
646
|
+
this.retryUpdate();
|
|
647
|
+
}, delay);
|
|
648
|
+
} else {
|
|
649
|
+
// Give up and show manual update option
|
|
650
|
+
this.showManualUpdateOption();
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
private calculateBackoff(): number {
|
|
656
|
+
// Exponential backoff: 2s, 4s, 8s
|
|
657
|
+
return Math.pow(2, this.retryCount) * 1000;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
## Testing App Updates
|
|
663
|
+
|
|
664
|
+
### Development Testing
|
|
665
|
+
|
|
666
|
+
```typescript
|
|
667
|
+
// Mock update info for testing
|
|
668
|
+
const mockUpdateInfo: AppUpdateInfo = {
|
|
669
|
+
updateAvailable: true,
|
|
670
|
+
currentVersion: '1.0.0',
|
|
671
|
+
availableVersion: '2.0.0',
|
|
672
|
+
updatePriority: 4,
|
|
673
|
+
updateSize: 15 * 1024 * 1024, // 15MB
|
|
674
|
+
releaseNotes: [
|
|
675
|
+
'New feature: Dark mode',
|
|
676
|
+
'Performance improvements',
|
|
677
|
+
'Bug fixes',
|
|
678
|
+
],
|
|
679
|
+
isFlexibleUpdateAllowed: true,
|
|
680
|
+
isImmediateUpdateAllowed: true,
|
|
681
|
+
};
|
|
682
|
+
|
|
683
|
+
// Test different scenarios
|
|
684
|
+
async function testUpdateScenarios() {
|
|
685
|
+
// Test immediate update
|
|
686
|
+
await testImmediateUpdate();
|
|
687
|
+
|
|
688
|
+
// Test flexible update
|
|
689
|
+
await testFlexibleUpdate();
|
|
690
|
+
|
|
691
|
+
// Test update cancellation
|
|
692
|
+
await testUpdateCancellation();
|
|
693
|
+
|
|
694
|
+
// Test network errors
|
|
695
|
+
await testNetworkErrors();
|
|
696
|
+
}
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
### Platform-Specific Testing
|
|
700
|
+
|
|
701
|
+
```bash
|
|
702
|
+
# Android testing with internal app sharing
|
|
703
|
+
# 1. Build APK with higher version number
|
|
704
|
+
# 2. Upload to Play Console internal app sharing
|
|
705
|
+
# 3. Install lower version on device
|
|
706
|
+
# 4. Test update flow
|
|
707
|
+
|
|
708
|
+
# iOS testing with TestFlight
|
|
709
|
+
# 1. Upload new version to TestFlight
|
|
710
|
+
# 2. Install older version
|
|
711
|
+
# 3. Test update detection and App Store redirect
|
|
712
|
+
```
|
|
713
|
+
|
|
714
|
+
## Best Practices
|
|
715
|
+
|
|
716
|
+
### 1. User Experience
|
|
717
|
+
|
|
718
|
+
```typescript
|
|
719
|
+
// Provide clear update information
|
|
720
|
+
const updateMessage = {
|
|
721
|
+
title: 'Exciting Update!',
|
|
722
|
+
features: [
|
|
723
|
+
'🎨 Beautiful new design',
|
|
724
|
+
'⚡ 2x faster performance',
|
|
725
|
+
'🐛 Bug fixes and improvements',
|
|
726
|
+
],
|
|
727
|
+
size: '12 MB',
|
|
728
|
+
time: 'Less than 1 minute',
|
|
729
|
+
};
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
### 2. Smart Scheduling
|
|
733
|
+
|
|
734
|
+
```typescript
|
|
735
|
+
// Update at appropriate times
|
|
736
|
+
class SmartUpdateScheduler {
|
|
737
|
+
async scheduleUpdate(info: AppUpdateInfo) {
|
|
738
|
+
// Don't interrupt critical user flows
|
|
739
|
+
if (this.isUserInCriticalFlow()) {
|
|
740
|
+
this.scheduleForLater();
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// Check device conditions
|
|
745
|
+
const conditions = await this.checkDeviceConditions();
|
|
746
|
+
|
|
747
|
+
if (conditions.isCharging && conditions.onWifi) {
|
|
748
|
+
// Ideal conditions - start update
|
|
749
|
+
await this.startUpdate();
|
|
750
|
+
} else {
|
|
751
|
+
// Wait for better conditions
|
|
752
|
+
this.waitForBetterConditions();
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
```
|
|
757
|
+
|
|
758
|
+
### 3. Analytics Integration
|
|
759
|
+
|
|
760
|
+
```typescript
|
|
761
|
+
// Track update metrics
|
|
762
|
+
async function trackUpdateMetrics(event: string, data: any) {
|
|
763
|
+
await analytics.track({
|
|
764
|
+
event: `app_update_${event}`,
|
|
765
|
+
properties: {
|
|
766
|
+
currentVersion: data.currentVersion,
|
|
767
|
+
availableVersion: data.availableVersion,
|
|
768
|
+
updatePriority: data.updatePriority,
|
|
769
|
+
userAction: data.userAction,
|
|
770
|
+
timestamp: Date.now(),
|
|
771
|
+
},
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
## Next Steps
|
|
777
|
+
|
|
778
|
+
- Configure [App Reviews](./app-reviews.md) for user feedback
|
|
779
|
+
- Implement [Security Best Practices](../guides/security-best-practices.md)
|
|
780
|
+
- Set up [Update Monitoring](../guides/monitoring.md)
|
|
781
|
+
- Review [API Reference](../api/app-update-api.md)
|
|
782
|
+
|
|
783
|
+
---
|
|
784
|
+
|
|
785
|
+
Made with ❤️ by Ahsan Mahmood
|