@capawesome/capacitor-live-update 6.2.0 → 6.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -11
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/LiveUpdate.java +46 -12
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/LiveUpdateConfig.java +22 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/LiveUpdatePlugin.java +19 -1
- package/dist/docs.json +34 -2
- package/dist/esm/definitions.d.ts +18 -1
- package/dist/esm/definitions.js.map +1 -1
- package/ios/Plugin/Classes/CustomError.swift +6 -0
- package/ios/Plugin/LiveUpdate.swift +78 -26
- package/ios/Plugin/LiveUpdateConfig.swift +2 -0
- package/ios/Plugin/LiveUpdatePlugin.swift +12 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -65,15 +65,17 @@ We recommend to declare [`CA92.1`](https://developer.apple.com/documentation/bun
|
|
|
65
65
|
<docgen-config>
|
|
66
66
|
<!--Update the source file JSDoc comments and rerun docgen to update the docs below-->
|
|
67
67
|
|
|
68
|
-
| Prop | Type | Description
|
|
69
|
-
| ----------------------- | ------------------------- |
|
|
70
|
-
| **`appId`** | <code>string</code> | The app ID is used to identify the app when using [Capawesome Cloud](https://capawesome.io/cloud).
|
|
71
|
-
| **`autoDeleteBundles`** | <code>boolean</code> | Whether or not to automatically delete unused bundles. When enabled, the plugin will automatically delete unused bundles after calling `ready()`.
|
|
72
|
-
| **`
|
|
73
|
-
| **`
|
|
74
|
-
| **`
|
|
75
|
-
| **`
|
|
76
|
-
| **`
|
|
68
|
+
| Prop | Type | Description | Default | Since |
|
|
69
|
+
| ----------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------ | ----- |
|
|
70
|
+
| **`appId`** | <code>string</code> | The app ID is used to identify the app when using [Capawesome Cloud](https://capawesome.io/cloud). This is **NOT** the same as the app identifier (e.g. `com.example.app`). This is a unique identifier generated by Capawesome Cloud (e.g. `6e351b4f-69a7-415e-a057-4567df7ffe94`). | | 5.0.0 |
|
|
71
|
+
| **`autoDeleteBundles`** | <code>boolean</code> | Whether or not to automatically delete unused bundles. When enabled, the plugin will automatically delete unused bundles after calling `ready()`. | <code>false</code> | 5.0.0 |
|
|
72
|
+
| **`defaultChannel`** | <code>string</code> | The default channel of the app. | | 6.3.0 |
|
|
73
|
+
| **`enabled`** | <code>boolean</code> | Whether or not the plugin is enabled. | <code>true</code> | 5.0.0 |
|
|
74
|
+
| **`httpTimeout`** | <code>number</code> | The timeout in milliseconds for HTTP requests. | <code>60000</code> | 6.4.0 |
|
|
75
|
+
| **`location`** | <code>'us' \| 'eu'</code> | The location of the server to use when using [Capawesome Cloud](https://capawesome.io/cloud). | <code>'us'</code> | 6.2.0 |
|
|
76
|
+
| **`publicKey`** | <code>string</code> | The public key to verify the integrity of the bundle. The public key must be a PEM-encoded RSA public key. | | 6.1.0 |
|
|
77
|
+
| **`readyTimeout`** | <code>number</code> | The timeout in milliseconds to wait for the app to be ready before resetting to the default bundle. | <code>10000</code> | 5.0.0 |
|
|
78
|
+
| **`resetOnUpdate`** | <code>boolean</code> | Whether or not the app should be reset to the default bundle during an update. | <code>true</code> | 5.0.0 |
|
|
77
79
|
|
|
78
80
|
### Examples
|
|
79
81
|
|
|
@@ -83,9 +85,11 @@ In `capacitor.config.json`:
|
|
|
83
85
|
{
|
|
84
86
|
"plugins": {
|
|
85
87
|
"LiveUpdate": {
|
|
86
|
-
"appId": '
|
|
88
|
+
"appId": '6e351b4f-69a7-415e-a057-4567df7ffe94',
|
|
87
89
|
"autoDeleteBundles": undefined,
|
|
90
|
+
"defaultChannel": 'production',
|
|
88
91
|
"enabled": undefined,
|
|
92
|
+
"httpTimeout": undefined,
|
|
89
93
|
"location": 'eu',
|
|
90
94
|
"publicKey": '-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDodf1SD0OOn6hIlDuKBza0Ed0OqtwyVJwiyjmE9BJaZ7y8ZUfcF+SKmd0l2cDPM45XIg2tAFux5n29uoKyHwSt+6tCi5CJA5Z1/1eZruRRqABLonV77KS3HUtvOgqRLDnKSV89dYZkM++NwmzOPgIF422mvc+VukcVOBfc8/AHQIDAQAB-----END PUBLIC KEY-----',
|
|
91
95
|
"readyTimeout": undefined,
|
|
@@ -105,9 +109,11 @@ import { CapacitorConfig } from '@capacitor/cli';
|
|
|
105
109
|
const config: CapacitorConfig = {
|
|
106
110
|
plugins: {
|
|
107
111
|
LiveUpdate: {
|
|
108
|
-
appId: '
|
|
112
|
+
appId: '6e351b4f-69a7-415e-a057-4567df7ffe94',
|
|
109
113
|
autoDeleteBundles: undefined,
|
|
114
|
+
defaultChannel: 'production',
|
|
110
115
|
enabled: undefined,
|
|
116
|
+
httpTimeout: undefined,
|
|
111
117
|
location: 'eu',
|
|
112
118
|
publicKey: '-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDodf1SD0OOn6hIlDuKBza0Ed0OqtwyVJwiyjmE9BJaZ7y8ZUfcF+SKmd0l2cDPM45XIg2tAFux5n29uoKyHwSt+6tCi5CJA5Z1/1eZruRRqABLonV77KS3HUtvOgqRLDnKSV89dYZkM++NwmzOPgIF422mvc+VukcVOBfc8/AHQIDAQAB-----END PUBLIC KEY-----',
|
|
113
119
|
readyTimeout: undefined,
|
|
@@ -641,6 +647,20 @@ Live updates are only supported for [binary-compatible changes](https://capaweso
|
|
|
641
647
|
If you change native code, such as adding a new plugin, you need to resubmit your app to the app stores.
|
|
642
648
|
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.
|
|
643
649
|
|
|
650
|
+
## FAQ
|
|
651
|
+
|
|
652
|
+
### Why can't I see my changes during development?
|
|
653
|
+
|
|
654
|
+
As soon as you have installed a live update, the app will use the live update bundle and no longer the default bundle.
|
|
655
|
+
So if you make local changes to your app and execute `npx cap run`, for example, these changes will apply to the default bundle, which is not currently in use.
|
|
656
|
+
You then have three options to get back to the default bundle:
|
|
657
|
+
|
|
658
|
+
1. **Reset**: Call the [`reset()`](#reset) method to reset the app to the default bundle.
|
|
659
|
+
2. **Reinstall**: Reinstall the app to use the default bundle.
|
|
660
|
+
3. **Update**: Increase the `versionCode`/`CFBundleVersion` so that the plugin automatically performs a reset (assuming the [`resetOnUpdate`](#configuration) configuration option is active).
|
|
661
|
+
|
|
662
|
+
However, this is only a problem during development. It is not a problem in production as long as you have the [`resetOnUpdate`](#configuration) configuration option enabled, as the `versionCode`/`CFBundleVersion` is always incremented during a native update and thus always resets to the default bundle.
|
|
663
|
+
|
|
644
664
|
## Changelog
|
|
645
665
|
|
|
646
666
|
See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/live-update/CHANGELOG.md).
|
|
@@ -37,6 +37,7 @@ import java.security.Signature;
|
|
|
37
37
|
import java.security.spec.X509EncodedKeySpec;
|
|
38
38
|
import java.util.Arrays;
|
|
39
39
|
import java.util.UUID;
|
|
40
|
+
import java.util.concurrent.TimeUnit;
|
|
40
41
|
import net.lingala.zip4j.ZipFile;
|
|
41
42
|
import okhttp3.Call;
|
|
42
43
|
import okhttp3.Callback;
|
|
@@ -133,7 +134,7 @@ public class LiveUpdate {
|
|
|
133
134
|
}
|
|
134
135
|
|
|
135
136
|
public void getChannel(@NonNull NonEmptyCallback callback) {
|
|
136
|
-
String channel =
|
|
137
|
+
String channel = getChannel();
|
|
137
138
|
GetChannelResult result = new GetChannelResult(channel);
|
|
138
139
|
callback.success(result);
|
|
139
140
|
}
|
|
@@ -213,6 +214,11 @@ public class LiveUpdate {
|
|
|
213
214
|
new NonEmptyCallback<GetLatestBundleResponse>() {
|
|
214
215
|
@Override
|
|
215
216
|
public void error(Exception exception) {
|
|
217
|
+
// Throw error if the exception is a timeout
|
|
218
|
+
if (exception instanceof java.net.SocketTimeoutException) {
|
|
219
|
+
callback.error(new Exception(LiveUpdatePlugin.ERROR_HTTP_TIMEOUT));
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
216
222
|
// No update available
|
|
217
223
|
Logger.debug(LiveUpdatePlugin.TAG, "No update available.");
|
|
218
224
|
SyncResult syncResult = new SyncResult(null);
|
|
@@ -381,6 +387,11 @@ public class LiveUpdate {
|
|
|
381
387
|
new NonEmptyCallback<File>() {
|
|
382
388
|
@Override
|
|
383
389
|
public void error(Exception exception) {
|
|
390
|
+
// Throw error if the exception is a timeout
|
|
391
|
+
if (exception instanceof java.net.SocketTimeoutException) {
|
|
392
|
+
callback.error(new Exception(LiveUpdatePlugin.ERROR_HTTP_TIMEOUT));
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
384
395
|
callback.error(exception);
|
|
385
396
|
}
|
|
386
397
|
|
|
@@ -425,7 +436,12 @@ public class LiveUpdate {
|
|
|
425
436
|
}
|
|
426
437
|
|
|
427
438
|
private void downloadFile(@NonNull String url, @NonNull NonEmptyCallback<File> callback) {
|
|
428
|
-
|
|
439
|
+
int httpTimeout = config.getHttpTimeout();
|
|
440
|
+
OkHttpClient okHttpClient = new OkHttpClient.Builder()
|
|
441
|
+
.connectTimeout(httpTimeout, TimeUnit.MILLISECONDS)
|
|
442
|
+
.readTimeout(httpTimeout, TimeUnit.MILLISECONDS)
|
|
443
|
+
.writeTimeout(httpTimeout, TimeUnit.MILLISECONDS)
|
|
444
|
+
.build();
|
|
429
445
|
Request request = new Request.Builder().url(url).build();
|
|
430
446
|
okHttpClient
|
|
431
447
|
.newCall(request)
|
|
@@ -487,7 +503,7 @@ public class LiveUpdate {
|
|
|
487
503
|
.addQueryParameter("appVersionCode", getVersionCode())
|
|
488
504
|
.addQueryParameter("appVersionName", getVersionName())
|
|
489
505
|
.addQueryParameter("bundleId", getCurrentBundleId())
|
|
490
|
-
.addQueryParameter("channelName",
|
|
506
|
+
.addQueryParameter("channelName", getChannel())
|
|
491
507
|
.addQueryParameter("customId", preferences.getCustomId())
|
|
492
508
|
.addQueryParameter("deviceId", getDeviceId())
|
|
493
509
|
.addQueryParameter("osVersion", String.valueOf(Build.VERSION.SDK_INT))
|
|
@@ -495,7 +511,13 @@ public class LiveUpdate {
|
|
|
495
511
|
.addQueryParameter("pluginVersion", LiveUpdatePlugin.VERSION)
|
|
496
512
|
.build();
|
|
497
513
|
Logger.debug(LiveUpdatePlugin.TAG, "Fetching latest bundle from " + url);
|
|
498
|
-
|
|
514
|
+
int httpTimeout = config.getHttpTimeout();
|
|
515
|
+
OkHttpClient client = new OkHttpClient()
|
|
516
|
+
.newBuilder()
|
|
517
|
+
.connectTimeout(httpTimeout, TimeUnit.MILLISECONDS)
|
|
518
|
+
.readTimeout(httpTimeout, TimeUnit.MILLISECONDS)
|
|
519
|
+
.writeTimeout(httpTimeout, TimeUnit.MILLISECONDS)
|
|
520
|
+
.build();
|
|
499
521
|
okhttp3.Request request = new okhttp3.Request.Builder().url(url).build();
|
|
500
522
|
client
|
|
501
523
|
.newCall(request)
|
|
@@ -530,14 +552,6 @@ public class LiveUpdate {
|
|
|
530
552
|
);
|
|
531
553
|
}
|
|
532
554
|
|
|
533
|
-
private String getVersionCode() throws PackageManager.NameNotFoundException {
|
|
534
|
-
return String.valueOf(getPackageInfo().versionCode);
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
private String getVersionName() throws PackageManager.NameNotFoundException {
|
|
538
|
-
return getPackageInfo().versionName;
|
|
539
|
-
}
|
|
540
|
-
|
|
541
555
|
private String[] getBundleIds() {
|
|
542
556
|
File bundlesDirectory = buildBundlesDirectory();
|
|
543
557
|
File[] bundles = bundlesDirectory.listFiles();
|
|
@@ -553,6 +567,18 @@ public class LiveUpdate {
|
|
|
553
567
|
return bundleIds;
|
|
554
568
|
}
|
|
555
569
|
|
|
570
|
+
@Nullable
|
|
571
|
+
private String getChannel() {
|
|
572
|
+
String channel = null;
|
|
573
|
+
if (config.getDefaultChannel() != null) {
|
|
574
|
+
channel = config.getDefaultChannel();
|
|
575
|
+
}
|
|
576
|
+
if (preferences.getChannel() != null) {
|
|
577
|
+
channel = preferences.getChannel();
|
|
578
|
+
}
|
|
579
|
+
return channel;
|
|
580
|
+
}
|
|
581
|
+
|
|
556
582
|
/**
|
|
557
583
|
* @return The current bundle ID (`public` for the built-in bundle).
|
|
558
584
|
*/
|
|
@@ -604,6 +630,14 @@ public class LiveUpdate {
|
|
|
604
630
|
.getString(WebView.CAP_SERVER_PATH, defaultWebAssetDir);
|
|
605
631
|
}
|
|
606
632
|
|
|
633
|
+
private String getVersionCode() throws PackageManager.NameNotFoundException {
|
|
634
|
+
return String.valueOf(getPackageInfo().versionCode);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
private String getVersionName() throws PackageManager.NameNotFoundException {
|
|
638
|
+
return getPackageInfo().versionName;
|
|
639
|
+
}
|
|
640
|
+
|
|
607
641
|
private boolean hasBundle(@NonNull String bundleId) {
|
|
608
642
|
File bundleDirectory = buildBundleDirectoryFor(bundleId);
|
|
609
643
|
return bundleDirectory.exists();
|
package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/LiveUpdateConfig.java
CHANGED
|
@@ -9,7 +9,12 @@ public class LiveUpdateConfig {
|
|
|
9
9
|
private String appId = null;
|
|
10
10
|
|
|
11
11
|
private boolean autoDeleteBundles = false;
|
|
12
|
+
|
|
13
|
+
@Nullable
|
|
14
|
+
private String defaultChannel = null;
|
|
15
|
+
|
|
12
16
|
private boolean enabled = true;
|
|
17
|
+
private int httpTimeout = 60000;
|
|
13
18
|
|
|
14
19
|
@Nullable
|
|
15
20
|
private String location = null;
|
|
@@ -29,10 +34,19 @@ public class LiveUpdateConfig {
|
|
|
29
34
|
return autoDeleteBundles;
|
|
30
35
|
}
|
|
31
36
|
|
|
37
|
+
@Nullable
|
|
38
|
+
public String getDefaultChannel() {
|
|
39
|
+
return defaultChannel;
|
|
40
|
+
}
|
|
41
|
+
|
|
32
42
|
public boolean getEnabled() {
|
|
33
43
|
return enabled;
|
|
34
44
|
}
|
|
35
45
|
|
|
46
|
+
public int getHttpTimeout() {
|
|
47
|
+
return httpTimeout;
|
|
48
|
+
}
|
|
49
|
+
|
|
36
50
|
@Nullable
|
|
37
51
|
public String getLocation() {
|
|
38
52
|
return location;
|
|
@@ -59,10 +73,18 @@ public class LiveUpdateConfig {
|
|
|
59
73
|
this.autoDeleteBundles = autoDeleteBundles;
|
|
60
74
|
}
|
|
61
75
|
|
|
76
|
+
public void setDefaultChannel(@Nullable String defaultChannel) {
|
|
77
|
+
this.defaultChannel = defaultChannel;
|
|
78
|
+
}
|
|
79
|
+
|
|
62
80
|
public void setEnabled(boolean enabled) {
|
|
63
81
|
this.enabled = enabled;
|
|
64
82
|
}
|
|
65
83
|
|
|
84
|
+
public void setHttpTimeout(int httpTimeout) {
|
|
85
|
+
this.httpTimeout = httpTimeout;
|
|
86
|
+
}
|
|
87
|
+
|
|
66
88
|
public void setLocation(@Nullable String location) {
|
|
67
89
|
this.location = location;
|
|
68
90
|
}
|
package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/LiveUpdatePlugin.java
CHANGED
|
@@ -19,7 +19,7 @@ import io.capawesome.capacitorjs.plugins.liveupdate.interfaces.Result;
|
|
|
19
19
|
public class LiveUpdatePlugin extends Plugin {
|
|
20
20
|
|
|
21
21
|
public static final String TAG = "LiveUpdate";
|
|
22
|
-
public static final String VERSION = "6.
|
|
22
|
+
public static final String VERSION = "6.4.0";
|
|
23
23
|
public static final String SHARED_PREFERENCES_NAME = "CapawesomeLiveUpdate"; // DO NOT CHANGE
|
|
24
24
|
public static final String ERROR_APP_ID_MISSING = "appId must be configured.";
|
|
25
25
|
public static final String ERROR_BUNDLE_EXISTS = "bundle already exists.";
|
|
@@ -30,10 +30,14 @@ public class LiveUpdatePlugin extends Plugin {
|
|
|
30
30
|
public static final String ERROR_CHECKSUM_MISMATCH = "Checksum mismatch.";
|
|
31
31
|
public static final String ERROR_CUSTOM_ID_MISSING = "customId must be provided.";
|
|
32
32
|
public static final String ERROR_DOWNLOAD_FAILED = "Bundle could not be downloaded.";
|
|
33
|
+
public static final String ERROR_HTTP_TIMEOUT = "Request timed out.";
|
|
33
34
|
public static final String ERROR_URL_MISSING = "url must be provided.";
|
|
34
35
|
public static final String ERROR_SIGNATURE_VERIFICATION_FAILED = "Signature verification failed.";
|
|
35
36
|
public static final String ERROR_PUBLIC_KEY_INVALID = "Invalid public key.";
|
|
36
37
|
public static final String ERROR_SIGNATURE_MISSING = "Bundle does not contain a signature.";
|
|
38
|
+
public static final String ERROR_SYNC_IN_PROGRESS = "Sync is already in progress.";
|
|
39
|
+
|
|
40
|
+
private boolean syncInProgress = false;
|
|
37
41
|
|
|
38
42
|
@Nullable
|
|
39
43
|
private LiveUpdateConfig config;
|
|
@@ -404,14 +408,23 @@ public class LiveUpdatePlugin extends Plugin {
|
|
|
404
408
|
call.reject(ERROR_APP_ID_MISSING);
|
|
405
409
|
return;
|
|
406
410
|
}
|
|
411
|
+
|
|
412
|
+
if (syncInProgress) {
|
|
413
|
+
call.reject(ERROR_SYNC_IN_PROGRESS);
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
syncInProgress = true;
|
|
417
|
+
|
|
407
418
|
NonEmptyCallback<Result> callback = new NonEmptyCallback<>() {
|
|
408
419
|
@Override
|
|
409
420
|
public void success(Result result) {
|
|
421
|
+
syncInProgress = false;
|
|
410
422
|
call.resolve(result.toJSObject());
|
|
411
423
|
}
|
|
412
424
|
|
|
413
425
|
@Override
|
|
414
426
|
public void error(Exception exception) {
|
|
427
|
+
syncInProgress = false;
|
|
415
428
|
Logger.error(TAG, exception.getMessage(), exception);
|
|
416
429
|
call.reject(exception.getMessage());
|
|
417
430
|
}
|
|
@@ -419,6 +432,7 @@ public class LiveUpdatePlugin extends Plugin {
|
|
|
419
432
|
|
|
420
433
|
implementation.sync(callback);
|
|
421
434
|
} catch (Exception exception) {
|
|
435
|
+
syncInProgress = false;
|
|
422
436
|
Logger.error(TAG, exception.getMessage(), exception);
|
|
423
437
|
call.reject(exception.getMessage());
|
|
424
438
|
}
|
|
@@ -431,8 +445,12 @@ public class LiveUpdatePlugin extends Plugin {
|
|
|
431
445
|
config.setAppId(appId);
|
|
432
446
|
boolean autoDeleteBundles = getConfig().getBoolean("autoDeleteBundles", config.getAutoDeleteBundles());
|
|
433
447
|
config.setAutoDeleteBundles(autoDeleteBundles);
|
|
448
|
+
String defaultChannel = getConfig().getString("defaultChannel", config.getDefaultChannel());
|
|
449
|
+
config.setDefaultChannel(defaultChannel);
|
|
434
450
|
boolean enabled = getConfig().getBoolean("enabled", config.getEnabled());
|
|
435
451
|
config.setEnabled(enabled);
|
|
452
|
+
int httpTimeout = getConfig().getInt("httpTimeout", config.getHttpTimeout());
|
|
453
|
+
config.setHttpTimeout(httpTimeout);
|
|
436
454
|
String location = getConfig().getString("location", config.getLocation());
|
|
437
455
|
config.setLocation(location);
|
|
438
456
|
String publicKey = getConfig().getString("publicKey", config.getPublicKey());
|
package/dist/docs.json
CHANGED
|
@@ -722,11 +722,11 @@
|
|
|
722
722
|
"name": "since"
|
|
723
723
|
},
|
|
724
724
|
{
|
|
725
|
-
"text": "'
|
|
725
|
+
"text": "'6e351b4f-69a7-415e-a057-4567df7ffe94'",
|
|
726
726
|
"name": "example"
|
|
727
727
|
}
|
|
728
728
|
],
|
|
729
|
-
"docs": "The app ID is used to identify the app when using [Capawesome Cloud](https://capawesome.io/cloud).",
|
|
729
|
+
"docs": "The app ID is used to identify the app when using [Capawesome Cloud](https://capawesome.io/cloud).\n\nThis is **NOT** the same as the app identifier (e.g. `com.example.app`).\nThis is a unique identifier generated by Capawesome Cloud (e.g. `6e351b4f-69a7-415e-a057-4567df7ffe94`).",
|
|
730
730
|
"complexTypes": [],
|
|
731
731
|
"type": "string | undefined"
|
|
732
732
|
},
|
|
@@ -746,6 +746,22 @@
|
|
|
746
746
|
"complexTypes": [],
|
|
747
747
|
"type": "boolean | undefined"
|
|
748
748
|
},
|
|
749
|
+
{
|
|
750
|
+
"name": "defaultChannel",
|
|
751
|
+
"tags": [
|
|
752
|
+
{
|
|
753
|
+
"text": "6.3.0",
|
|
754
|
+
"name": "since"
|
|
755
|
+
},
|
|
756
|
+
{
|
|
757
|
+
"text": "'production'",
|
|
758
|
+
"name": "example"
|
|
759
|
+
}
|
|
760
|
+
],
|
|
761
|
+
"docs": "The default channel of the app.",
|
|
762
|
+
"complexTypes": [],
|
|
763
|
+
"type": "string | undefined"
|
|
764
|
+
},
|
|
749
765
|
{
|
|
750
766
|
"name": "enabled",
|
|
751
767
|
"tags": [
|
|
@@ -762,6 +778,22 @@
|
|
|
762
778
|
"complexTypes": [],
|
|
763
779
|
"type": "boolean | undefined"
|
|
764
780
|
},
|
|
781
|
+
{
|
|
782
|
+
"name": "httpTimeout",
|
|
783
|
+
"tags": [
|
|
784
|
+
{
|
|
785
|
+
"text": "6.4.0",
|
|
786
|
+
"name": "since"
|
|
787
|
+
},
|
|
788
|
+
{
|
|
789
|
+
"text": "60000",
|
|
790
|
+
"name": "default"
|
|
791
|
+
}
|
|
792
|
+
],
|
|
793
|
+
"docs": "The timeout in milliseconds for HTTP requests.",
|
|
794
|
+
"complexTypes": [],
|
|
795
|
+
"type": "number | undefined"
|
|
796
|
+
},
|
|
765
797
|
{
|
|
766
798
|
"name": "location",
|
|
767
799
|
"tags": [
|
|
@@ -4,8 +4,11 @@ declare module '@capacitor/cli' {
|
|
|
4
4
|
/**
|
|
5
5
|
* The app ID is used to identify the app when using [Capawesome Cloud](https://capawesome.io/cloud).
|
|
6
6
|
*
|
|
7
|
+
* This is **NOT** the same as the app identifier (e.g. `com.example.app`).
|
|
8
|
+
* This is a unique identifier generated by Capawesome Cloud (e.g. `6e351b4f-69a7-415e-a057-4567df7ffe94`).
|
|
9
|
+
*
|
|
7
10
|
* @since 5.0.0
|
|
8
|
-
* @example '
|
|
11
|
+
* @example '6e351b4f-69a7-415e-a057-4567df7ffe94'
|
|
9
12
|
*/
|
|
10
13
|
appId?: string;
|
|
11
14
|
/**
|
|
@@ -17,6 +20,13 @@ declare module '@capacitor/cli' {
|
|
|
17
20
|
* @default false
|
|
18
21
|
*/
|
|
19
22
|
autoDeleteBundles?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* The default channel of the app.
|
|
25
|
+
*
|
|
26
|
+
* @since 6.3.0
|
|
27
|
+
* @example 'production'
|
|
28
|
+
*/
|
|
29
|
+
defaultChannel?: string;
|
|
20
30
|
/**
|
|
21
31
|
* Whether or not the plugin is enabled.
|
|
22
32
|
*
|
|
@@ -24,6 +34,13 @@ declare module '@capacitor/cli' {
|
|
|
24
34
|
* @default true
|
|
25
35
|
*/
|
|
26
36
|
enabled?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* The timeout in milliseconds for HTTP requests.
|
|
39
|
+
*
|
|
40
|
+
* @since 6.4.0
|
|
41
|
+
* @default 60000
|
|
42
|
+
*/
|
|
43
|
+
httpTimeout?: number;
|
|
27
44
|
/**
|
|
28
45
|
* The location of the server to use when using [Capawesome Cloud](https://capawesome.io/cloud).
|
|
29
46
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAAA,wCAAwC","sourcesContent":["/// <reference types=\"@capacitor/cli\" />\n\ndeclare module '@capacitor/cli' {\n export interface PluginsConfig {\n LiveUpdate?: {\n /**\n * The app ID is used to identify the app when using [Capawesome Cloud](https://capawesome.io/cloud).\n *\n * @since 5.0.0\n * @example '4100e356-e851-47fe-9d3c-b411eb325a99'\n */\n appId?: string;\n /**\n * Whether or not to automatically delete unused bundles.\n *\n * When enabled, the plugin will automatically delete unused bundles after calling `ready()`.\n *\n * @since 5.0.0\n * @default false\n */\n autoDeleteBundles?: boolean;\n /**\n * Whether or not the plugin is enabled.\n *\n * @since 5.0.0\n * @default true\n */\n enabled?: boolean;\n /**\n * The location of the server to use when using [Capawesome Cloud](https://capawesome.io/cloud).\n *\n * @since 6.2.0\n * @default 'us'\n * @example 'eu'\n * @see https://capawesome.io/cloud/live-updates/advanced/privacy/\n */\n location?: 'us' | 'eu';\n /**\n * The public key to verify the integrity of the bundle.\n *\n * The public key must be a PEM-encoded RSA public key.\n *\n * @since 6.1.0\n * @example '-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDodf1SD0OOn6hIlDuKBza0Ed0OqtwyVJwiyjmE9BJaZ7y8ZUfcF+SKmd0l2cDPM45XIg2tAFux5n29uoKyHwSt+6tCi5CJA5Z1/1eZruRRqABLonV77KS3HUtvOgqRLDnKSV89dYZkM++NwmzOPgIF422mvc+VukcVOBfc8/AHQIDAQAB-----END PUBLIC KEY-----'\n */\n publicKey?: string;\n /**\n * The timeout in milliseconds to wait for the app to be ready before resetting to the default bundle.\n *\n * @since 5.0.0\n * @default 10000\n */\n readyTimeout?: number;\n /**\n * Whether or not the app should be reset to the default bundle during an update.\n *\n * @since 5.0.0\n * @default true\n */\n resetOnUpdate?: boolean;\n };\n }\n}\n\nexport interface LiveUpdatePlugin {\n /**\n * Delete a bundle from the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n deleteBundle(options: DeleteBundleOptions): Promise<void>;\n /**\n * Download a bundle.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n downloadBundle(options: DownloadBundleOptions): Promise<void>;\n /**\n * Get the active bundle identifier.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getBundle(): Promise<GetBundleResult>;\n /**\n * Get all available bundle identifiers.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getBundles(): Promise<GetBundlesResult>;\n /**\n * Get the channel of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getChannel(): Promise<GetChannelResult>;\n /**\n * Get the custom identifier of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getCustomId(): Promise<GetCustomIdResult>;\n /**\n * Get the device identifier of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getDeviceId(): Promise<GetDeviceIdResult>;\n /**\n * Get the version code of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getVersionCode(): Promise<GetVersionCodeResult>;\n /**\n * Get the version name of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getVersionName(): Promise<GetVersionNameResult>;\n /**\n * Notify the plugin that the app is ready to use and no rollback is needed.\n *\n * **Attention**: This method should be called as soon as the app is ready to use\n * to prevent the app from being reset to the default bundle.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n ready(): Promise<void>;\n /**\n * Reload the app to apply the new bundle.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n reload(): Promise<void>;\n /**\n * Reset the app to the default bundle.\n *\n * Call `reload()` or restart the app to apply the changes.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n reset(): Promise<void>;\n /**\n * Set the next bundle to use for the app.\n *\n * Call `reload()` or restart the app to apply the changes.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n setBundle(options: SetBundleOptions): Promise<void>;\n /**\n * Set the channel of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n setChannel(options: SetChannelOptions): Promise<void>;\n /**\n * Set the custom identifier of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n setCustomId(options: SetCustomIdOptions): Promise<void>;\n /**\n * Automatically download and set the latest bundle for the app using the [Capawesome Cloud](https://capawesome.io/cloud/).\n *\n * Call `reload()` or restart the app to apply the changes.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n sync(): Promise<SyncResult>;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface DeleteBundleOptions {\n /**\n * The unique identifier of the bundle to delete.\n *\n * @since 5.0.0\n * @example '1.0.0'\n */\n bundleId: string;\n}\n\nexport interface DownloadBundleOptions {\n /**\n * The unique identifier of the bundle.\n *\n * @since 5.0.0\n * @example '1.0.0'\n */\n bundleId: string;\n /**\n * The checksum of the bundle to verify the integrity of the ZIP file.\n *\n * Must be a SHA-256 hash in hexadecimal format.\n *\n * @since 6.1.0\n * @example 'aab8e0a312e807c8eaeb923e2bdccd7731c5508869c62ad8e6e43ee881414c84'\n */\n checksum?: string;\n /**\n * The URL of the bundle to download.\n *\n * The bundle must be a ZIP file containing at least a `index.html` file.\n *\n * @since 5.0.0\n * @example 'https://example.com/bundle.zip'\n */\n url: string;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetBundleResult {\n /**\n * The unique identifier of the active bundle.\n *\n * If `null`, the default bundle is being used.\n *\n * @since 5.0.0\n * @example '1.0.0'\n */\n bundleId: string | null;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetBundlesResult {\n /**\n * An array of unique identifiers of all available bundles.\n *\n * @since 5.0.0\n */\n bundleIds: string[];\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetChannelResult {\n /**\n * The channel of the app.\n *\n * If `null`, the app is using the default channel.\n *\n * @since 5.0.0\n * @example 'production'\n */\n channel: string | null;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetDeviceIdResult {\n /**\n * The unique identifier of the device.\n *\n * On iOS, [`identifierForVendor`](https://developer.apple.com/documentation/uikit/uidevice/1620059-identifierforvendor) is used.\n * The value of this property is the same for apps that come from the same vendor running on the same device.\n *\n * @since 5.0.0\n *\n * @example '50d2a548-80b7-4dad-adc7-97c0e79d8a89'\n */\n deviceId: string;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetVersionCodeResult {\n /**\n * The version code of the app.\n *\n * On **Android**, this is the `versionCode` from the `android/app/build.gradle` file.\n * On **iOS**, this is the `CFBundleVersion` from the `Info.plist` file.\n *\n * @since 5.0.0\n * @example \"1\"\n */\n versionCode: string;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetVersionNameResult {\n /**\n * The version name of the app.\n *\n * On **Android**, this is the `versionName` from the `android/app/build.gradle` file.\n * On **iOS**, this is the `CFBundleShortVersionString` from the `Info.plist` file.\n *\n * @since 5.0.0\n * @example \"1.0.0\"\n */\n versionName: string;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetCustomIdResult {\n /**\n * The custom identifier of the app.\n *\n * If `null`, no custom identifier is set.\n *\n * @since 5.0.0\n * @example '50d2a548-80b7-4dad-adc7-97c0e79d8a89'\n */\n customId: string | null;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface SetBundleOptions {\n /**\n * The unique identifier of the bundle to use.\n *\n * @since 5.0.0\n * @example '1.0.0'\n */\n bundleId: string;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface SetChannelOptions {\n /**\n * The channel of the app.\n *\n * Set `null` to remove the channel.\n *\n * @since 5.0.0\n */\n channel: string | null;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface SetCustomIdOptions {\n /**\n * The custom identifier of the app.\n *\n * Set `null` to remove the custom identifier.\n *\n * @since 5.0.0\n */\n customId: string | null;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface SyncResult {\n /**\n * The identifier of the next bundle to use.\n *\n * If `null`, the app is up-to-date and no new bundle is available.\n *\n * @since 5.0.0\n */\n nextBundleId: string | null;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAAA,wCAAwC","sourcesContent":["/// <reference types=\"@capacitor/cli\" />\n\ndeclare module '@capacitor/cli' {\n export interface PluginsConfig {\n LiveUpdate?: {\n /**\n * The app ID is used to identify the app when using [Capawesome Cloud](https://capawesome.io/cloud).\n *\n * This is **NOT** the same as the app identifier (e.g. `com.example.app`).\n * This is a unique identifier generated by Capawesome Cloud (e.g. `6e351b4f-69a7-415e-a057-4567df7ffe94`).\n *\n * @since 5.0.0\n * @example '6e351b4f-69a7-415e-a057-4567df7ffe94'\n */\n appId?: string;\n /**\n * Whether or not to automatically delete unused bundles.\n *\n * When enabled, the plugin will automatically delete unused bundles after calling `ready()`.\n *\n * @since 5.0.0\n * @default false\n */\n autoDeleteBundles?: boolean;\n /**\n * The default channel of the app.\n *\n * @since 6.3.0\n * @example 'production'\n */\n defaultChannel?: string;\n /**\n * Whether or not the plugin is enabled.\n *\n * @since 5.0.0\n * @default true\n */\n enabled?: boolean;\n /**\n * The timeout in milliseconds for HTTP requests.\n *\n * @since 6.4.0\n * @default 60000\n */\n httpTimeout?: number;\n /**\n * The location of the server to use when using [Capawesome Cloud](https://capawesome.io/cloud).\n *\n * @since 6.2.0\n * @default 'us'\n * @example 'eu'\n * @see https://capawesome.io/cloud/live-updates/advanced/privacy/\n */\n location?: 'us' | 'eu';\n /**\n * The public key to verify the integrity of the bundle.\n *\n * The public key must be a PEM-encoded RSA public key.\n *\n * @since 6.1.0\n * @example '-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDodf1SD0OOn6hIlDuKBza0Ed0OqtwyVJwiyjmE9BJaZ7y8ZUfcF+SKmd0l2cDPM45XIg2tAFux5n29uoKyHwSt+6tCi5CJA5Z1/1eZruRRqABLonV77KS3HUtvOgqRLDnKSV89dYZkM++NwmzOPgIF422mvc+VukcVOBfc8/AHQIDAQAB-----END PUBLIC KEY-----'\n */\n publicKey?: string;\n /**\n * The timeout in milliseconds to wait for the app to be ready before resetting to the default bundle.\n *\n * @since 5.0.0\n * @default 10000\n */\n readyTimeout?: number;\n /**\n * Whether or not the app should be reset to the default bundle during an update.\n *\n * @since 5.0.0\n * @default true\n */\n resetOnUpdate?: boolean;\n };\n }\n}\n\nexport interface LiveUpdatePlugin {\n /**\n * Delete a bundle from the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n deleteBundle(options: DeleteBundleOptions): Promise<void>;\n /**\n * Download a bundle.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n downloadBundle(options: DownloadBundleOptions): Promise<void>;\n /**\n * Get the active bundle identifier.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getBundle(): Promise<GetBundleResult>;\n /**\n * Get all available bundle identifiers.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getBundles(): Promise<GetBundlesResult>;\n /**\n * Get the channel of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getChannel(): Promise<GetChannelResult>;\n /**\n * Get the custom identifier of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getCustomId(): Promise<GetCustomIdResult>;\n /**\n * Get the device identifier of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getDeviceId(): Promise<GetDeviceIdResult>;\n /**\n * Get the version code of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getVersionCode(): Promise<GetVersionCodeResult>;\n /**\n * Get the version name of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getVersionName(): Promise<GetVersionNameResult>;\n /**\n * Notify the plugin that the app is ready to use and no rollback is needed.\n *\n * **Attention**: This method should be called as soon as the app is ready to use\n * to prevent the app from being reset to the default bundle.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n ready(): Promise<void>;\n /**\n * Reload the app to apply the new bundle.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n reload(): Promise<void>;\n /**\n * Reset the app to the default bundle.\n *\n * Call `reload()` or restart the app to apply the changes.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n reset(): Promise<void>;\n /**\n * Set the next bundle to use for the app.\n *\n * Call `reload()` or restart the app to apply the changes.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n setBundle(options: SetBundleOptions): Promise<void>;\n /**\n * Set the channel of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n setChannel(options: SetChannelOptions): Promise<void>;\n /**\n * Set the custom identifier of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n setCustomId(options: SetCustomIdOptions): Promise<void>;\n /**\n * Automatically download and set the latest bundle for the app using the [Capawesome Cloud](https://capawesome.io/cloud/).\n *\n * Call `reload()` or restart the app to apply the changes.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n sync(): Promise<SyncResult>;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface DeleteBundleOptions {\n /**\n * The unique identifier of the bundle to delete.\n *\n * @since 5.0.0\n * @example '1.0.0'\n */\n bundleId: string;\n}\n\nexport interface DownloadBundleOptions {\n /**\n * The unique identifier of the bundle.\n *\n * @since 5.0.0\n * @example '1.0.0'\n */\n bundleId: string;\n /**\n * The checksum of the bundle to verify the integrity of the ZIP file.\n *\n * Must be a SHA-256 hash in hexadecimal format.\n *\n * @since 6.1.0\n * @example 'aab8e0a312e807c8eaeb923e2bdccd7731c5508869c62ad8e6e43ee881414c84'\n */\n checksum?: string;\n /**\n * The URL of the bundle to download.\n *\n * The bundle must be a ZIP file containing at least a `index.html` file.\n *\n * @since 5.0.0\n * @example 'https://example.com/bundle.zip'\n */\n url: string;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetBundleResult {\n /**\n * The unique identifier of the active bundle.\n *\n * If `null`, the default bundle is being used.\n *\n * @since 5.0.0\n * @example '1.0.0'\n */\n bundleId: string | null;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetBundlesResult {\n /**\n * An array of unique identifiers of all available bundles.\n *\n * @since 5.0.0\n */\n bundleIds: string[];\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetChannelResult {\n /**\n * The channel of the app.\n *\n * If `null`, the app is using the default channel.\n *\n * @since 5.0.0\n * @example 'production'\n */\n channel: string | null;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetDeviceIdResult {\n /**\n * The unique identifier of the device.\n *\n * On iOS, [`identifierForVendor`](https://developer.apple.com/documentation/uikit/uidevice/1620059-identifierforvendor) is used.\n * The value of this property is the same for apps that come from the same vendor running on the same device.\n *\n * @since 5.0.0\n *\n * @example '50d2a548-80b7-4dad-adc7-97c0e79d8a89'\n */\n deviceId: string;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetVersionCodeResult {\n /**\n * The version code of the app.\n *\n * On **Android**, this is the `versionCode` from the `android/app/build.gradle` file.\n * On **iOS**, this is the `CFBundleVersion` from the `Info.plist` file.\n *\n * @since 5.0.0\n * @example \"1\"\n */\n versionCode: string;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetVersionNameResult {\n /**\n * The version name of the app.\n *\n * On **Android**, this is the `versionName` from the `android/app/build.gradle` file.\n * On **iOS**, this is the `CFBundleShortVersionString` from the `Info.plist` file.\n *\n * @since 5.0.0\n * @example \"1.0.0\"\n */\n versionName: string;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetCustomIdResult {\n /**\n * The custom identifier of the app.\n *\n * If `null`, no custom identifier is set.\n *\n * @since 5.0.0\n * @example '50d2a548-80b7-4dad-adc7-97c0e79d8a89'\n */\n customId: string | null;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface SetBundleOptions {\n /**\n * The unique identifier of the bundle to use.\n *\n * @since 5.0.0\n * @example '1.0.0'\n */\n bundleId: string;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface SetChannelOptions {\n /**\n * The channel of the app.\n *\n * Set `null` to remove the channel.\n *\n * @since 5.0.0\n */\n channel: string | null;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface SetCustomIdOptions {\n /**\n * The custom identifier of the app.\n *\n * Set `null` to remove the custom identifier.\n *\n * @since 5.0.0\n */\n customId: string | null;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface SyncResult {\n /**\n * The identifier of the next bundle to use.\n *\n * If `null`, the app is up-to-date and no new bundle is available.\n *\n * @since 5.0.0\n */\n nextBundleId: string | null;\n}\n"]}
|
|
@@ -11,8 +11,10 @@ public enum CustomError: Error {
|
|
|
11
11
|
case checksumMismatch
|
|
12
12
|
case customIdMissing
|
|
13
13
|
case downloadFailed
|
|
14
|
+
case httpTimeout
|
|
14
15
|
case signatureMissing
|
|
15
16
|
case signatureVerificationFailed
|
|
17
|
+
case syncInProgress
|
|
16
18
|
case urlMissing
|
|
17
19
|
}
|
|
18
20
|
|
|
@@ -39,10 +41,14 @@ extension CustomError: LocalizedError {
|
|
|
39
41
|
return NSLocalizedString("customId must be provided.", comment: "customIdMissing")
|
|
40
42
|
case .downloadFailed:
|
|
41
43
|
return NSLocalizedString("Bundle could not be downloaded.", comment: "downloadFailed")
|
|
44
|
+
case .httpTimeout:
|
|
45
|
+
return NSLocalizedString("Request timed out.", comment: "httpTimeout")
|
|
42
46
|
case .signatureMissing:
|
|
43
47
|
return NSLocalizedString("Bundle does not contain a signature.", comment: "signatureMissing")
|
|
44
48
|
case .signatureVerificationFailed:
|
|
45
49
|
return NSLocalizedString("Signature verification failed.", comment: "signatureVerificationFailed")
|
|
50
|
+
case .syncInProgress:
|
|
51
|
+
return NSLocalizedString("Sync is already in progress.", comment: "syncInProgress")
|
|
46
52
|
case .urlMissing:
|
|
47
53
|
return NSLocalizedString("url must be provided.", comment: "urlMissing")
|
|
48
54
|
}
|
|
@@ -89,7 +89,7 @@ import CommonCrypto
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
@objc public func getChannel(completion: @escaping (Result?, Error?) -> Void) {
|
|
92
|
-
let channel =
|
|
92
|
+
let channel = getChannel()
|
|
93
93
|
let result = GetChannelResult(channel: channel)
|
|
94
94
|
completion(result, nil)
|
|
95
95
|
}
|
|
@@ -356,11 +356,22 @@ import CommonCrypto
|
|
|
356
356
|
let temporaryZipFileUrl = self.cachesDirectoryUrl.appendingPathComponent(timestamp + ".zip")
|
|
357
357
|
return (temporaryZipFileUrl, [.createIntermediateDirectories])
|
|
358
358
|
}
|
|
359
|
-
|
|
360
|
-
|
|
359
|
+
var request = URLRequest(url: URL(string: url)!)
|
|
360
|
+
request.httpMethod = HTTPMethod.get.rawValue
|
|
361
|
+
request.timeoutInterval = Double(config.httpTimeout) / 1000.0
|
|
362
|
+
AF.download(request, to: destination).validate().response { response in
|
|
361
363
|
if let error = response.error {
|
|
362
364
|
CAPLog.print("[", LiveUpdatePlugin.tag, "] ", error)
|
|
363
|
-
|
|
365
|
+
if let urlError = error.underlyingError as? URLError {
|
|
366
|
+
switch urlError.code {
|
|
367
|
+
case .timedOut:
|
|
368
|
+
completion(nil, CustomError.httpTimeout)
|
|
369
|
+
default:
|
|
370
|
+
completion(nil, CustomError.downloadFailed)
|
|
371
|
+
}
|
|
372
|
+
} else {
|
|
373
|
+
completion(nil, CustomError.downloadFailed)
|
|
374
|
+
}
|
|
364
375
|
return
|
|
365
376
|
}
|
|
366
377
|
completion(response.fileURL, nil)
|
|
@@ -372,7 +383,7 @@ import CommonCrypto
|
|
|
372
383
|
parameters["appVersionCode"] = getVersionCode()
|
|
373
384
|
parameters["appVersionName"] = getVersionName()
|
|
374
385
|
parameters["bundleId"] = getCurrentBundleId()
|
|
375
|
-
parameters["channelName"] =
|
|
386
|
+
parameters["channelName"] = getChannel()
|
|
376
387
|
parameters["customId"] = preferences.getCustomId()
|
|
377
388
|
parameters["deviceId"] = getDeviceId()
|
|
378
389
|
parameters["osVersion"] = UIDevice.current.systemVersion
|
|
@@ -384,35 +395,34 @@ import CommonCrypto
|
|
|
384
395
|
host = "api.cloud.capawesome.eu"
|
|
385
396
|
}
|
|
386
397
|
}
|
|
387
|
-
|
|
388
|
-
|
|
398
|
+
var urlComponents = URLComponents(string: "https://\(host)/v1/apps/\(config.appId ?? "")/bundles/latest")!
|
|
399
|
+
urlComponents.queryItems = parameters.map { URLQueryItem(name: $0.key, value: $0.value) }
|
|
400
|
+
var request = URLRequest(url: urlComponents.url!)
|
|
401
|
+
request.httpMethod = HTTPMethod.get.rawValue
|
|
402
|
+
request.timeoutInterval = Double(config.httpTimeout) / 1000.0
|
|
403
|
+
AF.request(request).validate().responseDecodable(of: GetLatestBundleResponse.self) { response in
|
|
389
404
|
CAPLog.print("[", LiveUpdatePlugin.tag, "] Fetching latest bundle from ", response.request?.url?.absoluteString ?? "")
|
|
390
405
|
if let error = response.error {
|
|
391
406
|
CAPLog.print("[", LiveUpdatePlugin.tag, "] ", error)
|
|
392
407
|
if let data = response.data {
|
|
393
408
|
CAPLog.print("[", LiveUpdatePlugin.tag, "] ", String(decoding: data, as: UTF8.self))
|
|
394
409
|
}
|
|
395
|
-
|
|
410
|
+
if let urlError = error.underlyingError as? URLError {
|
|
411
|
+
switch urlError.code {
|
|
412
|
+
case .timedOut:
|
|
413
|
+
completion(nil, CustomError.httpTimeout)
|
|
414
|
+
default:
|
|
415
|
+
completion(nil, error)
|
|
416
|
+
}
|
|
417
|
+
} else {
|
|
418
|
+
completion(nil, error)
|
|
419
|
+
}
|
|
396
420
|
return
|
|
397
421
|
}
|
|
398
422
|
completion(response.value, nil)
|
|
399
423
|
}
|
|
400
424
|
}
|
|
401
425
|
|
|
402
|
-
private func getVersionCode() -> String {
|
|
403
|
-
guard let appVersionCode = Bundle.main.infoDictionary?["CFBundleVersion"] as? String else {
|
|
404
|
-
return ""
|
|
405
|
-
}
|
|
406
|
-
return appVersionCode
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
private func getVersionName() -> String {
|
|
410
|
-
guard let appVersionName = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else {
|
|
411
|
-
return ""
|
|
412
|
-
}
|
|
413
|
-
return appVersionName
|
|
414
|
-
}
|
|
415
|
-
|
|
416
426
|
private func getBundleIds() -> [String] {
|
|
417
427
|
let url = libraryDirectoryUrl.appendingPathComponent(bundlesDirectory)
|
|
418
428
|
do {
|
|
@@ -427,6 +437,17 @@ import CommonCrypto
|
|
|
427
437
|
}
|
|
428
438
|
}
|
|
429
439
|
|
|
440
|
+
private func getChannel() -> String? {
|
|
441
|
+
var channel: String?
|
|
442
|
+
if let _ = config.defaultChannel {
|
|
443
|
+
channel = config.defaultChannel
|
|
444
|
+
}
|
|
445
|
+
if let _ = preferences.getChannel() {
|
|
446
|
+
channel = preferences.getChannel()
|
|
447
|
+
}
|
|
448
|
+
return channel
|
|
449
|
+
}
|
|
450
|
+
|
|
430
451
|
/// - Returns: The current bundle ID (`public` for the built-in bundle) or `nil` if no view controller was found.
|
|
431
452
|
private func getCurrentBundleId() -> String? {
|
|
432
453
|
guard let path = getCurrentCapacitorServerPath() else {
|
|
@@ -463,6 +484,20 @@ import CommonCrypto
|
|
|
463
484
|
return defaultCapacitorServerPath
|
|
464
485
|
}
|
|
465
486
|
|
|
487
|
+
private func getVersionCode() -> String {
|
|
488
|
+
guard let appVersionCode = Bundle.main.infoDictionary?["CFBundleVersion"] as? String else {
|
|
489
|
+
return ""
|
|
490
|
+
}
|
|
491
|
+
return appVersionCode
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
private func getVersionName() -> String {
|
|
495
|
+
guard let appVersionName = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else {
|
|
496
|
+
return ""
|
|
497
|
+
}
|
|
498
|
+
return appVersionName
|
|
499
|
+
}
|
|
500
|
+
|
|
466
501
|
private func hasBundle(bundleId: String) -> Bool {
|
|
467
502
|
let path = buildBundlePathFor(bundleId: bundleId)
|
|
468
503
|
return FileManager.default.fileExists(atPath: path)
|
|
@@ -486,11 +521,28 @@ import CommonCrypto
|
|
|
486
521
|
}
|
|
487
522
|
|
|
488
523
|
private func searchIndexHtmlFile(url: URL) -> URL? {
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
if
|
|
492
|
-
return
|
|
524
|
+
do {
|
|
525
|
+
let directoryContents = try FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil, options: [])
|
|
526
|
+
if directoryContents.isEmpty {
|
|
527
|
+
return nil
|
|
493
528
|
}
|
|
529
|
+
let fileNames = directoryContents.map { $0.lastPathComponent }
|
|
530
|
+
if fileNames.contains("index.html") {
|
|
531
|
+
return url.appendingPathComponent("index.html")
|
|
532
|
+
} else {
|
|
533
|
+
for fileUrl in directoryContents {
|
|
534
|
+
var isDirectory: ObjCBool = false
|
|
535
|
+
if FileManager.default.fileExists(atPath: fileUrl.path, isDirectory: &isDirectory) {
|
|
536
|
+
if isDirectory.boolValue {
|
|
537
|
+
if let indexHtmlFile = searchIndexHtmlFile(url: fileUrl) {
|
|
538
|
+
return indexHtmlFile
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
} catch {
|
|
545
|
+
CAPLog.print("[", LiveUpdatePlugin.tag, "] ", "Failed to search index.html file: \(error.localizedDescription)")
|
|
494
546
|
}
|
|
495
547
|
return nil
|
|
496
548
|
}
|
|
@@ -8,11 +8,12 @@ import Capacitor
|
|
|
8
8
|
@objc(LiveUpdatePlugin)
|
|
9
9
|
public class LiveUpdatePlugin: CAPPlugin {
|
|
10
10
|
public static let tag = "LiveUpdate"
|
|
11
|
-
public static let version = "6.
|
|
11
|
+
public static let version = "6.4.0"
|
|
12
12
|
public static let userDefaultsPrefix = "CapawesomeLiveUpdate" // DO NOT CHANGE
|
|
13
13
|
|
|
14
14
|
private var config: LiveUpdateConfig?
|
|
15
15
|
private var implementation: LiveUpdate?
|
|
16
|
+
private var syncInProgress = false
|
|
16
17
|
|
|
17
18
|
override public func load() {
|
|
18
19
|
self.config = liveUpdateConfig()
|
|
@@ -222,7 +223,15 @@ public class LiveUpdatePlugin: CAPPlugin {
|
|
|
222
223
|
call.reject(CustomError.appIdMissing.localizedDescription)
|
|
223
224
|
return
|
|
224
225
|
}
|
|
226
|
+
|
|
227
|
+
guard !syncInProgress else {
|
|
228
|
+
call.reject(CustomError.syncInProgress.localizedDescription)
|
|
229
|
+
return
|
|
230
|
+
}
|
|
231
|
+
syncInProgress = true
|
|
232
|
+
|
|
225
233
|
implementation?.sync(completion: { result, error in
|
|
234
|
+
self.syncInProgress = false
|
|
226
235
|
if let error = error {
|
|
227
236
|
CAPLog.print("[", LiveUpdatePlugin.tag, "] ", error)
|
|
228
237
|
call.reject(error.localizedDescription)
|
|
@@ -239,7 +248,9 @@ public class LiveUpdatePlugin: CAPPlugin {
|
|
|
239
248
|
|
|
240
249
|
config.appId = getConfig().getString("appId", config.appId)
|
|
241
250
|
config.autoDeleteBundles = getConfig().getBoolean("autoDeleteBundles", config.autoDeleteBundles)
|
|
251
|
+
config.defaultChannel = getConfig().getString("defaultChannel", config.defaultChannel)
|
|
242
252
|
config.enabled = getConfig().getBoolean("enabled", config.enabled)
|
|
253
|
+
config.httpTimeout = getConfig().getInt("httpTimeout", config.httpTimeout)
|
|
243
254
|
config.location = getConfig().getString("location", config.location)
|
|
244
255
|
config.publicKey = getConfig().getString("publicKey", config.publicKey)
|
|
245
256
|
config.readyTimeout = getConfig().getInt("readyTimeout", config.readyTimeout)
|