@capawesome/capacitor-live-update 8.2.3 → 8.3.0-dev.f9cc1d15.1783929784
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/CapawesomeCapacitorLiveUpdate.podspec +16 -1
- package/Package.swift +22 -6
- package/README.md +213 -84
- package/android/build.gradle +17 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/LiveUpdate.java +69 -20
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/LiveUpdatePlugin.java +30 -1
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/classes/api/GetLatestBundleResponse.java +9 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/classes/options/FetchLatestBundleOptions.java +15 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/classes/results/FetchLatestBundleResult.java +38 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/providers/ionic/LiveUpdateIonicManager.kt +169 -0
- package/dist/docs.json +14 -2
- package/dist/esm/definitions.d.ts +17 -2
- package/dist/esm/definitions.js.map +1 -1
- package/ios/Plugin/Classes/Options/FetchLatestBundleOptions.swift +12 -0
- package/ios/Plugin/Classes/Results/FetchLatestBundleResult.swift +30 -1
- package/ios/Plugin/Enums/CustomError.swift +12 -0
- package/ios/Plugin/LiveUpdate.swift +23 -9
- package/ios/Plugin/LiveUpdatePlugin.swift +2 -2
- package/ios/Plugin/Protocols/Api/GetLatestBundleResponse.swift +1 -0
- package/ios/Plugin/Providers/Ionic/LiveUpdateIonicManager.swift +108 -0
- package/ios/Plugin/Providers/Ionic/LiveUpdatePlugin+LiveUpdateProvider.swift +15 -0
- package/package.json +11 -5
|
@@ -13,8 +13,23 @@ Pod::Spec.new do |s|
|
|
|
13
13
|
s.source_files = 'ios/Plugin/**/*.{swift,h,m,c,cc,mm,cpp}'
|
|
14
14
|
s.ios.deployment_target = '15.0'
|
|
15
15
|
s.dependency 'Capacitor'
|
|
16
|
-
s.dependency 'Alamofire', '
|
|
16
|
+
s.dependency 'Alamofire', '>= 5.10.2', '< 6.0.0'
|
|
17
17
|
s.dependency 'ZIPFoundation', '~> 0.9.0'
|
|
18
18
|
s.swift_version = '5.1'
|
|
19
19
|
s.static_framework = true
|
|
20
|
+
s.default_subspec = 'Default'
|
|
21
|
+
|
|
22
|
+
s.subspec 'Default' do |ss|
|
|
23
|
+
# Default subspec
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Opt-in subspec that pulls in the Ionic Live Update Provider SDK and
|
|
27
|
+
# compiles in the provider/manager classes guarded by
|
|
28
|
+
# `#if CAPAWESOME_INCLUDE_IONIC_PROVIDER`.
|
|
29
|
+
s.subspec 'IonicProvider' do |ss|
|
|
30
|
+
ss.dependency 'LiveUpdateProvider', '~> 1.0.0'
|
|
31
|
+
ss.pod_target_xcconfig = {
|
|
32
|
+
'OTHER_SWIFT_FLAGS' => '$(inherited) -DCAPAWESOME_INCLUDE_IONIC_PROVIDER'
|
|
33
|
+
}
|
|
34
|
+
end
|
|
20
35
|
end
|
package/Package.swift
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// swift-tools-version:
|
|
1
|
+
// swift-tools-version: 6.1
|
|
2
2
|
import PackageDescription
|
|
3
3
|
|
|
4
4
|
let package = Package(
|
|
@@ -9,10 +9,17 @@ let package = Package(
|
|
|
9
9
|
name: "CapawesomeCapacitorLiveUpdate",
|
|
10
10
|
targets: ["LiveUpdatePlugin"])
|
|
11
11
|
],
|
|
12
|
+
traits: [
|
|
13
|
+
.trait(
|
|
14
|
+
name: "IonicProvider",
|
|
15
|
+
description: "Enables the Ionic Live Update Provider SDK integration."
|
|
16
|
+
)
|
|
17
|
+
],
|
|
12
18
|
dependencies: [
|
|
13
19
|
.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0"),
|
|
14
|
-
.package(url: "https://github.com/Alamofire/Alamofire.git", .
|
|
15
|
-
.package(url: "https://github.com/weichsel/ZIPFoundation.git", .upToNextMinor(from: "0.9.0"))
|
|
20
|
+
.package(url: "https://github.com/Alamofire/Alamofire.git", .upToNextMajor(from: "5.10.2")),
|
|
21
|
+
.package(url: "https://github.com/weichsel/ZIPFoundation.git", .upToNextMinor(from: "0.9.0")),
|
|
22
|
+
.package(url: "https://github.com/ionic-team/live-update-provider-sdk.git", from: "1.0.0")
|
|
16
23
|
],
|
|
17
24
|
targets: [
|
|
18
25
|
.target(
|
|
@@ -21,12 +28,21 @@ let package = Package(
|
|
|
21
28
|
.product(name: "Capacitor", package: "capacitor-swift-pm"),
|
|
22
29
|
.product(name: "Cordova", package: "capacitor-swift-pm"),
|
|
23
30
|
.product(name: "Alamofire", package: "Alamofire"),
|
|
24
|
-
.product(name: "ZIPFoundation", package: "ZIPFoundation")
|
|
31
|
+
.product(name: "ZIPFoundation", package: "ZIPFoundation"),
|
|
32
|
+
.product(
|
|
33
|
+
name: "LiveUpdateProvider",
|
|
34
|
+
package: "live-update-provider-sdk",
|
|
35
|
+
condition: .when(traits: ["IonicProvider"])
|
|
36
|
+
)
|
|
25
37
|
],
|
|
26
|
-
path: "ios/Plugin"
|
|
38
|
+
path: "ios/Plugin",
|
|
39
|
+
swiftSettings: [
|
|
40
|
+
.define("CAPAWESOME_INCLUDE_IONIC_PROVIDER", .when(traits: ["IonicProvider"]))
|
|
41
|
+
]),
|
|
27
42
|
.testTarget(
|
|
28
43
|
name: "LiveUpdatePluginTests",
|
|
29
44
|
dependencies: ["LiveUpdatePlugin"],
|
|
30
45
|
path: "ios/PluginTests")
|
|
31
|
-
]
|
|
46
|
+
],
|
|
47
|
+
swiftLanguageModes: [.v5]
|
|
32
48
|
)
|
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Capacitor Live Update Plugin
|
|
2
2
|
|
|
3
3
|
Capacitor plugin that allows you to update your app remotely in real-time without requiring users to download a new version from the app store, known as Over-the-Air (OTA) updates.
|
|
4
4
|
|
|
@@ -10,7 +10,7 @@ Capacitor plugin that allows you to update your app remotely in real-time withou
|
|
|
10
10
|
|
|
11
11
|
## Features
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
The Capacitor Live Update plugin is one of the most feature-rich Over-the-Air (OTA) update solutions for Capacitor apps. Here are some of the key features:
|
|
14
14
|
|
|
15
15
|
- 🔋 Supports **Android and iOS**
|
|
16
16
|
- ⚡️ **Capacitor 6/7/8** support
|
|
@@ -25,28 +25,36 @@ We are proud to offer one of the most complete and feature-rich Capacitor plugin
|
|
|
25
25
|
- 📡 **Update Lifecycle Events**: Track download progress, react to bundle changes, and detect reloads with auto-blocking of rolled back bundles.
|
|
26
26
|
- 🏠 **Self-Hosted Bundles**: Download bundles from any URL, no Capawesome Cloud dependency required.
|
|
27
27
|
- 🏷️ **Custom Properties**: Associate custom key-value metadata with bundles via Capawesome Cloud.
|
|
28
|
+
- 🌀 **Ionic Portals Support**: Power live updates for [Ionic Portals](https://ionic.io/docs/portals) via the [Ionic Live Update Provider SDK](https://github.com/ionic-team/live-update-provider-sdk).
|
|
29
|
+
- 🧩 **Federated Capacitor Support**: Power live updates for [Federated Capacitor](https://ionic.io/docs/portals/for-capacitor/overview) shells via the [Ionic Live Update Provider SDK](https://github.com/ionic-team/live-update-provider-sdk).
|
|
28
30
|
- 🔒 **Security**: Verify the authenticity and integrity of the bundle using a public key.
|
|
29
31
|
- ⚔️ **Battle-Tested**: Used in more than 1,000 projects to update apps on more than 20,000,000 devices.
|
|
30
32
|
- 🌐 **Open Source**: Licensed under the MIT License.
|
|
31
33
|
|
|
32
34
|
Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look!
|
|
33
35
|
|
|
34
|
-
##
|
|
36
|
+
## Use Cases
|
|
35
37
|
|
|
36
|
-
|
|
38
|
+
The Live Update plugin is typically used to ship changes to users without going through the app stores, for example:
|
|
39
|
+
|
|
40
|
+
- **Instant bug fixes**: Deliver critical fixes to your users in real-time without waiting for app store reviews.
|
|
41
|
+
- **Fast feature delivery**: Ship new web-based features as soon as they are ready by publishing a new bundle.
|
|
42
|
+
- **Staged rollouts**: Gradually roll out new bundles to a subset of devices to gather feedback before a full release.
|
|
43
|
+
- **Testing and QA channels**: Distribute preview versions to testers via dedicated channels before promoting them to production.
|
|
44
|
+
- **Safe releases**: Automatically roll back to the default bundle if a faulty update prevents the app from becoming ready.
|
|
37
45
|
|
|
38
46
|
## Compatibility
|
|
39
47
|
|
|
40
48
|
| Plugin Version | Capacitor Version | Status |
|
|
41
49
|
| -------------- | ----------------- | -------------- |
|
|
42
50
|
| 8.x.x | >=8.x.x | Active support |
|
|
43
|
-
| 7.x.x | 7.x.x |
|
|
44
|
-
| 6.x.x | 6.x.x |
|
|
51
|
+
| 7.x.x | 7.x.x | Maintenance |
|
|
52
|
+
| 6.x.x | 6.x.x | Maintenance |
|
|
45
53
|
| 5.x.x | 5.x.x | Deprecated |
|
|
46
54
|
|
|
47
55
|
## Guides
|
|
48
56
|
|
|
49
|
-
- [Getting Started with Capawesome Cloud Live Updates](https://capawesome.io/cloud/live-updates/setup/)
|
|
57
|
+
- [Getting Started with Capawesome Cloud Live Updates](https://capawesome.io/docs/cloud/live-updates/setup/)
|
|
50
58
|
- [Migrating from Ionic Appflow to Capawesome Cloud](https://capawesome.io/blog/migrating-from-ionic-appflow-to-capawesome-cloud/)
|
|
51
59
|
- [Migrating from App Center to Capawesome Cloud](https://capawesome.io/blog/migrating-from-app-center-to-capawesome-cloud/)
|
|
52
60
|
|
|
@@ -76,7 +84,7 @@ npx cap sync
|
|
|
76
84
|
|
|
77
85
|
#### Channel
|
|
78
86
|
|
|
79
|
-
If you are using [Versioned Channels](https://capawesome.io/cloud/live-updates/guides/best-practices/#versioned-channels), you can set a default channel directly in your native project by adding a string resource.
|
|
87
|
+
If you are using [Versioned Channels](https://capawesome.io/docs/cloud/live-updates/guides/best-practices/#versioned-channels), you can set a default channel directly in your native project by adding a string resource.
|
|
80
88
|
This allows you to tie the channel to the version code at build time.
|
|
81
89
|
|
|
82
90
|
Add the following to your app's `build.gradle` file:
|
|
@@ -93,6 +101,7 @@ android {
|
|
|
93
101
|
|
|
94
102
|
If needed, you can define the following project variable in your app’s `variables.gradle` file to change the default version of the dependency:
|
|
95
103
|
|
|
104
|
+
- `$ionicLiveUpdateProviderVersion` version of `io.ionic:liveupdateprovider` (default: `1.0.0`)
|
|
96
105
|
- `$okhttp3Version` version of `com.squareup.okhttp3:okhttp` (default: `5.3.2`)
|
|
97
106
|
- `$zip4jVersion` version of `net.lingala.zip4j:zip4j` (default: `2.11.5`)
|
|
98
107
|
|
|
@@ -102,7 +111,7 @@ This can be useful if you encounter dependency conflicts with other plugins in y
|
|
|
102
111
|
|
|
103
112
|
#### Channel
|
|
104
113
|
|
|
105
|
-
If you are using [Versioned Channels](https://capawesome.io/cloud/live-updates/guides/best-practices/#versioned-channels), you can set a default channel directly in your native project by adding a key to your `Info.plist` file.
|
|
114
|
+
If you are using [Versioned Channels](https://capawesome.io/docs/cloud/live-updates/guides/best-practices/#versioned-channels), you can set a default channel directly in your native project by adding a key to your `Info.plist` file.
|
|
106
115
|
This allows you to tie the channel to the build version at build time.
|
|
107
116
|
|
|
108
117
|
Add the following to your `Info.plist` file:
|
|
@@ -139,6 +148,10 @@ Add the `NSPrivacyAccessedAPICategoryUserDefaults` dictionary key to your [Priva
|
|
|
139
148
|
|
|
140
149
|
We recommend to declare [`CA92.1`](https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api#4278401) as the reason for accessing the [`UserDefaults`](https://developer.apple.com/documentation/foundation/userdefaults) API.
|
|
141
150
|
|
|
151
|
+
## Ionic Live Update Provider SDK Integration
|
|
152
|
+
|
|
153
|
+
This plugin implements the [Ionic Live Update Provider SDK](https://github.com/ionic-team/live-update-provider-sdk) contract, so that **Ionic Portals** and **Federated Capacitor** apps can use Capawesome Cloud to deliver live updates. Federated Capacitor resolves the provider by the plugin name `LiveUpdate`. See [the full setup guide](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/live-update/docs/ionic-live-update-provider-sdk-integration.md) for details.
|
|
154
|
+
|
|
142
155
|
## Configuration
|
|
143
156
|
|
|
144
157
|
<docgen-config>
|
|
@@ -206,57 +219,191 @@ export default config;
|
|
|
206
219
|
|
|
207
220
|
</docgen-config>
|
|
208
221
|
|
|
222
|
+
## Demo
|
|
223
|
+
|
|
224
|
+
A working example can be found here: [capawesome-team/capacitor-live-update-demo](https://github.com/capawesome-team/capacitor-live-update-demo)
|
|
225
|
+
|
|
226
|
+
| Android | iOS |
|
|
227
|
+
| --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
228
|
+
| <img src="https://raw.githubusercontent.com/capawesome-team/capacitor-plugins/main/packages/live-update/.github/assets/demo-android.png" width="324" alt="Android Demo" /> | <img src="https://raw.githubusercontent.com/capawesome-team/capacitor-plugins/main/packages/live-update/.github/assets/demo-ios.png" width="324" alt="iOS Demo" /> |
|
|
229
|
+
|
|
209
230
|
## Usage
|
|
210
231
|
|
|
232
|
+
All methods are only available on Android and iOS. The following examples show how to sync with Capawesome Cloud, mark the app as ready, check for and download new bundles, switch between and manage bundles, work with channels, inspect the current and next bundle, identify devices, read the app version, and reset to the default bundle.
|
|
233
|
+
|
|
234
|
+
### Sync with Capawesome Cloud
|
|
235
|
+
|
|
236
|
+
Automatically download and set the latest bundle for the app using [Capawesome Cloud](https://cloud.capawesome.io/). Call `reload()` or restart the app to apply the changes. The `channel` option overrides the channel for this single sync call without persisting it:
|
|
237
|
+
|
|
211
238
|
```typescript
|
|
212
239
|
import { LiveUpdate } from '@capawesome/capacitor-live-update';
|
|
213
240
|
|
|
214
|
-
const
|
|
215
|
-
await LiveUpdate.
|
|
241
|
+
const sync = async () => {
|
|
242
|
+
const result = await LiveUpdate.sync({
|
|
243
|
+
channel: 'production-5',
|
|
244
|
+
});
|
|
245
|
+
return result.nextBundleId;
|
|
216
246
|
};
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Notify the plugin that the app is ready
|
|
250
|
+
|
|
251
|
+
Call `ready()` as soon as the app is ready to use so that the plugin knows no rollback is needed. If the `readyTimeout` configuration option is set and this method is not called in time, the plugin resets the app to the default bundle:
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
import { LiveUpdate } from '@capawesome/capacitor-live-update';
|
|
255
|
+
|
|
256
|
+
const ready = async () => {
|
|
257
|
+
const result = await LiveUpdate.ready();
|
|
258
|
+
if (result.currentBundleId) {
|
|
259
|
+
console.log(`The app is now using the bundle with the identifier ${result.currentBundleId}.`);
|
|
260
|
+
}
|
|
261
|
+
if (result.previousBundleId) {
|
|
262
|
+
console.log(`The app was using the bundle with the identifier ${result.previousBundleId}.`);
|
|
263
|
+
}
|
|
264
|
+
if (result.rollback) {
|
|
265
|
+
console.log('The app was reset to the default bundle.');
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Check if a new bundle is available
|
|
271
|
+
|
|
272
|
+
Fetch the latest bundle from Capawesome Cloud and compare it with the bundle that is currently in use:
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
import { LiveUpdate } from '@capawesome/capacitor-live-update';
|
|
276
|
+
|
|
277
|
+
const fetchLatestBundle = async () => {
|
|
278
|
+
await LiveUpdate.fetchLatestBundle();
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
const isNewBundleAvailable = async () => {
|
|
282
|
+
const { bundleId: latestBundleId } = await LiveUpdate.fetchLatestBundle({
|
|
283
|
+
channel: 'production-5',
|
|
284
|
+
});
|
|
285
|
+
if (latestBundleId) {
|
|
286
|
+
const { bundleId: currentBundleId } = await LiveUpdate.getCurrentBundle();
|
|
287
|
+
return latestBundleId !== currentBundleId;
|
|
288
|
+
} else {
|
|
289
|
+
return false;
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Download a bundle from your own server
|
|
295
|
+
|
|
296
|
+
Download a self-hosted bundle from any URL, without any Capawesome Cloud dependency:
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
import { LiveUpdate } from '@capawesome/capacitor-live-update';
|
|
217
300
|
|
|
218
301
|
const downloadBundle = async () => {
|
|
219
302
|
await LiveUpdate.downloadBundle({ url: 'https://example.com/1.0.0.zip', bundleId: '1.0.0' });
|
|
220
303
|
};
|
|
304
|
+
```
|
|
221
305
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
306
|
+
### Switch to a downloaded bundle
|
|
307
|
+
|
|
308
|
+
Set the next bundle to use for the app. Call `reload()` or restart the app to apply the changes:
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
import { LiveUpdate } from '@capawesome/capacitor-live-update';
|
|
312
|
+
|
|
313
|
+
const setNextBundle = async () => {
|
|
314
|
+
await LiveUpdate.setNextBundle({ bundleId: '7f0b9bf2-dff6-4be2-bcac-b068cc5ea756' });
|
|
225
315
|
};
|
|
226
316
|
|
|
227
|
-
const
|
|
228
|
-
await LiveUpdate.
|
|
317
|
+
const reload = async () => {
|
|
318
|
+
await LiveUpdate.reload();
|
|
229
319
|
};
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### Manage downloaded bundles
|
|
323
|
+
|
|
324
|
+
Get the identifiers of all bundles that have been downloaded and delete bundles that are no longer needed:
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
import { LiveUpdate } from '@capawesome/capacitor-live-update';
|
|
230
328
|
|
|
231
329
|
const getBundles = async () => {
|
|
232
330
|
const result = await LiveUpdate.getBundles();
|
|
233
331
|
return result.bundleIds;
|
|
234
332
|
};
|
|
235
333
|
|
|
334
|
+
const deleteBundle = async () => {
|
|
335
|
+
await LiveUpdate.deleteBundle({ bundleId: 'my-bundle' });
|
|
336
|
+
};
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### Work with channels
|
|
340
|
+
|
|
341
|
+
Set the channel to use for updates, read the currently resolved channel, and fetch the available channels from Capawesome Cloud. The channel set via `setChannel(...)` is persisted across app restarts. Note that `fetchChannels(...)` only works for apps with public channels enabled:
|
|
342
|
+
|
|
343
|
+
```typescript
|
|
344
|
+
import { LiveUpdate } from '@capawesome/capacitor-live-update';
|
|
345
|
+
|
|
346
|
+
const setChannel = async () => {
|
|
347
|
+
await LiveUpdate.setChannel({ channel: 'production-5' });
|
|
348
|
+
};
|
|
349
|
+
|
|
236
350
|
const getChannel = async () => {
|
|
237
351
|
const result = await LiveUpdate.getChannel();
|
|
238
352
|
return result.channel;
|
|
239
353
|
};
|
|
240
354
|
|
|
355
|
+
const fetchChannels = async () => {
|
|
356
|
+
const result = await LiveUpdate.fetchChannels();
|
|
357
|
+
return result.channels;
|
|
358
|
+
};
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### Inspect the current and next bundle
|
|
362
|
+
|
|
363
|
+
Get the identifier of the bundle that is currently used by the app and of the bundle that will be used after the next reload or app restart:
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
import { LiveUpdate } from '@capawesome/capacitor-live-update';
|
|
367
|
+
|
|
241
368
|
const getCurrentBundle = async () => {
|
|
242
369
|
const result = await LiveUpdate.getCurrentBundle();
|
|
243
370
|
return result.bundleId;
|
|
244
371
|
};
|
|
245
372
|
|
|
246
|
-
const
|
|
247
|
-
const result = await LiveUpdate.
|
|
248
|
-
return result.
|
|
373
|
+
const getNextBundle = async () => {
|
|
374
|
+
const result = await LiveUpdate.getNextBundle();
|
|
375
|
+
return result.bundleId;
|
|
249
376
|
};
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### Identify devices
|
|
380
|
+
|
|
381
|
+
Read the unique device identifier and manage a custom identifier for the device:
|
|
382
|
+
|
|
383
|
+
```typescript
|
|
384
|
+
import { LiveUpdate } from '@capawesome/capacitor-live-update';
|
|
250
385
|
|
|
251
386
|
const getDeviceId = async () => {
|
|
252
387
|
const result = await LiveUpdate.getDeviceId();
|
|
253
388
|
return result.deviceId;
|
|
254
389
|
};
|
|
255
390
|
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
|
|
391
|
+
const setCustomId = async () => {
|
|
392
|
+
await LiveUpdate.setCustomId({ customId: 'my-custom-id' });
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
const getCustomId = async () => {
|
|
396
|
+
const result = await LiveUpdate.getCustomId();
|
|
397
|
+
return result.customId;
|
|
259
398
|
};
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### Read the app version
|
|
402
|
+
|
|
403
|
+
Get the native version code and version name of the app, for example to restrict live updates to compatible native versions:
|
|
404
|
+
|
|
405
|
+
```typescript
|
|
406
|
+
import { LiveUpdate } from '@capawesome/capacitor-live-update';
|
|
260
407
|
|
|
261
408
|
const getVersionCode = async () => {
|
|
262
409
|
const result = await LiveUpdate.getVersionCode();
|
|
@@ -267,58 +414,18 @@ const getVersionName = async () => {
|
|
|
267
414
|
const result = await LiveUpdate.getVersionName();
|
|
268
415
|
return result.versionName;
|
|
269
416
|
};
|
|
417
|
+
```
|
|
270
418
|
|
|
271
|
-
|
|
272
|
-
const result = await LiveUpdate.ready();
|
|
273
|
-
if (result.currentBundleId) {
|
|
274
|
-
console.log(`The app is now using the bundle with the identifier ${result.currentBundleId}.`);
|
|
275
|
-
}
|
|
276
|
-
if (result.previousBundleId) {
|
|
277
|
-
console.log(`The app was using the bundle with the identifier ${result.previousBundleId}.`);
|
|
278
|
-
}
|
|
279
|
-
if (result.rollback) {
|
|
280
|
-
console.log('The app was reset to the default bundle.');
|
|
281
|
-
}
|
|
282
|
-
};
|
|
419
|
+
### Reset to the default bundle
|
|
283
420
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
421
|
+
Reset the app to the bundle that shipped with the native app binary. Call `reload()` or restart the app to apply the changes:
|
|
422
|
+
|
|
423
|
+
```typescript
|
|
424
|
+
import { LiveUpdate } from '@capawesome/capacitor-live-update';
|
|
287
425
|
|
|
288
426
|
const reset = async () => {
|
|
289
427
|
await LiveUpdate.reset();
|
|
290
428
|
};
|
|
291
|
-
|
|
292
|
-
const setChannel = async () => {
|
|
293
|
-
await LiveUpdate.setChannel({ channel: 'production-5' });
|
|
294
|
-
};
|
|
295
|
-
|
|
296
|
-
const setCustomId = async () => {
|
|
297
|
-
await LiveUpdate.setCustomId({ customId: 'my-custom-id' });
|
|
298
|
-
};
|
|
299
|
-
|
|
300
|
-
const setNextBundle = async () => {
|
|
301
|
-
await LiveUpdate.setNextBundle({ bundleId: '7f0b9bf2-dff6-4be2-bcac-b068cc5ea756' });
|
|
302
|
-
};
|
|
303
|
-
|
|
304
|
-
const sync = async () => {
|
|
305
|
-
const result = await LiveUpdate.sync({
|
|
306
|
-
channel: 'production-5',
|
|
307
|
-
});
|
|
308
|
-
return result.nextBundleId;
|
|
309
|
-
};
|
|
310
|
-
|
|
311
|
-
const isNewBundleAvailable = async () => {
|
|
312
|
-
const { bundleId: latestBundleId } = await LiveUpdate.fetchLatestBundle({
|
|
313
|
-
channel: 'production-5',
|
|
314
|
-
});
|
|
315
|
-
if (latestBundleId) {
|
|
316
|
-
const { bundleId: currentBundleId } = await LiveUpdate.getCurrentBundle();
|
|
317
|
-
return latestBundleId !== currentBundleId;
|
|
318
|
-
} else {
|
|
319
|
-
return false;
|
|
320
|
-
}
|
|
321
|
-
};
|
|
322
429
|
```
|
|
323
430
|
|
|
324
431
|
## API
|
|
@@ -1011,21 +1118,22 @@ Remove all listeners for this plugin.
|
|
|
1011
1118
|
|
|
1012
1119
|
#### FetchLatestBundleResult
|
|
1013
1120
|
|
|
1014
|
-
| Prop | Type | Description
|
|
1015
|
-
| ---------------------- | --------------------------------------- |
|
|
1016
|
-
| **`artifactType`** | <code>'manifest' \| 'zip'</code> | The artifact type of the bundle.
|
|
1017
|
-
| **`bundleId`** | <code>string \| null</code> | The unique identifier of the latest bundle. On Capawesome Cloud, this is the ID of the app build artifact. If `null`, no bundle is available.
|
|
1018
|
-
| **`
|
|
1019
|
-
| **`
|
|
1020
|
-
| **`
|
|
1021
|
-
| **`
|
|
1121
|
+
| Prop | Type | Description | Since |
|
|
1122
|
+
| ---------------------- | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
|
|
1123
|
+
| **`artifactType`** | <code>'manifest' \| 'zip'</code> | The artifact type of the bundle. | 6.7.0 |
|
|
1124
|
+
| **`bundleId`** | <code>string \| null</code> | The unique identifier of the latest bundle. On Capawesome Cloud, this is the ID of the app build artifact. If `null`, no bundle is available. | 6.6.0 |
|
|
1125
|
+
| **`channel`** | <code>string</code> | The name of the channel that the bundle is actually from. This is the resolved channel after applying any forced channel assignment and may differ from the channel set by the Live Update SDK. | 8.3.0 |
|
|
1126
|
+
| **`checksum`** | <code>string</code> | The checksum of the latest bundle if the bundle is self-hosted. If the bundle is hosted on Capawesome Cloud, the checksum will be returned as response header when downloading the bundle. | 7.1.0 |
|
|
1127
|
+
| **`customProperties`** | <code>{ [key: string]: string; }</code> | Custom properties that are associated with the latest bundle. | 7.0.0 |
|
|
1128
|
+
| **`downloadUrl`** | <code>string</code> | The URL of the latest bundle to download. Pass this URL to the `downloadBundle(...)` method to download the bundle. | 6.7.0 |
|
|
1129
|
+
| **`signature`** | <code>string</code> | The signature of the latest bundle if the bundle is self-hosted. If the bundle is hosted on Capawesome Cloud, the signature will be returned as response header when downloading the bundle. | 7.1.0 |
|
|
1022
1130
|
|
|
1023
1131
|
|
|
1024
1132
|
#### FetchLatestBundleOptions
|
|
1025
1133
|
|
|
1026
|
-
| Prop | Type | Description
|
|
1027
|
-
| ------------- | ------------------- |
|
|
1028
|
-
| **`channel`** | <code>string</code> | The
|
|
1134
|
+
| Prop | Type | Description | Since |
|
|
1135
|
+
| ------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----- |
|
|
1136
|
+
| **`channel`** | <code>string</code> | The preferred channel name from which the latest bundle should be fetched. This is the SDK preference and may be overridden by a forced channel assignment configured in the Capawesome Cloud Console. | 6.7.0 |
|
|
1029
1137
|
|
|
1030
1138
|
|
|
1031
1139
|
#### GetBlockedBundlesResult
|
|
@@ -1159,9 +1267,9 @@ Remove all listeners for this plugin.
|
|
|
1159
1267
|
|
|
1160
1268
|
#### SyncOptions
|
|
1161
1269
|
|
|
1162
|
-
| Prop | Type | Description
|
|
1163
|
-
| ------------- | ------------------- |
|
|
1164
|
-
| **`channel`** | <code>string</code> | The
|
|
1270
|
+
| Prop | Type | Description | Since |
|
|
1271
|
+
| ------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----- |
|
|
1272
|
+
| **`channel`** | <code>string</code> | The preferred channel name from which the latest bundle should be fetched. This is the SDK preference and may be overridden by a forced channel assignment configured in the Capawesome Cloud Console. | 6.7.0 |
|
|
1165
1273
|
|
|
1166
1274
|
|
|
1167
1275
|
#### PluginListenerHandle
|
|
@@ -1241,7 +1349,7 @@ This way, the app will check for updates, but no updates will be found.
|
|
|
1241
1349
|
|
|
1242
1350
|
## Limitations
|
|
1243
1351
|
|
|
1244
|
-
Live updates are only supported for [binary-compatible changes](https://capawesome.io/cloud/live-updates/faq/#what-are-binary-compatible-changes) (e.g. HTML, CSS, JavaScript).
|
|
1352
|
+
Live updates are only supported for [binary-compatible changes](https://capawesome.io/docs/cloud/live-updates/faq/#what-are-binary-compatible-changes) (e.g. HTML, CSS, JavaScript).
|
|
1245
1353
|
If you change native code, such as adding a new plugin, you need to resubmit your app to the app stores.
|
|
1246
1354
|
For this reason, you must be careful to [restrict live updates to compatible native versions](https://capawesome.io/blog/how-to-restrict-capacitor-live-updates-to-native-versions/) of your app.
|
|
1247
1355
|
|
|
@@ -1259,7 +1367,7 @@ On Capawesome Cloud, the bundle ID corresponds to the ID of the app build artifa
|
|
|
1259
1367
|
There are four ways to set a channel, listed from lowest to highest priority:
|
|
1260
1368
|
|
|
1261
1369
|
1. **Capacitor config**: Set the `defaultChannel` property in the [plugin configuration](#configuration). This is the simplest way to set a static default channel.
|
|
1262
|
-
2. **Native config**: Set `CapawesomeLiveUpdateDefaultChannel` in `Info.plist` (iOS) or `capawesome_live_update_default_channel` in `strings.xml` (Android). This is useful for [Versioned Channels](https://capawesome.io/cloud/live-updates/guides/best-practices/#versioned-channels) where the channel is tied to the build version.
|
|
1370
|
+
2. **Native config**: Set `CapawesomeLiveUpdateDefaultChannel` in `Info.plist` (iOS) or `capawesome_live_update_default_channel` in `strings.xml` (Android). This is useful for [Versioned Channels](https://capawesome.io/docs/cloud/live-updates/guides/best-practices/#versioned-channels) where the channel is tied to the build version.
|
|
1263
1371
|
3. **[`setChannel(...)`](#setchannel)**: Set the channel at runtime. The value is persisted across app restarts.
|
|
1264
1372
|
4. **[`sync(...)`](#sync)**: Pass a `channel` option to override the channel for a single sync call. This does **not** persist the channel.
|
|
1265
1373
|
|
|
@@ -1279,6 +1387,27 @@ You then have three options to get back to the default bundle:
|
|
|
1279
1387
|
|
|
1280
1388
|
However, this is only a problem during development. It is not a problem in production, as Capacitor automatically resets to the default bundle after a native update.
|
|
1281
1389
|
|
|
1390
|
+
### Do I have to use Capawesome Cloud?
|
|
1391
|
+
|
|
1392
|
+
No, the plugin also supports self-hosted bundles. You can download a bundle from any URL using the [`downloadBundle(...)`](#downloadbundle) method and apply it with [`setNextBundle(...)`](#setnextbundle), without any Capawesome Cloud dependency. Capawesome Cloud is only required for the convenience features such as [`sync(...)`](#sync), channels, and rollouts.
|
|
1393
|
+
|
|
1394
|
+
### What changes can I ship with a live update?
|
|
1395
|
+
|
|
1396
|
+
Live updates are only supported for [binary-compatible changes](https://capawesome.io/docs/cloud/live-updates/faq/#what-are-binary-compatible-changes) such as HTML, CSS, and JavaScript. If you change native code, for example by adding a new plugin, you need to resubmit your app to the app stores. You should therefore [restrict live updates to compatible native versions](https://capawesome.io/blog/how-to-restrict-capacitor-live-updates-to-native-versions/) of your app, as described in the [Limitations](#limitations) section.
|
|
1397
|
+
|
|
1398
|
+
### How does the rollback mechanism work?
|
|
1399
|
+
|
|
1400
|
+
If the `readyTimeout` configuration option is set, the plugin waits for the app to call the [`ready()`](#ready) method after a new bundle has been applied. If the method is not called within the configured timeout, the plugin assumes the bundle is faulty and resets the app to the default bundle. It is strongly recommended to configure `readyTimeout` (e.g. `10000` ms). Additionally, with the `autoBlockRolledBackBundles` option enabled, bundles that caused a rollback are automatically blocked and skipped in future sync operations.
|
|
1401
|
+
|
|
1402
|
+
## Related Plugins
|
|
1403
|
+
|
|
1404
|
+
- [App Update](https://capawesome.io/docs/sdks/capacitor/app-update/): Assist users with native app updates, which are required for changes that are not binary-compatible.
|
|
1405
|
+
- [Firebase Remote Config](https://capawesome.io/docs/sdks/capacitor/firebase/remote-config/): Change the behavior and appearance of your app remotely without publishing an update.
|
|
1406
|
+
|
|
1407
|
+
## Newsletter
|
|
1408
|
+
|
|
1409
|
+
Stay up to date with the latest news and updates about the Capawesome, Capacitor, and Ionic ecosystem by subscribing to our [Capawesome Newsletter](https://cloud.capawesome.io/newsletter/).
|
|
1410
|
+
|
|
1282
1411
|
## Changelog
|
|
1283
1412
|
|
|
1284
1413
|
See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/live-update/CHANGELOG.md).
|
package/android/build.gradle
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
|
2
|
+
|
|
1
3
|
ext {
|
|
2
4
|
junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
|
|
3
5
|
androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.1'
|
|
@@ -5,19 +7,23 @@ ext {
|
|
|
5
7
|
androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.7.0'
|
|
6
8
|
okhttp3Version = project.hasProperty('okhttp3Version') ? rootProject.ext.okhttp3Version : '5.3.2'
|
|
7
9
|
zip4jVersion = project.hasProperty('zip4jVersion') ? rootProject.ext.zip4jVersion : '2.11.5'
|
|
10
|
+
ionicLiveUpdateProviderVersion = project.hasProperty('ionicLiveUpdateProviderVersion') ? rootProject.ext.ionicLiveUpdateProviderVersion : '1.0.0'
|
|
8
11
|
}
|
|
9
12
|
|
|
10
13
|
buildscript {
|
|
14
|
+
ext.kotlin_version = project.hasProperty("kotlin_version") ? rootProject.ext.kotlin_version : '2.2.20'
|
|
11
15
|
repositories {
|
|
12
16
|
google()
|
|
13
17
|
mavenCentral()
|
|
14
18
|
}
|
|
15
19
|
dependencies {
|
|
16
20
|
classpath 'com.android.tools.build:gradle:8.13.0'
|
|
21
|
+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
|
17
22
|
}
|
|
18
23
|
}
|
|
19
24
|
|
|
20
25
|
apply plugin: 'com.android.library'
|
|
26
|
+
apply plugin: 'org.jetbrains.kotlin.android'
|
|
21
27
|
|
|
22
28
|
android {
|
|
23
29
|
namespace = "io.capawesome.capacitorjs.plugins.liveupdate"
|
|
@@ -28,6 +34,7 @@ android {
|
|
|
28
34
|
versionCode 1
|
|
29
35
|
versionName "1.0"
|
|
30
36
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
|
37
|
+
consumerProguardFiles 'consumer-rules.pro'
|
|
31
38
|
}
|
|
32
39
|
buildTypes {
|
|
33
40
|
release {
|
|
@@ -44,6 +51,12 @@ android {
|
|
|
44
51
|
}
|
|
45
52
|
}
|
|
46
53
|
|
|
54
|
+
kotlin {
|
|
55
|
+
compilerOptions {
|
|
56
|
+
jvmTarget = JvmTarget.JVM_21
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
47
60
|
repositories {
|
|
48
61
|
google()
|
|
49
62
|
mavenCentral()
|
|
@@ -56,6 +69,10 @@ dependencies {
|
|
|
56
69
|
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
|
|
57
70
|
implementation "net.lingala.zip4j:zip4j:$zip4jVersion"
|
|
58
71
|
implementation "com.squareup.okhttp3:okhttp:$okhttp3Version"
|
|
72
|
+
// The Ionic Live Update Provider SDK is compile-time only: Federated Capacitor
|
|
73
|
+
// and Ionic Portals bring it transitively at runtime, and the provider classes
|
|
74
|
+
// are only loaded when a host actually calls `createManager`.
|
|
75
|
+
compileOnly "io.ionic:liveupdateprovider:$ionicLiveUpdateProviderVersion"
|
|
59
76
|
testImplementation "junit:junit:$junitVersion"
|
|
60
77
|
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
|
|
61
78
|
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
|