cordova-plugin-hot-updates 2.1.0 → 2.1.2
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 +332 -360
- package/package.json +4 -5
- package/plugin.xml +5 -10
- package/src/ios/HotUpdates.m +2 -2
- package/www/HotUpdates.js +162 -142
- package/CHANGELOG.md +0 -239
- package/docs/API.md +0 -352
- package/docs/README.md +0 -187
- package/docs/hot-updates-admin.html +0 -467
package/README.md
CHANGED
|
@@ -1,487 +1,459 @@
|
|
|
1
|
-
# Cordova Hot Updates Plugin
|
|
1
|
+
# Cordova Hot Updates Plugin v2.1.2
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Frontend-controlled manual hot updates for Cordova iOS applications using WebView Reload approach.
|
|
4
4
|
|
|
5
5
|
[](https://badge.fury.io/js/cordova-plugin-hot-updates)
|
|
6
6
|
[](#license)
|
|
7
7
|
|
|
8
|
-
This plugin enables
|
|
8
|
+
This plugin enables **manual, JavaScript-controlled** web content updates for your Cordova iOS applications without requiring App Store approval. Your frontend code decides when to check, download, and install updates.
|
|
9
9
|
|
|
10
|
-
##
|
|
10
|
+
## Features
|
|
11
11
|
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
12
|
+
- **Frontend Control**: JavaScript decides when to update (no automatic background checking)
|
|
13
|
+
- **Two-Step Updates**: Separate download (`getUpdate`) and install (`forceUpdate`) for better UX
|
|
14
|
+
- **Auto-Install on Launch**: If user ignores update prompt, it installs on next app launch
|
|
15
|
+
- **Canary System**: Automatic rollback if update fails to load (20-second timeout)
|
|
16
|
+
- **IgnoreList**: Tracks problematic versions (information only, does NOT block installation)
|
|
17
|
+
- **Instant Effect**: WebView Reload approach - no app restart needed
|
|
18
|
+
- **Cache Management**: Clears WKWebView cache (disk, memory, Service Worker) before reload
|
|
18
19
|
|
|
19
|
-
##
|
|
20
|
-
|
|
21
|
-
- [Installation](#-installation)
|
|
22
|
-
- [Quick Start](#-quick-start)
|
|
23
|
-
- [Configuration](#-configuration)
|
|
24
|
-
- [API Reference](#-api-reference)
|
|
25
|
-
- [Server Implementation](#-server-implementation)
|
|
26
|
-
- [How It Works](#-how-it-works)
|
|
27
|
-
- [Troubleshooting](#-troubleshooting)
|
|
28
|
-
- [Contributing](#-contributing)
|
|
29
|
-
- [License](#-license)
|
|
30
|
-
|
|
31
|
-
## 📦 Installation
|
|
32
|
-
|
|
33
|
-
### Standard Installation (Recommended)
|
|
20
|
+
## Installation
|
|
34
21
|
|
|
35
22
|
```bash
|
|
36
|
-
# Install from npm
|
|
23
|
+
# Install from npm
|
|
37
24
|
cordova plugin add cordova-plugin-hot-updates
|
|
38
25
|
|
|
39
|
-
# Install CocoaPods dependencies (
|
|
26
|
+
# Install CocoaPods dependencies (required)
|
|
40
27
|
cd platforms/ios
|
|
41
28
|
pod install
|
|
42
29
|
```
|
|
43
30
|
|
|
44
|
-
|
|
31
|
+
Or install from local directory:
|
|
45
32
|
|
|
46
33
|
```bash
|
|
47
|
-
# Install from GitHub
|
|
48
|
-
cordova plugin add https://github.com/vladimirDarksy/Cordova_hot_update.git
|
|
49
|
-
|
|
50
|
-
# Install from local path
|
|
51
34
|
cordova plugin add /path/to/cordova-plugin-hot-updates
|
|
35
|
+
```
|
|
52
36
|
|
|
53
|
-
|
|
54
|
-
|
|
37
|
+
Or install from GitHub:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
cordova plugin add https://github.com/vladimirDarksy/Cordova_hot_update.git
|
|
55
41
|
```
|
|
56
42
|
|
|
57
|
-
|
|
43
|
+
**Requirements:**
|
|
44
|
+
- Cordova >= 7.0.0
|
|
45
|
+
- cordova-ios >= 4.4.0
|
|
46
|
+
- iOS >= 11.2
|
|
47
|
+
- CocoaPods (for SSZipArchive dependency)
|
|
58
48
|
|
|
59
|
-
|
|
49
|
+
## Quick Start
|
|
60
50
|
|
|
61
|
-
1.
|
|
51
|
+
### 1. Minimal Integration
|
|
62
52
|
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
53
|
+
```javascript
|
|
54
|
+
document.addEventListener('deviceready', function() {
|
|
55
|
+
// CRITICAL: Confirm successful bundle load within 20 seconds
|
|
56
|
+
var currentVersion = localStorage.getItem('app_version') || '1.0.0';
|
|
57
|
+
window.hotUpdate.canary(currentVersion);
|
|
58
|
+
|
|
59
|
+
// Check for updates
|
|
60
|
+
checkForUpdates();
|
|
61
|
+
}, false);
|
|
62
|
+
|
|
63
|
+
function checkForUpdates() {
|
|
64
|
+
fetch('https://your-server.com/api/check-update?version=1.0.0')
|
|
65
|
+
.then(response => response.json())
|
|
66
|
+
.then(data => {
|
|
67
|
+
if (data.hasUpdate) {
|
|
68
|
+
downloadAndInstall(data.downloadUrl, data.version);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function downloadAndInstall(url, version) {
|
|
74
|
+
window.hotUpdate.getUpdate({url: url, version: version}, function(error) {
|
|
75
|
+
if (!error) {
|
|
76
|
+
if (confirm('Update available. Install now?')) {
|
|
77
|
+
localStorage.setItem('app_version', version);
|
|
78
|
+
window.hotUpdate.forceUpdate(function(error) {
|
|
79
|
+
// WebView will reload, canary() will be called in deviceready
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
// If user declines, update auto-installs on next launch
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
67
86
|
```
|
|
68
87
|
|
|
69
|
-
|
|
88
|
+
## API Reference
|
|
70
89
|
|
|
71
|
-
|
|
72
|
-
cordova plugin add cordova-plugin-hot-updates
|
|
73
|
-
```
|
|
90
|
+
All API methods are available via `window.hotUpdate` after the `deviceready` event.
|
|
74
91
|
|
|
75
|
-
|
|
92
|
+
### window.hotUpdate.getUpdate(options, callback)
|
|
76
93
|
|
|
77
|
-
|
|
94
|
+
Downloads update from server.
|
|
78
95
|
|
|
79
|
-
|
|
96
|
+
Downloads ZIP from provided URL and saves to two locations:
|
|
97
|
+
- `temp_downloaded_update` (for immediate installation via `forceUpdate()`)
|
|
98
|
+
- `pending_update` (for auto-installation on next app launch)
|
|
80
99
|
|
|
81
|
-
|
|
100
|
+
If version already downloaded, returns success without re-downloading.
|
|
82
101
|
|
|
83
|
-
|
|
84
|
-
<preference name="hot_updates_server_url" value="https://your-server.com/api/updates" />
|
|
85
|
-
<preference name="hot_updates_check_interval" value="300000" />
|
|
86
|
-
```
|
|
102
|
+
**Does NOT check ignoreList** - JavaScript controls all installation decisions.
|
|
87
103
|
|
|
88
|
-
|
|
104
|
+
**Parameters:**
|
|
105
|
+
- `options` (Object):
|
|
106
|
+
- `url` (string, required) - URL to download ZIP archive
|
|
107
|
+
- `version` (string, optional) - Version string
|
|
108
|
+
- `callback` (Function) - `callback(error)`
|
|
109
|
+
- `null` on success
|
|
110
|
+
- `{error: {message?: string}}` on error
|
|
89
111
|
|
|
112
|
+
**Example:**
|
|
90
113
|
```javascript
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
// Check for pending updates
|
|
102
|
-
HotUpdates.getPendingUpdateInfo(
|
|
103
|
-
function(info) {
|
|
104
|
-
if (info.hasPendingUpdate) {
|
|
105
|
-
console.log('Update ready:', info.pendingVersion);
|
|
106
|
-
alert('New update will be applied on next app restart');
|
|
114
|
+
window.hotUpdate.getUpdate({
|
|
115
|
+
url: 'https://your-server.com/updates/2.0.0.zip',
|
|
116
|
+
version: '2.0.0'
|
|
117
|
+
}, function(error) {
|
|
118
|
+
if (error) {
|
|
119
|
+
console.error('Download failed:', error);
|
|
120
|
+
} else {
|
|
121
|
+
console.log('Update downloaded successfully');
|
|
107
122
|
}
|
|
108
|
-
|
|
109
|
-
function(error) {
|
|
110
|
-
console.error('Error:', error);
|
|
111
|
-
}
|
|
112
|
-
);
|
|
113
|
-
|
|
114
|
-
// Manually check for updates
|
|
115
|
-
HotUpdates.checkForUpdates(
|
|
116
|
-
function(result) {
|
|
117
|
-
if (result.hasUpdate) {
|
|
118
|
-
console.log('Update available:', result.availableVersion);
|
|
119
|
-
// Optionally download manually
|
|
120
|
-
HotUpdates.downloadUpdate(
|
|
121
|
-
result.downloadURL,
|
|
122
|
-
result.availableVersion,
|
|
123
|
-
function() {
|
|
124
|
-
console.log('Download completed');
|
|
125
|
-
},
|
|
126
|
-
function(error) {
|
|
127
|
-
console.error('Download failed:', error);
|
|
128
|
-
}
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
},
|
|
132
|
-
function(error) {
|
|
133
|
-
console.error('Check failed:', error);
|
|
134
|
-
}
|
|
135
|
-
);
|
|
123
|
+
});
|
|
136
124
|
```
|
|
137
125
|
|
|
138
|
-
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
### window.hotUpdate.forceUpdate(callback)
|
|
129
|
+
|
|
130
|
+
Installs downloaded update immediately and reloads WebView.
|
|
139
131
|
|
|
140
|
-
|
|
132
|
+
**Process:**
|
|
133
|
+
1. Backup current version to `www_previous`
|
|
134
|
+
2. Copy downloaded update to `Documents/www`
|
|
135
|
+
3. Clear WebView cache (disk, memory, Service Worker)
|
|
136
|
+
4. Reload WebView
|
|
137
|
+
5. Start 20-second canary timer
|
|
141
138
|
|
|
142
|
-
|
|
143
|
-
|------------|---------|-------------|
|
|
144
|
-
| `hot_updates_server_url` | `https://your-server.com/api/updates` | Update server endpoint |
|
|
145
|
-
| `hot_updates_check_interval` | `300000` | Check interval in milliseconds (5 minutes) |
|
|
146
|
-
| `hot_updates_auto_download` | `true` | Enable automatic download |
|
|
147
|
-
| `hot_updates_auto_install` | `true` | Enable automatic installation on restart |
|
|
139
|
+
**IMPORTANT:** JavaScript MUST call `canary(version)` within 20 seconds after reload to confirm successful bundle load. Otherwise automatic rollback occurs.
|
|
148
140
|
|
|
149
|
-
|
|
141
|
+
**Does NOT check ignoreList** - JavaScript decides what to install.
|
|
150
142
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
<preference name="hot_updates_check_interval" value="600000" />
|
|
156
|
-
<preference name="hot_updates_auto_download" value="true" />
|
|
157
|
-
<preference name="hot_updates_auto_install" value="true" />
|
|
143
|
+
**Parameters:**
|
|
144
|
+
- `callback` (Function) - `callback(error)`
|
|
145
|
+
- `null` on success (before WebView reload)
|
|
146
|
+
- `{error: {message?: string}}` on error
|
|
158
147
|
|
|
159
|
-
|
|
160
|
-
|
|
148
|
+
**Example:**
|
|
149
|
+
```javascript
|
|
150
|
+
window.hotUpdate.forceUpdate(function(error) {
|
|
151
|
+
if (error) {
|
|
152
|
+
console.error('Install failed:', error);
|
|
153
|
+
} else {
|
|
154
|
+
console.log('Update installing, WebView will reload...');
|
|
155
|
+
}
|
|
156
|
+
});
|
|
161
157
|
```
|
|
162
158
|
|
|
163
|
-
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
### window.hotUpdate.canary(version, callback)
|
|
162
|
+
|
|
163
|
+
Confirms successful bundle load after update.
|
|
164
164
|
|
|
165
|
-
|
|
165
|
+
**MUST be called within 20 seconds** after `forceUpdate()` to stop canary timer and prevent automatic rollback.
|
|
166
166
|
|
|
167
|
-
|
|
167
|
+
**If not called within 20 seconds:**
|
|
168
|
+
- Automatic rollback to previous version
|
|
169
|
+
- Failed version added to ignoreList
|
|
170
|
+
- WebView reloaded with previous version
|
|
168
171
|
|
|
169
|
-
|
|
172
|
+
**Parameters:**
|
|
173
|
+
- `version` (string) - Version that loaded successfully
|
|
174
|
+
- `callback` (Function, optional) - Not used, method is synchronous
|
|
170
175
|
|
|
176
|
+
**Example:**
|
|
171
177
|
```javascript
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
function(error) {
|
|
177
|
-
console.error('Error getting version:', error);
|
|
178
|
-
}
|
|
179
|
-
);
|
|
178
|
+
document.addEventListener('deviceready', function() {
|
|
179
|
+
var version = localStorage.getItem('app_version') || '1.0.0';
|
|
180
|
+
window.hotUpdate.canary(version);
|
|
181
|
+
}, false);
|
|
180
182
|
```
|
|
181
183
|
|
|
182
|
-
|
|
184
|
+
---
|
|
183
185
|
|
|
184
|
-
|
|
186
|
+
### window.hotUpdate.getIgnoreList(callback)
|
|
185
187
|
|
|
186
|
-
|
|
187
|
-
HotUpdates.getPendingUpdateInfo(
|
|
188
|
-
function(info) {
|
|
189
|
-
// info object contains:
|
|
190
|
-
// - hasPendingUpdate: boolean
|
|
191
|
-
// - pendingVersion: string
|
|
192
|
-
// - appBundleVersion: string
|
|
193
|
-
// - installedVersion: string
|
|
194
|
-
// - message: string
|
|
195
|
-
},
|
|
196
|
-
function(error) {
|
|
197
|
-
console.error('Error getting update info:', error);
|
|
198
|
-
}
|
|
199
|
-
);
|
|
200
|
-
```
|
|
188
|
+
Returns list of problematic versions (information only).
|
|
201
189
|
|
|
202
|
-
|
|
190
|
+
**This is an INFORMATION-ONLY system** - native does NOT block installation. JavaScript should read this list and decide whether to skip these versions.
|
|
203
191
|
|
|
204
|
-
|
|
192
|
+
Native automatically adds versions to this list when rollback occurs.
|
|
205
193
|
|
|
194
|
+
**Parameters:**
|
|
195
|
+
- `callback` (Function) - `callback(result)`
|
|
196
|
+
- `result`: `{versions: string[]}` - Array of problematic version strings
|
|
197
|
+
|
|
198
|
+
**Example:**
|
|
206
199
|
```javascript
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
// - minAppVersion: string (if specified)
|
|
215
|
-
},
|
|
216
|
-
function(error) {
|
|
217
|
-
console.error('Update check failed:', error);
|
|
218
|
-
}
|
|
219
|
-
);
|
|
200
|
+
window.hotUpdate.getIgnoreList(function(result) {
|
|
201
|
+
console.log('Problematic versions:', result.versions);
|
|
202
|
+
|
|
203
|
+
if (result.versions.includes(newVersion)) {
|
|
204
|
+
console.log('Skipping known problematic version');
|
|
205
|
+
}
|
|
206
|
+
});
|
|
220
207
|
```
|
|
221
208
|
|
|
222
|
-
|
|
209
|
+
---
|
|
223
210
|
|
|
224
|
-
|
|
211
|
+
## Complete Update Flow
|
|
225
212
|
|
|
226
213
|
```javascript
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
214
|
+
// Step 1: Check for updates on your server
|
|
215
|
+
function checkForUpdates() {
|
|
216
|
+
var currentVersion = localStorage.getItem('app_version') || '1.0.0';
|
|
217
|
+
|
|
218
|
+
fetch('https://your-server.com/api/check-update?version=' + currentVersion)
|
|
219
|
+
.then(response => response.json())
|
|
220
|
+
.then(data => {
|
|
221
|
+
if (data.hasUpdate) {
|
|
222
|
+
// Step 2: Check ignoreList
|
|
223
|
+
window.hotUpdate.getIgnoreList(function(ignoreList) {
|
|
224
|
+
if (ignoreList.versions.includes(data.version)) {
|
|
225
|
+
console.log('Skipping problematic version');
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Step 3: Download
|
|
230
|
+
window.hotUpdate.getUpdate({
|
|
231
|
+
url: data.downloadUrl,
|
|
232
|
+
version: data.version
|
|
233
|
+
}, function(error) {
|
|
234
|
+
if (!error) {
|
|
235
|
+
// Step 4: Prompt user
|
|
236
|
+
if (confirm('Update available. Install now?')) {
|
|
237
|
+
// Save version for canary check
|
|
238
|
+
localStorage.setItem('app_version', data.version);
|
|
239
|
+
|
|
240
|
+
// Step 5: Install
|
|
241
|
+
window.hotUpdate.forceUpdate(function(error) {
|
|
242
|
+
// WebView will reload
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
// If declined, auto-installs on next launch
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Step 6: After reload, confirm success
|
|
254
|
+
document.addEventListener('deviceready', function() {
|
|
255
|
+
var version = localStorage.getItem('app_version') || '1.0.0';
|
|
256
|
+
window.hotUpdate.canary(version); // Must call within 20 seconds!
|
|
257
|
+
|
|
258
|
+
initApp();
|
|
259
|
+
}, false);
|
|
240
260
|
```
|
|
241
261
|
|
|
242
|
-
|
|
262
|
+
## How It Works
|
|
243
263
|
|
|
244
|
-
|
|
264
|
+
### Update Flow
|
|
265
|
+
|
|
266
|
+
1. **Download** (`getUpdate()`):
|
|
267
|
+
- Downloads ZIP from URL
|
|
268
|
+
- Validates `www` folder structure
|
|
269
|
+
- Saves to TWO locations:
|
|
270
|
+
- `temp_downloaded_update` (for immediate install)
|
|
271
|
+
- `pending_update` (for auto-install on next launch)
|
|
272
|
+
|
|
273
|
+
2. **Installation Options**:
|
|
274
|
+
- **Immediate**: User clicks "Update" → `forceUpdate()` installs now
|
|
275
|
+
- **Deferred**: User ignores → Auto-installs on next app launch
|
|
276
|
+
|
|
277
|
+
3. **Rollback Protection**:
|
|
278
|
+
- Previous version backed up before installation
|
|
279
|
+
- 20-second canary timer starts after reload
|
|
280
|
+
- If `canary()` not called → automatic rollback
|
|
281
|
+
- Failed version added to ignoreList
|
|
282
|
+
|
|
283
|
+
4. **IgnoreList System**:
|
|
284
|
+
- Native tracks failed versions
|
|
285
|
+
- JavaScript reads via `getIgnoreList()`
|
|
286
|
+
- **Does NOT block** - JS decides what to install
|
|
287
|
+
|
|
288
|
+
### Storage Structure
|
|
245
289
|
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
// - appBundleVersion: string
|
|
253
|
-
// - autoDownload: boolean
|
|
254
|
-
},
|
|
255
|
-
function(error) {
|
|
256
|
-
console.error('Error getting config:', error);
|
|
257
|
-
}
|
|
258
|
-
);
|
|
290
|
+
```
|
|
291
|
+
Documents/
|
|
292
|
+
├── www/ // Active version
|
|
293
|
+
├── www_previous/ // Previous version (rollback)
|
|
294
|
+
├── pending_update/ // Next launch auto-install
|
|
295
|
+
└── temp_downloaded_update/ // Immediate install
|
|
259
296
|
```
|
|
260
297
|
|
|
261
|
-
|
|
298
|
+
### Version Management
|
|
262
299
|
|
|
263
|
-
|
|
300
|
+
- **appBundleVersion** - Native app version from Info.plist
|
|
301
|
+
- **installedVersion** - Current hot update version
|
|
302
|
+
- **previousVersion** - Last working version (rollback)
|
|
264
303
|
|
|
265
|
-
|
|
304
|
+
## Update Server API
|
|
266
305
|
|
|
267
|
-
|
|
306
|
+
Your server should provide:
|
|
268
307
|
|
|
269
|
-
**
|
|
270
|
-
```json
|
|
271
|
-
{
|
|
272
|
-
"hasUpdate": true,
|
|
273
|
-
"version": "1.2.0",
|
|
274
|
-
"downloadURL": "https://yourserver.com/updates/v1.2.0.zip",
|
|
275
|
-
"minAppVersion": "1.0.0",
|
|
276
|
-
"releaseNotes": "Bug fixes and improvements"
|
|
277
|
-
}
|
|
308
|
+
**Check API:**
|
|
278
309
|
```
|
|
310
|
+
GET https://your-server.com/api/check-update?version=1.0.0&platform=ios
|
|
279
311
|
|
|
280
|
-
|
|
281
|
-
```json
|
|
312
|
+
Response:
|
|
282
313
|
{
|
|
283
|
-
"hasUpdate":
|
|
284
|
-
"
|
|
314
|
+
"hasUpdate": true,
|
|
315
|
+
"version": "2.0.0",
|
|
316
|
+
"downloadUrl": "https://your-server.com/updates/2.0.0.zip",
|
|
317
|
+
"minAppVersion": "2.7.0",
|
|
318
|
+
"releaseNotes": "Bug fixes"
|
|
285
319
|
}
|
|
286
320
|
```
|
|
287
321
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
Your update ZIP file should contain a `www` folder with your web content:
|
|
291
|
-
|
|
322
|
+
**Update ZIP Structure:**
|
|
292
323
|
```
|
|
293
324
|
update.zip
|
|
294
325
|
└── www/
|
|
295
326
|
├── index.html
|
|
296
327
|
├── js/
|
|
297
328
|
├── css/
|
|
298
|
-
├── img/
|
|
299
329
|
└── ...
|
|
300
330
|
```
|
|
301
331
|
|
|
302
|
-
|
|
332
|
+
## Best Practices
|
|
303
333
|
|
|
304
|
-
|
|
305
|
-
const express = require('express');
|
|
306
|
-
const app = express();
|
|
307
|
-
|
|
308
|
-
app.get('/api/updates/check', (req, res) => {
|
|
309
|
-
const { version, platform } = req.query;
|
|
310
|
-
const currentVersion = version || '1.0.0';
|
|
311
|
-
const latestVersion = '1.2.0'; // Your latest version
|
|
312
|
-
|
|
313
|
-
if (compareVersions(currentVersion, latestVersion) < 0) {
|
|
314
|
-
res.json({
|
|
315
|
-
hasUpdate: true,
|
|
316
|
-
version: latestVersion,
|
|
317
|
-
downloadURL: `https://yourserver.com/updates/v${latestVersion}.zip`,
|
|
318
|
-
minAppVersion: '1.0.0',
|
|
319
|
-
releaseNotes: 'Bug fixes and improvements'
|
|
320
|
-
});
|
|
321
|
-
} else {
|
|
322
|
-
res.json({
|
|
323
|
-
hasUpdate: false,
|
|
324
|
-
message: 'No updates available'
|
|
325
|
-
});
|
|
326
|
-
}
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
function compareVersions(version1, version2) {
|
|
330
|
-
const v1parts = version1.split('.').map(Number);
|
|
331
|
-
const v2parts = version2.split('.').map(Number);
|
|
332
|
-
|
|
333
|
-
for (let i = 0; i < Math.max(v1parts.length, v2parts.length); i++) {
|
|
334
|
-
const v1part = v1parts[i] || 0;
|
|
335
|
-
const v2part = v2parts[i] || 0;
|
|
336
|
-
|
|
337
|
-
if (v1part < v2part) return -1;
|
|
338
|
-
if (v1part > v2part) return 1;
|
|
339
|
-
}
|
|
334
|
+
### 1. Always Call Canary
|
|
340
335
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
});
|
|
336
|
+
```javascript
|
|
337
|
+
document.addEventListener('deviceready', function() {
|
|
338
|
+
var version = localStorage.getItem('app_version');
|
|
339
|
+
window.hotUpdate.canary(version); // Within 20 seconds!
|
|
340
|
+
}, false);
|
|
347
341
|
```
|
|
348
342
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
### WebView Reload Approach
|
|
343
|
+
### 2. Check IgnoreList
|
|
352
344
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
### File Structure
|
|
360
|
-
|
|
361
|
-
```
|
|
362
|
-
Documents/
|
|
363
|
-
├── www/ # Updated web content (active)
|
|
364
|
-
├── pending_update/ # Downloaded update waiting for installation
|
|
365
|
-
│ └── www/ # New web content
|
|
366
|
-
└── www_backup/ # Backup of previous version (for rollback)
|
|
345
|
+
```javascript
|
|
346
|
+
window.hotUpdate.getIgnoreList(function(result) {
|
|
347
|
+
if (result.versions.includes(newVersion)) {
|
|
348
|
+
console.log('Known problematic version');
|
|
349
|
+
}
|
|
350
|
+
});
|
|
367
351
|
```
|
|
368
352
|
|
|
369
|
-
###
|
|
353
|
+
### 3. Handle Errors
|
|
370
354
|
|
|
355
|
+
```javascript
|
|
356
|
+
window.hotUpdate.getUpdate(options, function(error) {
|
|
357
|
+
if (error) {
|
|
358
|
+
analytics.track('update_failed', {error: error.message});
|
|
359
|
+
showUserMessage('Update failed');
|
|
360
|
+
}
|
|
361
|
+
});
|
|
371
362
|
```
|
|
372
|
-
[Bundle] -> [Check] -> [Download] -> [Prepare] -> [Install] -> [Reload]
|
|
373
|
-
↑ ↓
|
|
374
|
-
└─────────────────── [Next App Launch] ←──────────────────────┘
|
|
375
|
-
```
|
|
376
|
-
|
|
377
|
-
## 🐛 Troubleshooting
|
|
378
363
|
|
|
379
|
-
###
|
|
364
|
+
### 4. Store Version
|
|
380
365
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
- **Git installation**: Use full GitHub URL: `cordova plugin add https://github.com/vladimirDarksy/Cordova_hot_update.git`
|
|
385
|
-
- **Verify installation**: Run `cordova plugin list` to check if plugin is installed
|
|
386
|
-
|
|
387
|
-
**pnpm + Cordova compatibility issues**
|
|
388
|
-
If you're using pnpm and Cordova can't find the plugin:
|
|
389
|
-
|
|
390
|
-
1. Add to your project's `.npmrc`:
|
|
391
|
-
```
|
|
392
|
-
public-hoist-pattern[]=cordova-plugin-*
|
|
393
|
-
shamefully-hoist=true
|
|
394
|
-
```
|
|
395
|
-
|
|
396
|
-
2. Remove and reinstall:
|
|
397
|
-
```bash
|
|
398
|
-
cordova plugin remove cordova-plugin-hot-updates
|
|
399
|
-
rm -rf node_modules
|
|
400
|
-
pnpm install
|
|
401
|
-
cordova plugin add cordova-plugin-hot-updates
|
|
402
|
-
```
|
|
366
|
+
```javascript
|
|
367
|
+
// Before forceUpdate
|
|
368
|
+
localStorage.setItem('app_version', newVersion);
|
|
403
369
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
370
|
+
// After reload
|
|
371
|
+
var version = localStorage.getItem('app_version');
|
|
372
|
+
window.hotUpdate.canary(version);
|
|
373
|
+
```
|
|
408
374
|
|
|
409
|
-
|
|
410
|
-
- Check your server URL in config.xml
|
|
411
|
-
- Verify server is returning correct JSON format
|
|
412
|
-
- Check device network connectivity
|
|
413
|
-
- Enable debugging with `cordova run ios --device --debug`
|
|
375
|
+
## Troubleshooting
|
|
414
376
|
|
|
415
|
-
|
|
416
|
-
- Ensure `Documents/www/index.html` exists
|
|
417
|
-
- Check iOS device logs for WebView errors
|
|
418
|
-
- Verify ZIP package contains `www` folder
|
|
377
|
+
### Update doesn't install
|
|
419
378
|
|
|
420
|
-
|
|
421
|
-
-
|
|
422
|
-
-
|
|
423
|
-
- Clean and rebuild: `cordova clean ios && cordova build ios`
|
|
379
|
+
- Check ZIP structure (must have `www/` folder)
|
|
380
|
+
- Check URL accessibility
|
|
381
|
+
- Check Xcode console: `[HotUpdates] ...`
|
|
424
382
|
|
|
425
|
-
###
|
|
383
|
+
### Automatic rollback
|
|
426
384
|
|
|
427
|
-
|
|
385
|
+
**Cause:** `canary()` not called within 20 seconds
|
|
428
386
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
# Use Xcode console or Safari Web Inspector
|
|
387
|
+
**Solution:** Call immediately in `deviceready`:
|
|
388
|
+
```javascript
|
|
389
|
+
document.addEventListener('deviceready', function() {
|
|
390
|
+
window.hotUpdate.canary(version); // First thing!
|
|
391
|
+
}, false);
|
|
435
392
|
```
|
|
436
393
|
|
|
437
|
-
###
|
|
394
|
+
### window.hotUpdate is undefined
|
|
395
|
+
|
|
396
|
+
**Cause:** Called before `deviceready`
|
|
438
397
|
|
|
398
|
+
**Solution:**
|
|
439
399
|
```javascript
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
localStorage.removeItem('hot_updates_has_pending');
|
|
400
|
+
document.addEventListener('deviceready', function() {
|
|
401
|
+
console.log(window.hotUpdate); // Now available
|
|
402
|
+
}, false);
|
|
444
403
|
```
|
|
445
404
|
|
|
446
|
-
##
|
|
405
|
+
## Migration from v1.0.0
|
|
447
406
|
|
|
448
|
-
|
|
449
|
-
-
|
|
450
|
-
-
|
|
451
|
-
-
|
|
452
|
-
-
|
|
407
|
+
**Removed methods:**
|
|
408
|
+
- `getCurrentVersion()` - Manage in JS
|
|
409
|
+
- `getPendingUpdateInfo()` - Not needed
|
|
410
|
+
- `checkForUpdates()` - Frontend controls
|
|
411
|
+
- `downloadUpdate()` - Use `getUpdate()`
|
|
412
|
+
- `installUpdate()` - Use `forceUpdate()`
|
|
453
413
|
|
|
454
|
-
|
|
414
|
+
**New API:**
|
|
415
|
+
- `window.hotUpdate.getUpdate({url, version?}, callback)`
|
|
416
|
+
- `window.hotUpdate.forceUpdate(callback)`
|
|
417
|
+
- `window.hotUpdate.canary(version, callback)`
|
|
418
|
+
- `window.hotUpdate.getIgnoreList(callback)`
|
|
455
419
|
|
|
456
|
-
|
|
457
|
-
-
|
|
458
|
-
-
|
|
459
|
-
-
|
|
420
|
+
**Changes:**
|
|
421
|
+
- API via `window.hotUpdate` (not `window.HotUpdates`)
|
|
422
|
+
- Callback signature: `callback(error)` pattern
|
|
423
|
+
- No automatic background checking
|
|
460
424
|
|
|
461
|
-
##
|
|
425
|
+
## Changelog
|
|
462
426
|
|
|
463
|
-
|
|
427
|
+
### v2.1.2 (2025-11-13)
|
|
464
428
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
5. Open a Pull Request
|
|
429
|
+
**Breaking Changes:**
|
|
430
|
+
- Changed API from `window.HotUpdates` to `window.hotUpdate`
|
|
431
|
+
- Removed automatic update checking
|
|
432
|
+
- Simplified to 4 methods: `getUpdate`, `forceUpdate`, `canary`, `getIgnoreList`
|
|
470
433
|
|
|
471
|
-
|
|
434
|
+
**New Features:**
|
|
435
|
+
- Frontend-controlled manual updates
|
|
436
|
+
- Two-step update flow
|
|
437
|
+
- 20-second canary timer
|
|
438
|
+
- IgnoreList system
|
|
439
|
+
- Auto-install on next launch
|
|
440
|
+
- WebView cache clearing
|
|
472
441
|
|
|
473
|
-
|
|
442
|
+
### v1.0.0
|
|
474
443
|
|
|
475
|
-
|
|
444
|
+
- Initial release
|
|
476
445
|
|
|
477
|
-
|
|
446
|
+
## License
|
|
478
447
|
|
|
479
|
-
|
|
448
|
+
Custom Non-Commercial License - See [LICENSE](LICENSE) file
|
|
480
449
|
|
|
481
|
-
|
|
482
|
-
- **Documentation**: This README and inline code documentation
|
|
483
|
-
- **Discussions**: [GitHub Discussions](https://github.com/vladimirDarksy/Cordova_hot_update/discussions)
|
|
450
|
+
## Author
|
|
484
451
|
|
|
485
|
-
|
|
452
|
+
**Mustafin Vladimir**
|
|
453
|
+
- GitHub: [@vladimirDarksy](https://github.com/vladimirDarksy)
|
|
454
|
+
- Email: outvova.gor@gmail.com
|
|
455
|
+
|
|
456
|
+
## Support
|
|
486
457
|
|
|
487
|
-
|
|
458
|
+
- Issues: https://github.com/vladimirDarksy/Cordova_hot_update/issues
|
|
459
|
+
- Repository: https://github.com/vladimirDarksy/Cordova_hot_update
|