@capawesome/capacitor-live-update 8.2.1-dev.70823ec8.1778138208 → 8.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CapawesomeCapacitorLiveUpdate.podspec +0 -15
- package/Package.swift +5 -21
- package/README.md +2 -8
- package/android/build.gradle +0 -7
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/LiveUpdate.java +6 -18
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/LiveUpdatePlugin.java +0 -8
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/classes/options/FetchLatestBundleOptions.java +0 -15
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/classes/results/FetchLatestBundleResult.java +0 -30
- package/ios/Plugin/Classes/Options/FetchLatestBundleOptions.swift +0 -12
- package/ios/Plugin/Classes/Results/FetchLatestBundleResult.swift +0 -24
- package/ios/Plugin/Enums/CustomError.swift +0 -9
- package/ios/Plugin/LiveUpdate.swift +1 -12
- package/ios/Plugin/LiveUpdatePlugin.swift +0 -19
- package/package.json +1 -1
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/providers/ionic/LiveUpdateIonicManager.java +0 -232
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/providers/ionic/LiveUpdateIonicProvider.java +0 -41
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/providers/ionic/LiveUpdateIonicProviderRegistration.java +0 -46
- package/ios/Plugin/Providers/Ionic/LiveUpdateIonicManager.swift +0 -118
- package/ios/Plugin/Providers/Ionic/LiveUpdateIonicProvider.swift +0 -25
|
@@ -17,19 +17,4 @@ Pod::Spec.new do |s|
|
|
|
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', '0.1.0-alpha.2'
|
|
31
|
-
ss.pod_target_xcconfig = {
|
|
32
|
-
'OTHER_SWIFT_FLAGS' => '$(inherited) -DCAPAWESOME_INCLUDE_IONIC_PROVIDER'
|
|
33
|
-
}
|
|
34
|
-
end
|
|
35
20
|
end
|
package/Package.swift
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// swift-tools-version:
|
|
1
|
+
// swift-tools-version: 5.9
|
|
2
2
|
import PackageDescription
|
|
3
3
|
|
|
4
4
|
let package = Package(
|
|
@@ -9,17 +9,10 @@ let package = Package(
|
|
|
9
9
|
name: "CapawesomeCapacitorLiveUpdate",
|
|
10
10
|
targets: ["LiveUpdatePlugin"])
|
|
11
11
|
],
|
|
12
|
-
traits: [
|
|
13
|
-
.trait(
|
|
14
|
-
name: "IonicProvider",
|
|
15
|
-
description: "Enables registration with the Ionic Live Update Provider SDK."
|
|
16
|
-
)
|
|
17
|
-
],
|
|
18
12
|
dependencies: [
|
|
19
13
|
.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0"),
|
|
20
14
|
.package(url: "https://github.com/Alamofire/Alamofire.git", .upToNextMinor(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", exact: "0.1.0-alpha.2")
|
|
15
|
+
.package(url: "https://github.com/weichsel/ZIPFoundation.git", .upToNextMinor(from: "0.9.0"))
|
|
23
16
|
],
|
|
24
17
|
targets: [
|
|
25
18
|
.target(
|
|
@@ -28,21 +21,12 @@ let package = Package(
|
|
|
28
21
|
.product(name: "Capacitor", package: "capacitor-swift-pm"),
|
|
29
22
|
.product(name: "Cordova", package: "capacitor-swift-pm"),
|
|
30
23
|
.product(name: "Alamofire", package: "Alamofire"),
|
|
31
|
-
.product(name: "ZIPFoundation", package: "ZIPFoundation")
|
|
32
|
-
.product(
|
|
33
|
-
name: "LiveUpdateProvider",
|
|
34
|
-
package: "live-update-provider-sdk",
|
|
35
|
-
condition: .when(traits: ["IonicProvider"])
|
|
36
|
-
)
|
|
24
|
+
.product(name: "ZIPFoundation", package: "ZIPFoundation")
|
|
37
25
|
],
|
|
38
|
-
path: "ios/Plugin",
|
|
39
|
-
swiftSettings: [
|
|
40
|
-
.define("CAPAWESOME_INCLUDE_IONIC_PROVIDER", .when(traits: ["IonicProvider"]))
|
|
41
|
-
]),
|
|
26
|
+
path: "ios/Plugin"),
|
|
42
27
|
.testTarget(
|
|
43
28
|
name: "LiveUpdatePluginTests",
|
|
44
29
|
dependencies: ["LiveUpdatePlugin"],
|
|
45
30
|
path: "ios/PluginTests")
|
|
46
|
-
]
|
|
47
|
-
swiftLanguageModes: [.v5]
|
|
31
|
+
]
|
|
48
32
|
)
|
package/README.md
CHANGED
|
@@ -25,8 +25,6 @@ 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).
|
|
30
28
|
- 🔒 **Security**: Verify the authenticity and integrity of the bundle using a public key.
|
|
31
29
|
- ⚔️ **Battle-Tested**: Used in more than 1,000 projects to update apps on more than 20,000,000 devices.
|
|
32
30
|
- 🌐 **Open Source**: Licensed under the MIT License.
|
|
@@ -42,8 +40,8 @@ Stay up to date with the latest news and updates about the Capawesome, Capacitor
|
|
|
42
40
|
| Plugin Version | Capacitor Version | Status |
|
|
43
41
|
| -------------- | ----------------- | -------------- |
|
|
44
42
|
| 8.x.x | >=8.x.x | Active support |
|
|
45
|
-
| 7.x.x | 7.x.x |
|
|
46
|
-
| 6.x.x | 6.x.x |
|
|
43
|
+
| 7.x.x | 7.x.x | Deprecated |
|
|
44
|
+
| 6.x.x | 6.x.x | Deprecated |
|
|
47
45
|
| 5.x.x | 5.x.x | Deprecated |
|
|
48
46
|
|
|
49
47
|
## Guides
|
|
@@ -141,10 +139,6 @@ Add the `NSPrivacyAccessedAPICategoryUserDefaults` dictionary key to your [Priva
|
|
|
141
139
|
|
|
142
140
|
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.
|
|
143
141
|
|
|
144
|
-
## Ionic Live Update Provider SDK integration
|
|
145
|
-
|
|
146
|
-
This plugin can register itself as a provider for the [Ionic Live Update Provider SDK](https://github.com/ionic-team/live-update-provider-sdk), so that **Ionic Portals** and **Federated Capacitor** apps can use Capawesome Cloud to deliver live updates. 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.
|
|
147
|
-
|
|
148
142
|
## Configuration
|
|
149
143
|
|
|
150
144
|
<docgen-config>
|
package/android/build.gradle
CHANGED
|
@@ -5,8 +5,6 @@ ext {
|
|
|
5
5
|
androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.7.0'
|
|
6
6
|
okhttp3Version = project.hasProperty('okhttp3Version') ? rootProject.ext.okhttp3Version : '5.3.2'
|
|
7
7
|
zip4jVersion = project.hasProperty('zip4jVersion') ? rootProject.ext.zip4jVersion : '2.11.5'
|
|
8
|
-
capawesomeCapacitorLiveUpdateIncludeIonicProvider = project.hasProperty('capawesomeCapacitorLiveUpdateIncludeIonicProvider') ? rootProject.ext.capawesomeCapacitorLiveUpdateIncludeIonicProvider : false
|
|
9
|
-
ionicLiveUpdateProviderVersion = project.hasProperty('ionicLiveUpdateProviderVersion') ? rootProject.ext.ionicLiveUpdateProviderVersion : '0.1.0-alpha.2'
|
|
10
8
|
}
|
|
11
9
|
|
|
12
10
|
buildscript {
|
|
@@ -58,11 +56,6 @@ dependencies {
|
|
|
58
56
|
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
|
|
59
57
|
implementation "net.lingala.zip4j:zip4j:$zip4jVersion"
|
|
60
58
|
implementation "com.squareup.okhttp3:okhttp:$okhttp3Version"
|
|
61
|
-
if (capawesomeCapacitorLiveUpdateIncludeIonicProvider) {
|
|
62
|
-
implementation "io.ionic:liveupdateprovider:$ionicLiveUpdateProviderVersion"
|
|
63
|
-
} else {
|
|
64
|
-
compileOnly "io.ionic:liveupdateprovider:$ionicLiveUpdateProviderVersion"
|
|
65
|
-
}
|
|
66
59
|
testImplementation "junit:junit:$junitVersion"
|
|
67
60
|
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
|
|
68
61
|
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
|
|
@@ -298,17 +298,6 @@ public class LiveUpdate {
|
|
|
298
298
|
callback.success(result);
|
|
299
299
|
}
|
|
300
300
|
|
|
301
|
-
/**
|
|
302
|
-
* @return The on-disk directory of a downloaded bundle, or `null` if the bundle does not exist.
|
|
303
|
-
*/
|
|
304
|
-
@Nullable
|
|
305
|
-
public File getBundleDirectory(@NonNull String bundleId) {
|
|
306
|
-
if (!hasBundleById(bundleId)) {
|
|
307
|
-
return null;
|
|
308
|
-
}
|
|
309
|
-
return buildBundleDirectoryFor(bundleId);
|
|
310
|
-
}
|
|
311
|
-
|
|
312
301
|
public void getCustomId(@NonNull NonEmptyCallback callback) {
|
|
313
302
|
String customId = preferences.getCustomId();
|
|
314
303
|
GetCustomIdResult result = new GetCustomIdResult(customId);
|
|
@@ -678,8 +667,11 @@ public class LiveUpdate {
|
|
|
678
667
|
|
|
679
668
|
private void deleteFileRecursively(@NonNull File file) {
|
|
680
669
|
if (file.isDirectory()) {
|
|
681
|
-
|
|
682
|
-
|
|
670
|
+
File[] children = file.listFiles();
|
|
671
|
+
if (children != null) {
|
|
672
|
+
for (File child : children) {
|
|
673
|
+
deleteFileRecursively(child);
|
|
674
|
+
}
|
|
683
675
|
}
|
|
684
676
|
}
|
|
685
677
|
file.delete();
|
|
@@ -983,17 +975,13 @@ public class LiveUpdate {
|
|
|
983
975
|
@NonNull NonEmptyCallback<GetLatestBundleResponse> callback
|
|
984
976
|
) {
|
|
985
977
|
try {
|
|
986
|
-
String appId = options.getAppId() == null ? getAppId() : options.getAppId();
|
|
987
|
-
if (appId == null || appId.isEmpty()) {
|
|
988
|
-
throw new Exception(LiveUpdatePlugin.ERROR_APP_ID_MISSING);
|
|
989
|
-
}
|
|
990
978
|
String channel = options.getChannel() == null ? getChannel() : options.getChannel();
|
|
991
979
|
String url = new HttpUrl.Builder()
|
|
992
980
|
.scheme("https")
|
|
993
981
|
.host(config.getServerDomain())
|
|
994
982
|
.addPathSegment("v1")
|
|
995
983
|
.addPathSegment("apps")
|
|
996
|
-
.addPathSegment(
|
|
984
|
+
.addPathSegment(getAppId())
|
|
997
985
|
.addPathSegment("bundles")
|
|
998
986
|
.addPathSegment("latest")
|
|
999
987
|
.addQueryParameter("appVersionCode", getVersionCodeAsString())
|
package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/LiveUpdatePlugin.java
CHANGED
|
@@ -32,7 +32,6 @@ import io.capawesome.capacitorjs.plugins.liveupdate.classes.results.IsSyncingRes
|
|
|
32
32
|
import io.capawesome.capacitorjs.plugins.liveupdate.interfaces.EmptyCallback;
|
|
33
33
|
import io.capawesome.capacitorjs.plugins.liveupdate.interfaces.NonEmptyCallback;
|
|
34
34
|
import io.capawesome.capacitorjs.plugins.liveupdate.interfaces.Result;
|
|
35
|
-
import io.capawesome.capacitorjs.plugins.liveupdate.providers.ionic.LiveUpdateIonicProviderRegistration;
|
|
36
35
|
|
|
37
36
|
@CapacitorPlugin(name = "LiveUpdate")
|
|
38
37
|
public class LiveUpdatePlugin extends Plugin {
|
|
@@ -41,7 +40,6 @@ public class LiveUpdatePlugin extends Plugin {
|
|
|
41
40
|
public static final String VERSION = "8.2.0";
|
|
42
41
|
public static final String SHARED_PREFERENCES_NAME = "CapawesomeLiveUpdate"; // DO NOT CHANGE
|
|
43
42
|
public static final String ERROR_APP_ID_MISSING = "appId must be configured.";
|
|
44
|
-
public static final String ERROR_BUNDLE_DIRECTORY_NOT_FOUND = "Bundle directory could not be resolved.";
|
|
45
43
|
public static final String ERROR_BUNDLE_EXISTS = "bundle already exists.";
|
|
46
44
|
public static final String ERROR_BUNDLE_ID_MISSING = "bundleId must be provided.";
|
|
47
45
|
public static final String ERROR_BUNDLE_INDEX_HTML_MISSING = "The bundle does not contain an index.html file.";
|
|
@@ -50,9 +48,7 @@ public class LiveUpdatePlugin extends Plugin {
|
|
|
50
48
|
public static final String ERROR_CHECKSUM_MISMATCH = "Checksum mismatch.";
|
|
51
49
|
public static final String ERROR_CUSTOM_ID_MISSING = "customId must be provided.";
|
|
52
50
|
public static final String ERROR_DOWNLOAD_FAILED = "Bundle could not be downloaded.";
|
|
53
|
-
public static final String ERROR_DOWNLOAD_URL_MISSING = "Bundle does not have a valid download URL.";
|
|
54
51
|
public static final String ERROR_HTTP_TIMEOUT = "Request timed out.";
|
|
55
|
-
public static final String ERROR_MANAGER_KEY_MISSING = "managerKey must be provided.";
|
|
56
52
|
public static final String ERROR_URL_MISSING = "url must be provided.";
|
|
57
53
|
public static final String ERROR_SIGNATURE_VERIFICATION_FAILED = "Signature verification failed.";
|
|
58
54
|
public static final String ERROR_PUBLIC_KEY_INVALID = "Invalid public key.";
|
|
@@ -75,10 +71,6 @@ public class LiveUpdatePlugin extends Plugin {
|
|
|
75
71
|
try {
|
|
76
72
|
config = getLiveUpdateConfig();
|
|
77
73
|
implementation = new LiveUpdate(config, this);
|
|
78
|
-
// Register the Ionic Live Update Provider when the optional SDK is on the classpath.
|
|
79
|
-
if (LiveUpdateIonicProviderRegistration.isAvailable()) {
|
|
80
|
-
LiveUpdateIonicProviderRegistration.tryRegister(implementation);
|
|
81
|
-
}
|
|
82
74
|
} catch (Exception exception) {
|
|
83
75
|
Logger.error(TAG, exception.getMessage(), exception);
|
|
84
76
|
}
|
|
@@ -6,32 +6,17 @@ import com.getcapacitor.PluginCall;
|
|
|
6
6
|
|
|
7
7
|
public class FetchLatestBundleOptions {
|
|
8
8
|
|
|
9
|
-
@Nullable
|
|
10
|
-
private final String appId;
|
|
11
|
-
|
|
12
9
|
@Nullable
|
|
13
10
|
private final String channel;
|
|
14
11
|
|
|
15
12
|
public FetchLatestBundleOptions(@NonNull PluginCall call) {
|
|
16
|
-
this.appId = null;
|
|
17
13
|
this.channel = call.getString("channel", null);
|
|
18
14
|
}
|
|
19
15
|
|
|
20
16
|
public FetchLatestBundleOptions(@Nullable String channel) {
|
|
21
|
-
this.appId = null;
|
|
22
|
-
this.channel = channel;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
public FetchLatestBundleOptions(@Nullable String appId, @Nullable String channel) {
|
|
26
|
-
this.appId = appId;
|
|
27
17
|
this.channel = channel;
|
|
28
18
|
}
|
|
29
19
|
|
|
30
|
-
@Nullable
|
|
31
|
-
public String getAppId() {
|
|
32
|
-
return appId;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
20
|
@Nullable
|
|
36
21
|
public String getChannel() {
|
|
37
22
|
return channel;
|
|
@@ -43,36 +43,6 @@ public class FetchLatestBundleResult implements Result {
|
|
|
43
43
|
this.signature = signature;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
@Nullable
|
|
47
|
-
public ArtifactType getArtifactType() {
|
|
48
|
-
return artifactType;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
@Nullable
|
|
52
|
-
public String getBundleId() {
|
|
53
|
-
return bundleId;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
@Nullable
|
|
57
|
-
public String getChecksum() {
|
|
58
|
-
return checksum;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
@Nullable
|
|
62
|
-
public JSONObject getCustomProperties() {
|
|
63
|
-
return customProperties;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
@Nullable
|
|
67
|
-
public String getDownloadUrl() {
|
|
68
|
-
return downloadUrl;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
@Nullable
|
|
72
|
-
public String getSignature() {
|
|
73
|
-
return signature;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
46
|
@NonNull
|
|
77
47
|
public JSObject toJSObject() {
|
|
78
48
|
JSObject result = new JSObject();
|
|
@@ -2,28 +2,16 @@ import Foundation
|
|
|
2
2
|
import Capacitor
|
|
3
3
|
|
|
4
4
|
@objc public class FetchLatestBundleOptions: NSObject {
|
|
5
|
-
private var appId: String?
|
|
6
5
|
private var channel: String?
|
|
7
6
|
|
|
8
7
|
init(_ call: CAPPluginCall) {
|
|
9
|
-
self.appId = nil
|
|
10
8
|
self.channel = call.getString("channel")
|
|
11
9
|
}
|
|
12
10
|
|
|
13
11
|
init(channel: String?) {
|
|
14
|
-
self.appId = nil
|
|
15
12
|
self.channel = channel
|
|
16
13
|
}
|
|
17
14
|
|
|
18
|
-
init(appId: String?, channel: String?) {
|
|
19
|
-
self.appId = appId
|
|
20
|
-
self.channel = channel
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
func getAppId() -> String? {
|
|
24
|
-
return appId
|
|
25
|
-
}
|
|
26
|
-
|
|
27
15
|
func getChannel() -> String? {
|
|
28
16
|
return channel
|
|
29
17
|
}
|
|
@@ -18,30 +18,6 @@ import Capacitor
|
|
|
18
18
|
self.signature = signature
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
public func getArtifactType() -> String? {
|
|
22
|
-
return artifactType?.rawValue
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
public func getBundleId() -> String? {
|
|
26
|
-
return bundleId
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
public func getChecksum() -> String? {
|
|
30
|
-
return checksum
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
public func getCustomProperties() -> [String: Any]? {
|
|
34
|
-
return customProperties
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
public func getDownloadUrl() -> String? {
|
|
38
|
-
return downloadUrl
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
public func getSignature() -> String? {
|
|
42
|
-
return signature
|
|
43
|
-
}
|
|
44
|
-
|
|
45
21
|
public func toJSObject() -> AnyObject {
|
|
46
22
|
var result = JSObject()
|
|
47
23
|
if artifactType == .manifest {
|
|
@@ -3,7 +3,6 @@ import Foundation
|
|
|
3
3
|
public enum CustomError: Error {
|
|
4
4
|
case appIdMissing
|
|
5
5
|
case bundleAlreadyExists
|
|
6
|
-
case bundleDirectoryNotFound
|
|
7
6
|
case bundleIdMissing
|
|
8
7
|
case bundleIndexHtmlMissing
|
|
9
8
|
case bundleNotFound
|
|
@@ -13,9 +12,7 @@ public enum CustomError: Error {
|
|
|
13
12
|
case checksumMismatch
|
|
14
13
|
case customIdMissing
|
|
15
14
|
case downloadFailed
|
|
16
|
-
case downloadUrlMissing
|
|
17
15
|
case httpTimeout
|
|
18
|
-
case managerKeyMissing
|
|
19
16
|
case signatureMissing
|
|
20
17
|
case signatureVerificationFailed
|
|
21
18
|
case syncInProgress
|
|
@@ -30,8 +27,6 @@ extension CustomError: LocalizedError {
|
|
|
30
27
|
return NSLocalizedString("appId must be configured.", comment: "appIdMissing")
|
|
31
28
|
case .bundleAlreadyExists:
|
|
32
29
|
return NSLocalizedString("bundle already exists.", comment: "bundleAlreadyExists")
|
|
33
|
-
case .bundleDirectoryNotFound:
|
|
34
|
-
return NSLocalizedString("Bundle directory could not be resolved.", comment: "bundleDirectoryNotFound")
|
|
35
30
|
case .bundleIdMissing:
|
|
36
31
|
return NSLocalizedString("bundleId must be provided.", comment: "bundleIdMissing")
|
|
37
32
|
case .bundleIndexHtmlMissing:
|
|
@@ -50,12 +45,8 @@ extension CustomError: LocalizedError {
|
|
|
50
45
|
return NSLocalizedString("customId must be provided.", comment: "customIdMissing")
|
|
51
46
|
case .downloadFailed:
|
|
52
47
|
return NSLocalizedString("Bundle could not be downloaded.", comment: "downloadFailed")
|
|
53
|
-
case .downloadUrlMissing:
|
|
54
|
-
return NSLocalizedString("Bundle does not have a valid download URL.", comment: "downloadUrlMissing")
|
|
55
48
|
case .httpTimeout:
|
|
56
49
|
return NSLocalizedString("Request timed out.", comment: "httpTimeout")
|
|
57
|
-
case .managerKeyMissing:
|
|
58
|
-
return NSLocalizedString("managerKey must be provided.", comment: "managerKeyMissing")
|
|
59
50
|
case .signatureMissing:
|
|
60
51
|
return NSLocalizedString("Bundle does not contain a signature.", comment: "signatureMissing")
|
|
61
52
|
case .signatureVerificationFailed:
|
|
@@ -161,14 +161,6 @@ import CommonCrypto
|
|
|
161
161
|
completion(result, nil)
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
-
/// - Returns: The on-disk URL of a downloaded bundle, or `nil` if the bundle does not exist.
|
|
165
|
-
public func getBundleDirectory(bundleId: String) -> URL? {
|
|
166
|
-
guard hasBundleById(bundleId) else {
|
|
167
|
-
return nil
|
|
168
|
-
}
|
|
169
|
-
return buildBundleURLFor(bundleId: bundleId)
|
|
170
|
-
}
|
|
171
|
-
|
|
172
164
|
@objc public func getCustomId(completion: @escaping (Result?, Error?) -> Void) {
|
|
173
165
|
let customId = preferences.getCustomId()
|
|
174
166
|
|
|
@@ -588,9 +580,6 @@ import CommonCrypto
|
|
|
588
580
|
}
|
|
589
581
|
|
|
590
582
|
private func fetchLatestBundle(_ options: FetchLatestBundleOptions) async throws -> GetLatestBundleResponse? {
|
|
591
|
-
guard let appId = options.getAppId() ?? getAppId(), !appId.isEmpty else {
|
|
592
|
-
throw CustomError.appIdMissing
|
|
593
|
-
}
|
|
594
583
|
let channel = options.getChannel() ?? getChannel()
|
|
595
584
|
var parameters = [String: String]()
|
|
596
585
|
parameters["appVersionCode"] = getVersionCode()
|
|
@@ -602,7 +591,7 @@ import CommonCrypto
|
|
|
602
591
|
parameters["osVersion"] = await UIDevice.current.systemVersion
|
|
603
592
|
parameters["platform"] = "1"
|
|
604
593
|
parameters["pluginVersion"] = LiveUpdatePlugin.version
|
|
605
|
-
var urlComponents = URLComponents(string: "https://\(config.serverDomain)/v1/apps/\(
|
|
594
|
+
var urlComponents = URLComponents(string: "https://\(config.serverDomain)/v1/apps/\(getAppId() ?? "")/bundles/latest")!
|
|
606
595
|
urlComponents.queryItems = parameters.map { URLQueryItem(name: $0.key, value: $0.value) }
|
|
607
596
|
let url = try urlComponents.asURL()
|
|
608
597
|
CAPLog.print("[", LiveUpdatePlugin.tag, "] Fetching latest bundle: ", url)
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
import Capacitor
|
|
3
|
-
#if CAPAWESOME_INCLUDE_IONIC_PROVIDER
|
|
4
|
-
import LiveUpdateProvider
|
|
5
|
-
#endif
|
|
6
3
|
|
|
7
4
|
/**
|
|
8
5
|
* Please read the Capacitor iOS Plugin Development Guide
|
|
@@ -59,22 +56,6 @@ public class LiveUpdatePlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
59
56
|
// Notify implementation about load
|
|
60
57
|
implementation?.handleLoad()
|
|
61
58
|
|
|
62
|
-
// Register the Ionic Live Update Provider when the optional SDK is linked.
|
|
63
|
-
#if CAPAWESOME_INCLUDE_IONIC_PROVIDER
|
|
64
|
-
if let implementation = implementation {
|
|
65
|
-
do {
|
|
66
|
-
if LiveUpdateProviderRegistry.shared.resolve(LiveUpdateIonicProvider.id) == nil {
|
|
67
|
-
try LiveUpdateProviderRegistry.shared.register(
|
|
68
|
-
LiveUpdateIonicProvider(liveUpdate: implementation)
|
|
69
|
-
)
|
|
70
|
-
CAPLog.print("[", LiveUpdatePlugin.tag, "] Registered Ionic provider '\(LiveUpdateIonicProvider.id)'.")
|
|
71
|
-
}
|
|
72
|
-
} catch {
|
|
73
|
-
CAPLog.print("[", LiveUpdatePlugin.tag, "] Failed to register Ionic provider: ", error.localizedDescription)
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
#endif
|
|
77
|
-
|
|
78
59
|
// Register for app resume notifications
|
|
79
60
|
NotificationCenter.default.addObserver(
|
|
80
61
|
self,
|
package/package.json
CHANGED
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
package io.capawesome.capacitorjs.plugins.liveupdate.providers.ionic;
|
|
2
|
-
|
|
3
|
-
import android.content.Context;
|
|
4
|
-
import android.content.SharedPreferences;
|
|
5
|
-
import androidx.annotation.NonNull;
|
|
6
|
-
import androidx.annotation.Nullable;
|
|
7
|
-
import com.getcapacitor.Logger;
|
|
8
|
-
import io.capawesome.capacitorjs.plugins.liveupdate.LiveUpdate;
|
|
9
|
-
import io.capawesome.capacitorjs.plugins.liveupdate.LiveUpdatePlugin;
|
|
10
|
-
import io.capawesome.capacitorjs.plugins.liveupdate.classes.options.DownloadBundleOptions;
|
|
11
|
-
import io.capawesome.capacitorjs.plugins.liveupdate.classes.options.FetchLatestBundleOptions;
|
|
12
|
-
import io.capawesome.capacitorjs.plugins.liveupdate.classes.results.FetchLatestBundleResult;
|
|
13
|
-
import io.capawesome.capacitorjs.plugins.liveupdate.enums.ArtifactType;
|
|
14
|
-
import io.capawesome.capacitorjs.plugins.liveupdate.interfaces.EmptyCallback;
|
|
15
|
-
import io.capawesome.capacitorjs.plugins.liveupdate.interfaces.NonEmptyCallback;
|
|
16
|
-
import io.ionic.liveupdateprovider.FederatedCapacitorSyncResult;
|
|
17
|
-
import io.ionic.liveupdateprovider.LiveUpdateProviderError;
|
|
18
|
-
import io.ionic.liveupdateprovider.LiveUpdateProviderManager;
|
|
19
|
-
import io.ionic.liveupdateprovider.LiveUpdateProviderSyncCallback;
|
|
20
|
-
import java.io.File;
|
|
21
|
-
import java.util.HashMap;
|
|
22
|
-
import java.util.Iterator;
|
|
23
|
-
import java.util.Map;
|
|
24
|
-
import org.json.JSONObject;
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Ionic Live Update Provider manager backed by the Capawesome live update plugin.
|
|
28
|
-
*
|
|
29
|
-
* Each manager instance is scoped by a {@code managerKey} so that multiple shells (Portals or
|
|
30
|
-
* Federated Capacitor apps) can persist their own active bundle without colliding with each
|
|
31
|
-
* other or with the standalone plugin's {@code currentBundleId} / {@code nextBundleId} state.
|
|
32
|
-
*
|
|
33
|
-
* Provider config keys (V1):
|
|
34
|
-
* <ul>
|
|
35
|
-
* <li>{@code managerKey} (required) — scopes per-manager persisted state</li>
|
|
36
|
-
* <li>{@code appId} (optional) — Capawesome Cloud app UUID; falls back to plugin config</li>
|
|
37
|
-
* <li>{@code channel} (optional) — channel to sync; falls back to plugin config</li>
|
|
38
|
-
* </ul>
|
|
39
|
-
*/
|
|
40
|
-
public class LiveUpdateIonicManager implements LiveUpdateProviderManager {
|
|
41
|
-
|
|
42
|
-
public static final String SHARED_PREFERENCES_NAME = "CapawesomeLiveUpdateIonicProvider";
|
|
43
|
-
public static final String PREF_PREFIX_LAST_SYNCED_BUNDLE_ID = "lastSyncedBundleId.";
|
|
44
|
-
|
|
45
|
-
@Nullable
|
|
46
|
-
private final String appId;
|
|
47
|
-
|
|
48
|
-
@Nullable
|
|
49
|
-
private final String channel;
|
|
50
|
-
|
|
51
|
-
@Nullable
|
|
52
|
-
private File latestAppDirectory;
|
|
53
|
-
|
|
54
|
-
@NonNull
|
|
55
|
-
private final LiveUpdate liveUpdate;
|
|
56
|
-
|
|
57
|
-
@NonNull
|
|
58
|
-
private final String managerKey;
|
|
59
|
-
|
|
60
|
-
@NonNull
|
|
61
|
-
private final SharedPreferences sharedPreferences;
|
|
62
|
-
|
|
63
|
-
public LiveUpdateIonicManager(@NonNull Context context, @NonNull Map<String, ?> config, @NonNull LiveUpdate liveUpdate)
|
|
64
|
-
throws LiveUpdateProviderError.InvalidConfiguration {
|
|
65
|
-
Object managerKeyValue = config.get("managerKey");
|
|
66
|
-
if (!(managerKeyValue instanceof String) || ((String) managerKeyValue).isEmpty()) {
|
|
67
|
-
throw new LiveUpdateProviderError.InvalidConfiguration(LiveUpdatePlugin.ERROR_MANAGER_KEY_MISSING, null);
|
|
68
|
-
}
|
|
69
|
-
this.managerKey = (String) managerKeyValue;
|
|
70
|
-
|
|
71
|
-
Object appIdValue = config.get("appId");
|
|
72
|
-
this.appId = appIdValue instanceof String ? (String) appIdValue : null;
|
|
73
|
-
|
|
74
|
-
Object channelValue = config.get("channel");
|
|
75
|
-
this.channel = channelValue instanceof String ? (String) channelValue : null;
|
|
76
|
-
|
|
77
|
-
this.liveUpdate = liveUpdate;
|
|
78
|
-
this.sharedPreferences = context.getApplicationContext().getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
|
|
79
|
-
|
|
80
|
-
// Restore the last synced bundle directory, if any.
|
|
81
|
-
String persistedBundleId = sharedPreferences.getString(getLastSyncedBundleIdKey(), null);
|
|
82
|
-
if (persistedBundleId != null) {
|
|
83
|
-
this.latestAppDirectory = liveUpdate.getBundleDirectory(persistedBundleId);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
@Nullable
|
|
88
|
-
@Override
|
|
89
|
-
public File getLatestAppDirectory() {
|
|
90
|
-
return latestAppDirectory;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
@Override
|
|
94
|
-
public void sync(@Nullable LiveUpdateProviderSyncCallback callback) {
|
|
95
|
-
try {
|
|
96
|
-
FetchLatestBundleOptions fetchOptions = new FetchLatestBundleOptions(appId, channel);
|
|
97
|
-
liveUpdate.fetchLatestBundle(
|
|
98
|
-
fetchOptions,
|
|
99
|
-
new NonEmptyCallback<FetchLatestBundleResult>() {
|
|
100
|
-
@Override
|
|
101
|
-
public void success(@NonNull FetchLatestBundleResult result) {
|
|
102
|
-
handleFetchLatestBundleSuccess(result, callback);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
@Override
|
|
106
|
-
public void error(@NonNull Exception exception) {
|
|
107
|
-
notifyFailure(callback, exception);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
);
|
|
111
|
-
} catch (Exception exception) {
|
|
112
|
-
notifyFailure(callback, exception);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
private void handleFetchLatestBundleSuccess(
|
|
117
|
-
@NonNull FetchLatestBundleResult result,
|
|
118
|
-
@Nullable LiveUpdateProviderSyncCallback callback
|
|
119
|
-
) {
|
|
120
|
-
try {
|
|
121
|
-
String bundleId = result.getBundleId();
|
|
122
|
-
if (bundleId == null) {
|
|
123
|
-
// No update available; report success without changing state.
|
|
124
|
-
notifySuccess(callback, null, null);
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Bundle already on disk — just point latestAppDirectory at it and persist.
|
|
129
|
-
File existingDirectory = liveUpdate.getBundleDirectory(bundleId);
|
|
130
|
-
if (existingDirectory != null) {
|
|
131
|
-
applySyncedBundle(bundleId, existingDirectory);
|
|
132
|
-
notifySuccess(callback, bundleId, result.getCustomProperties());
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Otherwise, download and then apply.
|
|
137
|
-
String downloadUrl = result.getDownloadUrl();
|
|
138
|
-
if (downloadUrl == null || downloadUrl.isEmpty()) {
|
|
139
|
-
notifyFailure(callback, new Exception(LiveUpdatePlugin.ERROR_DOWNLOAD_URL_MISSING));
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
ArtifactType artifactType = result.getArtifactType() == null ? ArtifactType.ZIP : result.getArtifactType();
|
|
143
|
-
String artifactTypeString = artifactType == ArtifactType.MANIFEST ? "manifest" : "zip";
|
|
144
|
-
DownloadBundleOptions downloadOptions = new DownloadBundleOptions(
|
|
145
|
-
artifactTypeString,
|
|
146
|
-
bundleId,
|
|
147
|
-
result.getChecksum(),
|
|
148
|
-
result.getSignature(),
|
|
149
|
-
downloadUrl
|
|
150
|
-
);
|
|
151
|
-
liveUpdate.downloadBundle(
|
|
152
|
-
downloadOptions,
|
|
153
|
-
new EmptyCallback() {
|
|
154
|
-
@Override
|
|
155
|
-
public void success() {
|
|
156
|
-
File downloadedDirectory = liveUpdate.getBundleDirectory(bundleId);
|
|
157
|
-
if (downloadedDirectory == null) {
|
|
158
|
-
notifyFailure(callback, new Exception(LiveUpdatePlugin.ERROR_BUNDLE_DIRECTORY_NOT_FOUND));
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
applySyncedBundle(bundleId, downloadedDirectory);
|
|
162
|
-
notifySuccess(callback, bundleId, result.getCustomProperties());
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
@Override
|
|
166
|
-
public void error(@NonNull Exception exception) {
|
|
167
|
-
notifyFailure(callback, exception);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
);
|
|
171
|
-
} catch (Exception exception) {
|
|
172
|
-
notifyFailure(callback, exception);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
private void applySyncedBundle(@NonNull String bundleId, @NonNull File directory) {
|
|
177
|
-
latestAppDirectory = directory;
|
|
178
|
-
sharedPreferences.edit().putString(getLastSyncedBundleIdKey(), bundleId).apply();
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
private void notifySuccess(
|
|
182
|
-
@Nullable LiveUpdateProviderSyncCallback callback,
|
|
183
|
-
@Nullable String bundleId,
|
|
184
|
-
@Nullable JSONObject customProperties
|
|
185
|
-
) {
|
|
186
|
-
if (callback == null) {
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
Map<String, Object> metadata = new HashMap<>();
|
|
190
|
-
if (bundleId != null) {
|
|
191
|
-
metadata.put("bundleId", bundleId);
|
|
192
|
-
}
|
|
193
|
-
if (channel != null) {
|
|
194
|
-
metadata.put("channel", channel);
|
|
195
|
-
}
|
|
196
|
-
if (customProperties != null) {
|
|
197
|
-
Map<String, Object> customPropertiesMap = jsonObjectToMap(customProperties);
|
|
198
|
-
if (!customPropertiesMap.isEmpty()) {
|
|
199
|
-
metadata.put("customProperties", customPropertiesMap);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
callback.onSuccess(new FederatedCapacitorSyncResult(metadata));
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
private void notifyFailure(@Nullable LiveUpdateProviderSyncCallback callback, @NonNull Exception exception) {
|
|
206
|
-
Logger.error(LiveUpdatePlugin.TAG, "Ionic provider sync failed: " + exception.getMessage(), exception);
|
|
207
|
-
if (callback == null) {
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
String message = exception.getMessage() == null ? LiveUpdatePlugin.ERROR_UNKNOWN_ERROR : exception.getMessage();
|
|
211
|
-
callback.onFailure(new LiveUpdateProviderError.SyncFailed(message, exception));
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
@NonNull
|
|
215
|
-
private String getLastSyncedBundleIdKey() {
|
|
216
|
-
return PREF_PREFIX_LAST_SYNCED_BUNDLE_ID + managerKey;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
@NonNull
|
|
220
|
-
private static Map<String, Object> jsonObjectToMap(@NonNull JSONObject jsonObject) {
|
|
221
|
-
Map<String, Object> map = new HashMap<>();
|
|
222
|
-
Iterator<String> keys = jsonObject.keys();
|
|
223
|
-
while (keys.hasNext()) {
|
|
224
|
-
String key = keys.next();
|
|
225
|
-
Object value = jsonObject.opt(key);
|
|
226
|
-
if (value != null && value != JSONObject.NULL) {
|
|
227
|
-
map.put(key, value);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
return map;
|
|
231
|
-
}
|
|
232
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
package io.capawesome.capacitorjs.plugins.liveupdate.providers.ionic;
|
|
2
|
-
|
|
3
|
-
import android.content.Context;
|
|
4
|
-
import androidx.annotation.NonNull;
|
|
5
|
-
import io.capawesome.capacitorjs.plugins.liveupdate.LiveUpdate;
|
|
6
|
-
import io.ionic.liveupdateprovider.LiveUpdateProvider;
|
|
7
|
-
import io.ionic.liveupdateprovider.LiveUpdateProviderError;
|
|
8
|
-
import io.ionic.liveupdateprovider.LiveUpdateProviderManager;
|
|
9
|
-
import java.util.Map;
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Ionic Live Update Provider implementation backed by the Capawesome live update plugin.
|
|
13
|
-
*
|
|
14
|
-
* Registered with {@link io.ionic.liveupdateprovider.LiveUpdateProviderRegistry} when the
|
|
15
|
-
* Capacitor plugin loads, so Federated Capacitor can resolve provider id {@code "capawesome"}
|
|
16
|
-
* and create managers from the per-shell configuration.
|
|
17
|
-
*/
|
|
18
|
-
public class LiveUpdateIonicProvider implements LiveUpdateProvider {
|
|
19
|
-
|
|
20
|
-
public static final String ID = "capawesome";
|
|
21
|
-
|
|
22
|
-
@NonNull
|
|
23
|
-
private final LiveUpdate liveUpdate;
|
|
24
|
-
|
|
25
|
-
public LiveUpdateIonicProvider(@NonNull LiveUpdate liveUpdate) {
|
|
26
|
-
this.liveUpdate = liveUpdate;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
@NonNull
|
|
30
|
-
@Override
|
|
31
|
-
public String getId() {
|
|
32
|
-
return ID;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
@NonNull
|
|
36
|
-
@Override
|
|
37
|
-
public LiveUpdateProviderManager createManager(@NonNull Context context, @NonNull Map<String, ?> config)
|
|
38
|
-
throws LiveUpdateProviderError.InvalidConfiguration {
|
|
39
|
-
return new LiveUpdateIonicManager(context, config, liveUpdate);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
package io.capawesome.capacitorjs.plugins.liveupdate.providers.ionic;
|
|
2
|
-
|
|
3
|
-
import androidx.annotation.NonNull;
|
|
4
|
-
import com.getcapacitor.Logger;
|
|
5
|
-
import io.capawesome.capacitorjs.plugins.liveupdate.LiveUpdate;
|
|
6
|
-
import io.capawesome.capacitorjs.plugins.liveupdate.LiveUpdatePlugin;
|
|
7
|
-
import io.ionic.liveupdateprovider.LiveUpdateProviderRegistry;
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Helper that registers the Capawesome live update provider with the Ionic
|
|
11
|
-
* {@link LiveUpdateProviderRegistry}, but only when the optional
|
|
12
|
-
* {@code io.ionic:liveupdateprovider} dependency is present at runtime.
|
|
13
|
-
*
|
|
14
|
-
* The Gradle build wires the SDK as either {@code implementation} (when the consumer opts in
|
|
15
|
-
* via {@code capawesomeCapacitorLiveUpdateIncludeIonicProvider = true}) or {@code compileOnly}
|
|
16
|
-
* (the default). When opted out, the SDK classes are stripped from the consumer APK and the
|
|
17
|
-
* reflection check below short-circuits registration.
|
|
18
|
-
*/
|
|
19
|
-
public final class LiveUpdateIonicProviderRegistration {
|
|
20
|
-
|
|
21
|
-
private LiveUpdateIonicProviderRegistration() {
|
|
22
|
-
// Static utility — no instances.
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
public static boolean isAvailable() {
|
|
26
|
-
try {
|
|
27
|
-
Class.forName("io.ionic.liveupdateprovider.LiveUpdateProviderRegistry");
|
|
28
|
-
return true;
|
|
29
|
-
} catch (ClassNotFoundException exception) {
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
public static void tryRegister(@NonNull LiveUpdate liveUpdate) {
|
|
35
|
-
try {
|
|
36
|
-
if (LiveUpdateProviderRegistry.resolve(LiveUpdateIonicProvider.ID) != null) {
|
|
37
|
-
Logger.debug(LiveUpdatePlugin.TAG, "Ionic provider '" + LiveUpdateIonicProvider.ID + "' already registered.");
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
LiveUpdateProviderRegistry.register(new LiveUpdateIonicProvider(liveUpdate));
|
|
41
|
-
Logger.debug(LiveUpdatePlugin.TAG, "Registered Ionic provider '" + LiveUpdateIonicProvider.ID + "'.");
|
|
42
|
-
} catch (Exception exception) {
|
|
43
|
-
Logger.error(LiveUpdatePlugin.TAG, "Failed to register Ionic provider: " + exception.getMessage(), exception);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
#if CAPAWESOME_INCLUDE_IONIC_PROVIDER
|
|
2
|
-
import Foundation
|
|
3
|
-
import LiveUpdateProvider
|
|
4
|
-
|
|
5
|
-
/// Ionic Live Update Provider manager backed by the Capawesome live update plugin.
|
|
6
|
-
///
|
|
7
|
-
/// Each manager instance is scoped by a `managerKey` so that multiple shells (Portals or
|
|
8
|
-
/// Federated Capacitor apps) can persist their own active bundle without colliding with
|
|
9
|
-
/// each other or with the standalone plugin's `currentBundleId` / `nextBundleId` state.
|
|
10
|
-
///
|
|
11
|
-
/// Provider config keys (V1):
|
|
12
|
-
/// - `managerKey` (required) — scopes per-manager persisted state
|
|
13
|
-
/// - `appId` (optional) — Capawesome Cloud app UUID; falls back to plugin config
|
|
14
|
-
/// - `channel` (optional) — channel to sync; falls back to plugin config
|
|
15
|
-
public final class LiveUpdateIonicManager: LiveUpdateManaging {
|
|
16
|
-
private static let userDefaultsPrefix = "CapawesomeLiveUpdateIonicProvider.lastSyncedBundleId."
|
|
17
|
-
|
|
18
|
-
private let appId: String?
|
|
19
|
-
private let channel: String?
|
|
20
|
-
private let liveUpdate: LiveUpdate
|
|
21
|
-
private let managerKey: String
|
|
22
|
-
|
|
23
|
-
public private(set) var latestAppDirectory: URL?
|
|
24
|
-
|
|
25
|
-
public init(config: [String: Any], liveUpdate: LiveUpdate) throws {
|
|
26
|
-
guard let managerKey = config["managerKey"] as? String, !managerKey.isEmpty else {
|
|
27
|
-
throw LiveUpdateProviderError.invalidConfiguration(
|
|
28
|
-
CustomError.managerKeyMissing.localizedDescription,
|
|
29
|
-
underlyingError: nil
|
|
30
|
-
)
|
|
31
|
-
}
|
|
32
|
-
self.managerKey = managerKey
|
|
33
|
-
self.appId = config["appId"] as? String
|
|
34
|
-
self.channel = config["channel"] as? String
|
|
35
|
-
self.liveUpdate = liveUpdate
|
|
36
|
-
|
|
37
|
-
// Restore the last synced bundle directory, if any.
|
|
38
|
-
if let persistedBundleId = UserDefaults.standard.string(forKey: lastSyncedBundleIdKey()) {
|
|
39
|
-
self.latestAppDirectory = liveUpdate.getBundleDirectory(bundleId: persistedBundleId)
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
public func sync() async throws -> any LiveUpdateProvider.SyncResult {
|
|
44
|
-
do {
|
|
45
|
-
let fetchOptions = FetchLatestBundleOptions(appId: appId, channel: channel)
|
|
46
|
-
let result = try await liveUpdate.fetchLatestBundle(fetchOptions)
|
|
47
|
-
|
|
48
|
-
guard let bundleId = result.getBundleId() else {
|
|
49
|
-
// No update available; report success without changing state.
|
|
50
|
-
return DefaultFederatedCapacitorSyncResult(metadata: buildMetadata(bundleId: nil, customProperties: nil))
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Bundle already on disk — just point latestAppDirectory at it and persist.
|
|
54
|
-
if let existingDirectory = liveUpdate.getBundleDirectory(bundleId: bundleId) {
|
|
55
|
-
applySyncedBundle(bundleId: bundleId, directory: existingDirectory)
|
|
56
|
-
return DefaultFederatedCapacitorSyncResult(
|
|
57
|
-
metadata: buildMetadata(bundleId: bundleId, customProperties: result.getCustomProperties())
|
|
58
|
-
)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Otherwise, download and then apply.
|
|
62
|
-
guard let downloadUrl = result.getDownloadUrl(), !downloadUrl.isEmpty else {
|
|
63
|
-
throw LiveUpdateProviderError.syncFailed(
|
|
64
|
-
CustomError.downloadUrlMissing.localizedDescription,
|
|
65
|
-
underlyingError: nil
|
|
66
|
-
)
|
|
67
|
-
}
|
|
68
|
-
let artifactType = result.getArtifactType() ?? "zip"
|
|
69
|
-
let downloadOptions = DownloadBundleOptions(
|
|
70
|
-
artifactType: artifactType,
|
|
71
|
-
bundleId: bundleId,
|
|
72
|
-
checksum: result.getChecksum(),
|
|
73
|
-
signature: result.getSignature(),
|
|
74
|
-
url: downloadUrl
|
|
75
|
-
)
|
|
76
|
-
try await liveUpdate.downloadBundle(downloadOptions)
|
|
77
|
-
|
|
78
|
-
guard let downloadedDirectory = liveUpdate.getBundleDirectory(bundleId: bundleId) else {
|
|
79
|
-
throw LiveUpdateProviderError.syncFailed(
|
|
80
|
-
CustomError.bundleDirectoryNotFound.localizedDescription,
|
|
81
|
-
underlyingError: nil
|
|
82
|
-
)
|
|
83
|
-
}
|
|
84
|
-
applySyncedBundle(bundleId: bundleId, directory: downloadedDirectory)
|
|
85
|
-
return DefaultFederatedCapacitorSyncResult(
|
|
86
|
-
metadata: buildMetadata(bundleId: bundleId, customProperties: result.getCustomProperties())
|
|
87
|
-
)
|
|
88
|
-
} catch let error as LiveUpdateProviderError {
|
|
89
|
-
throw error
|
|
90
|
-
} catch {
|
|
91
|
-
throw LiveUpdateProviderError.syncFailed(error.localizedDescription, underlyingError: error)
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
private func applySyncedBundle(bundleId: String, directory: URL) {
|
|
96
|
-
latestAppDirectory = directory
|
|
97
|
-
UserDefaults.standard.set(bundleId, forKey: lastSyncedBundleIdKey())
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
private func buildMetadata(bundleId: String?, customProperties: [String: Any]?) -> [String: Any]? {
|
|
101
|
-
var metadata: [String: Any] = [:]
|
|
102
|
-
if let bundleId = bundleId {
|
|
103
|
-
metadata["bundleId"] = bundleId
|
|
104
|
-
}
|
|
105
|
-
if let channel = channel {
|
|
106
|
-
metadata["channel"] = channel
|
|
107
|
-
}
|
|
108
|
-
if let customProperties = customProperties, !customProperties.isEmpty {
|
|
109
|
-
metadata["customProperties"] = customProperties
|
|
110
|
-
}
|
|
111
|
-
return metadata.isEmpty ? nil : metadata
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
private func lastSyncedBundleIdKey() -> String {
|
|
115
|
-
return Self.userDefaultsPrefix + managerKey
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
#endif
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
#if CAPAWESOME_INCLUDE_IONIC_PROVIDER
|
|
2
|
-
import Foundation
|
|
3
|
-
import LiveUpdateProvider
|
|
4
|
-
|
|
5
|
-
/// Ionic Live Update Provider implementation backed by the Capawesome live update plugin.
|
|
6
|
-
///
|
|
7
|
-
/// Registered with `LiveUpdateProviderRegistry.shared` when the Capacitor plugin loads, so
|
|
8
|
-
/// Federated Capacitor can resolve provider id `"capawesome"` and create managers from the
|
|
9
|
-
/// per-shell configuration.
|
|
10
|
-
public struct LiveUpdateIonicProvider: LiveUpdateProviding {
|
|
11
|
-
public static let id = "capawesome"
|
|
12
|
-
|
|
13
|
-
public let id: String = LiveUpdateIonicProvider.id
|
|
14
|
-
|
|
15
|
-
private let liveUpdate: LiveUpdate
|
|
16
|
-
|
|
17
|
-
public init(liveUpdate: LiveUpdate) {
|
|
18
|
-
self.liveUpdate = liveUpdate
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
public func createManager(config: [String: Any]) throws -> any LiveUpdateManaging {
|
|
22
|
-
return try LiveUpdateIonicManager(config: config, liveUpdate: liveUpdate)
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
#endif
|