@capawesome/capacitor-live-update 8.2.2 → 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.
Files changed (22) hide show
  1. package/CapawesomeCapacitorLiveUpdate.podspec +16 -1
  2. package/Package.swift +22 -6
  3. package/README.md +213 -84
  4. package/android/build.gradle +17 -0
  5. package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/LiveUpdate.java +69 -20
  6. package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/LiveUpdatePlugin.java +30 -1
  7. package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/classes/api/GetLatestBundleResponse.java +9 -0
  8. package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/classes/options/FetchLatestBundleOptions.java +15 -0
  9. package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/classes/results/FetchLatestBundleResult.java +38 -0
  10. package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/providers/ionic/LiveUpdateIonicManager.kt +169 -0
  11. package/dist/docs.json +14 -2
  12. package/dist/esm/definitions.d.ts +17 -2
  13. package/dist/esm/definitions.js.map +1 -1
  14. package/ios/Plugin/Classes/Options/FetchLatestBundleOptions.swift +12 -0
  15. package/ios/Plugin/Classes/Results/FetchLatestBundleResult.swift +30 -1
  16. package/ios/Plugin/Enums/CustomError.swift +12 -0
  17. package/ios/Plugin/LiveUpdate.swift +23 -9
  18. package/ios/Plugin/LiveUpdatePlugin.swift +2 -2
  19. package/ios/Plugin/Protocols/Api/GetLatestBundleResponse.swift +1 -0
  20. package/ios/Plugin/Providers/Ionic/LiveUpdateIonicManager.swift +108 -0
  21. package/ios/Plugin/Providers/Ionic/LiveUpdatePlugin+LiveUpdateProvider.swift +15 -0
  22. package/package.json +13 -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', '~> 5.10.2'
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: 5.9
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", .upToNextMinor(from: "5.10.2")),
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
- # @capawesome/capacitor-live-update
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
- We are proud to offer one of the most complete and feature-rich Capacitor plugins for Over-the-Air (OTA) updates. Here are some of the key features:
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
- ## Newsletter
36
+ ## Use Cases
35
37
 
36
- 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/).
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 | Deprecated |
44
- | 6.x.x | 6.x.x | Deprecated |
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 deleteBundle = async () => {
215
- await LiveUpdate.deleteBundle({ bundleId: 'my-bundle' });
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
- const fetchChannels = async () => {
223
- const result = await LiveUpdate.fetchChannels();
224
- return result.channels;
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 fetchLatestBundle = async () => {
228
- await LiveUpdate.fetchLatestBundle();
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 getCustomId = async () => {
247
- const result = await LiveUpdate.getCustomId();
248
- return result.customId;
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 getNextBundle = async () => {
257
- const result = await LiveUpdate.getNextBundle();
258
- return result.bundleId;
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
- const ready = async () => {
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
- const reload = async () => {
285
- await LiveUpdate.reload();
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 | Since |
1015
- | ---------------------- | --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
1016
- | **`artifactType`** | <code>'manifest' \| 'zip'</code> | The artifact type of the bundle. | 6.7.0 |
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. | 6.6.0 |
1018
- | **`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 |
1019
- | **`customProperties`** | <code>{ [key: string]: string; }</code> | Custom properties that are associated with the latest bundle. | 7.0.0 |
1020
- | **`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 |
1021
- | **`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 |
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 | Since |
1027
- | ------------- | ------------------- | ---------------------------------------------------------------- | ----- |
1028
- | **`channel`** | <code>string</code> | The name of the channel where the latest bundle is fetched from. | 6.7.0 |
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 | Since |
1163
- | ------------- | ------------------- | ---------------------------------------------------------------- | ----- |
1164
- | **`channel`** | <code>string</code> | The name of the channel where the latest bundle is fetched from. | 6.7.0 |
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).
@@ -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"