@capgo/capacitor-updater 3.3.11 → 4.0.0-alpha.10
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/LICENCE +656 -160
- package/README.md +225 -129
- package/android/src/main/java/ee/forgr/capacitor_updater/BundleInfo.java +130 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/BundleStatus.java +36 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdater.java +314 -198
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +481 -246
- package/dist/docs.json +607 -114
- package/dist/esm/definitions.d.ts +222 -74
- package/dist/esm/web.d.ts +19 -16
- package/dist/esm/web.js +32 -23
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +32 -23
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +32 -23
- package/dist/plugin.js.map +1 -1
- package/ios/Plugin/BundleInfo.swift +94 -0
- package/ios/Plugin/BundleStatus.swift +41 -0
- package/ios/Plugin/CapacitorUpdater.swift +319 -82
- package/ios/Plugin/CapacitorUpdaterPlugin.m +4 -2
- package/ios/Plugin/CapacitorUpdaterPlugin.swift +287 -173
- package/ios/Plugin/ObjectPreferences.swift +41 -0
- package/package.json +3 -2
|
@@ -9,22 +9,32 @@ import Version
|
|
|
9
9
|
@objc(CapacitorUpdaterPlugin)
|
|
10
10
|
public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
11
11
|
private var implementation = CapacitorUpdater()
|
|
12
|
-
static let
|
|
13
|
-
static let statsUrlDefault = "https://
|
|
14
|
-
|
|
12
|
+
static let updateUrlDefault = "https://xvwzpoazmxkqosrdewyv.functions.supabase.co/updates"
|
|
13
|
+
static let statsUrlDefault = "https://xvwzpoazmxkqosrdewyv.functions.supabase.co/stats"
|
|
14
|
+
static let DELAY_UPDATE = "delayUpdate"
|
|
15
|
+
private var updateUrl = ""
|
|
16
|
+
private var statsUrl = ""
|
|
15
17
|
private var currentVersionNative: Version = "0.0.0"
|
|
16
18
|
private var autoUpdate = false
|
|
17
|
-
private var
|
|
18
|
-
private var
|
|
19
|
+
private var appReadyTimeout = 10000
|
|
20
|
+
private var appReadyCheck: DispatchWorkItem?
|
|
21
|
+
private var resetWhenUpdate = true
|
|
22
|
+
private var autoDeleteFailed = false
|
|
23
|
+
private var autoDeletePrevious = false
|
|
19
24
|
|
|
20
25
|
override public func load() {
|
|
21
26
|
do {
|
|
22
27
|
currentVersionNative = try Version(Bundle.main.buildVersionNumber ?? "0.0.0")
|
|
23
28
|
} catch {
|
|
24
|
-
print("
|
|
29
|
+
print("\(self.implementation.TAG) Cannot get version native \(currentVersionNative)")
|
|
25
30
|
}
|
|
26
|
-
|
|
31
|
+
autoDeleteFailed = getConfigValue("autoDeleteFailed") as? Bool ?? false
|
|
32
|
+
autoDeletePrevious = getConfigValue("autoDeletePrevious") as? Bool ?? false
|
|
33
|
+
updateUrl = getConfigValue("updateUrl") as? String ?? CapacitorUpdaterPlugin.updateUrlDefault
|
|
27
34
|
autoUpdate = getConfigValue("autoUpdate") as? Bool ?? false
|
|
35
|
+
appReadyTimeout = getConfigValue("appReadyTimeout") as? Int ?? 10000
|
|
36
|
+
resetWhenUpdate = getConfigValue("resetWhenUpdate") as? Bool ?? true
|
|
37
|
+
|
|
28
38
|
implementation.appId = Bundle.main.bundleIdentifier ?? ""
|
|
29
39
|
implementation.notifyDownload = notifyDownload
|
|
30
40
|
let config = (self.bridge?.viewController as? CAPBridgeViewController)?.instanceDescriptor().legacyConfig
|
|
@@ -32,34 +42,43 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
32
42
|
implementation.appId = config?["appId"] as! String
|
|
33
43
|
}
|
|
34
44
|
implementation.statsUrl = getConfigValue("statsUrl") as? String ?? CapacitorUpdaterPlugin.statsUrlDefault
|
|
35
|
-
|
|
45
|
+
|
|
36
46
|
if (resetWhenUpdate) {
|
|
37
|
-
|
|
38
|
-
do {
|
|
39
|
-
LatestVersionNative = try Version(UserDefaults.standard.string(forKey: "LatestVersionNative") ?? "0.0.0")
|
|
40
|
-
} catch {
|
|
41
|
-
print("✨ Capacitor-updater: Cannot get version native \(currentVersionNative)")
|
|
42
|
-
}
|
|
43
|
-
if (LatestVersionNative != "0.0.0" && currentVersionNative.major > LatestVersionNative.major) {
|
|
44
|
-
_ = self._reset(toAutoUpdate: false)
|
|
45
|
-
UserDefaults.standard.set("", forKey: "LatestVersionAutoUpdate")
|
|
46
|
-
UserDefaults.standard.set("", forKey: "LatestVersionNameAutoUpdate")
|
|
47
|
-
let res = implementation.list()
|
|
48
|
-
res.forEach { version in
|
|
49
|
-
_ = implementation.delete(version: version, versionName: "")
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
UserDefaults.standard.set( Bundle.main.buildVersionNumber, forKey: "LatestVersionNative")
|
|
47
|
+
self.cleanupObsoleteVersions()
|
|
53
48
|
}
|
|
54
|
-
if (!autoUpdate || autoUpdateUrl == "") { return }
|
|
55
49
|
let nc = NotificationCenter.default
|
|
56
50
|
nc.addObserver(self, selector: #selector(appMovedToBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
|
|
57
51
|
nc.addObserver(self, selector: #selector(appMovedToForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
|
|
58
52
|
self.appMovedToForeground()
|
|
59
53
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
54
|
+
|
|
55
|
+
private func cleanupObsoleteVersions() {
|
|
56
|
+
var LatestVersionNative: Version = "0.0.0"
|
|
57
|
+
do {
|
|
58
|
+
LatestVersionNative = try Version(UserDefaults.standard.string(forKey: "LatestVersionNative") ?? "0.0.0")
|
|
59
|
+
} catch {
|
|
60
|
+
print("\(self.implementation.TAG) Cannot get version native \(currentVersionNative)")
|
|
61
|
+
}
|
|
62
|
+
if (LatestVersionNative != "0.0.0" && currentVersionNative.major > LatestVersionNative.major) {
|
|
63
|
+
_ = self._reset(toLastSuccessful: false)
|
|
64
|
+
UserDefaults.standard.set("", forKey: "LatestVersionAutoUpdate")
|
|
65
|
+
UserDefaults.standard.set("", forKey: "LatestVersionNameAutoUpdate")
|
|
66
|
+
let res = implementation.list()
|
|
67
|
+
res.forEach { version in
|
|
68
|
+
print("\(self.implementation.TAG) Deleting obsolete bundle: \(version)")
|
|
69
|
+
_ = implementation.delete(id: version.getId())
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
UserDefaults.standard.set( self.currentVersionNative.description, forKey: "LatestVersionNative")
|
|
73
|
+
UserDefaults.standard.synchronize()
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@objc func notifyDownload(id: String, percent: Int) {
|
|
77
|
+
let bundle = self.implementation.getBundleInfo(id: id)
|
|
78
|
+
self.notifyListeners("download", data: ["percent": percent, "bundle": bundle.toJSON()])
|
|
79
|
+
if (percent == 100) {
|
|
80
|
+
self.notifyListeners("downloadComplete", data: ["bundle": bundle.toJSON()])
|
|
81
|
+
}
|
|
63
82
|
}
|
|
64
83
|
|
|
65
84
|
@objc func getId(_ call: CAPPluginCall) {
|
|
@@ -71,12 +90,21 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
71
90
|
}
|
|
72
91
|
|
|
73
92
|
@objc func download(_ call: CAPPluginCall) {
|
|
74
|
-
let
|
|
93
|
+
guard let urlString = call.getString("url") else {
|
|
94
|
+
print("\(self.implementation.TAG) Download called without url")
|
|
95
|
+
call.reject("Download called without url")
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
guard let version = call.getString("version") else {
|
|
99
|
+
print("\(self.implementation.TAG) Download called without version")
|
|
100
|
+
call.reject("Download called without version")
|
|
101
|
+
return
|
|
102
|
+
}
|
|
103
|
+
let url = URL(string: urlString)
|
|
104
|
+
print("\(self.implementation.TAG) Downloading \(url!)")
|
|
75
105
|
do {
|
|
76
|
-
let res = try implementation.download(url: url
|
|
77
|
-
call.resolve(
|
|
78
|
-
"version": res
|
|
79
|
-
])
|
|
106
|
+
let res = try implementation.download(url: url!, version: version)
|
|
107
|
+
call.resolve(res.toJSON())
|
|
80
108
|
} catch {
|
|
81
109
|
call.reject("download failed", error.localizedDescription)
|
|
82
110
|
}
|
|
@@ -84,18 +112,13 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
84
112
|
|
|
85
113
|
private func _reload() -> Bool {
|
|
86
114
|
guard let bridge = self.bridge else { return false }
|
|
87
|
-
|
|
115
|
+
let id = self.implementation.getCurrentBundleId()
|
|
116
|
+
let destHot = self.implementation.getPathHot(id: id)
|
|
117
|
+
print("\(self.implementation.TAG) Reloading \(id)")
|
|
88
118
|
if let vc = bridge.viewController as? CAPBridgeViewController {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
UserDefaults.standard.set(String(pathPersist.suffix(10)), forKey: "serverBasePath")
|
|
93
|
-
vc.setServerBasePath(path: pathHot)
|
|
94
|
-
print("✨ Capacitor-updater: Reload app done")
|
|
95
|
-
return true
|
|
96
|
-
} else {
|
|
97
|
-
return false
|
|
98
|
-
}
|
|
119
|
+
vc.setServerBasePath(path: destHot.path)
|
|
120
|
+
self.checkAppReady()
|
|
121
|
+
return true
|
|
99
122
|
}
|
|
100
123
|
return false
|
|
101
124
|
}
|
|
@@ -104,57 +127,87 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
104
127
|
if (self._reload()) {
|
|
105
128
|
call.resolve()
|
|
106
129
|
} else {
|
|
107
|
-
call.reject("
|
|
130
|
+
call.reject("Reload failed")
|
|
131
|
+
print("\(self.implementation.TAG) Reload failed")
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
@objc func next(_ call: CAPPluginCall) {
|
|
136
|
+
guard let id = call.getString("id") else {
|
|
137
|
+
print("\(self.implementation.TAG) Next called without id")
|
|
138
|
+
call.reject("Next called without id")
|
|
139
|
+
return
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
print("\(self.implementation.TAG) Setting next active id \(id)")
|
|
143
|
+
if (!self.implementation.setNextVersion(next: id)) {
|
|
144
|
+
call.reject("Set next version failed. id \(id) does not exist.")
|
|
145
|
+
} else {
|
|
146
|
+
call.resolve(self.implementation.getBundleInfo(id: id).toJSON())
|
|
108
147
|
}
|
|
109
148
|
}
|
|
110
149
|
|
|
111
150
|
@objc func set(_ call: CAPPluginCall) {
|
|
112
|
-
let
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
151
|
+
guard let id = call.getString("id") else {
|
|
152
|
+
print("\(self.implementation.TAG) Set called without id")
|
|
153
|
+
call.reject("Set called without id")
|
|
154
|
+
return
|
|
155
|
+
}
|
|
156
|
+
let res = implementation.set(id: id)
|
|
157
|
+
print("\(self.implementation.TAG) Set active bundle: \(id)")
|
|
158
|
+
if (!res) {
|
|
159
|
+
print("\(self.implementation.TAG) Bundle successfully set to: \(id) ")
|
|
160
|
+
call.reject("Update failed, id \(id) doesn't exist")
|
|
119
161
|
} else {
|
|
120
|
-
|
|
162
|
+
self.reload(call)
|
|
121
163
|
}
|
|
122
164
|
}
|
|
123
165
|
|
|
124
166
|
@objc func delete(_ call: CAPPluginCall) {
|
|
125
|
-
let
|
|
126
|
-
|
|
167
|
+
guard let id = call.getString("id") else {
|
|
168
|
+
print("\(self.implementation.TAG) Delete called without version")
|
|
169
|
+
call.reject("Delete called without id")
|
|
170
|
+
return
|
|
171
|
+
}
|
|
172
|
+
let res = implementation.delete(id: id)
|
|
127
173
|
if (res) {
|
|
128
174
|
call.resolve()
|
|
129
175
|
} else {
|
|
130
|
-
call.reject("Delete failed,
|
|
176
|
+
call.reject("Delete failed, id \(id) doesn't exist")
|
|
131
177
|
}
|
|
132
178
|
}
|
|
133
179
|
|
|
134
180
|
@objc func list(_ call: CAPPluginCall) {
|
|
135
181
|
let res = implementation.list()
|
|
182
|
+
var resArr: [[String: String]] = []
|
|
183
|
+
for v in res {
|
|
184
|
+
resArr.append(v.toJSON())
|
|
185
|
+
}
|
|
136
186
|
call.resolve([
|
|
137
|
-
"versions":
|
|
187
|
+
"versions": resArr
|
|
138
188
|
])
|
|
139
189
|
}
|
|
140
190
|
|
|
141
|
-
@objc func
|
|
191
|
+
@objc func getLatest(_ call: CAPPluginCall) {
|
|
192
|
+
let res = self.implementation.getLatest(url: URL(string: self.updateUrl)!)
|
|
193
|
+
call.resolve((res?.toDict())!)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
@objc func _reset(toLastSuccessful: Bool) -> Bool {
|
|
142
197
|
guard let bridge = self.bridge else { return false }
|
|
198
|
+
|
|
143
199
|
if let vc = bridge.viewController as? CAPBridgeViewController {
|
|
144
|
-
let
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
return res && self._reload()
|
|
200
|
+
let fallback: BundleInfo = self.implementation.getFallbackVersion()
|
|
201
|
+
if (toLastSuccessful && !fallback.isBuiltin()) {
|
|
202
|
+
print("\(self.implementation.TAG) Resetting to: \(fallback.toString())")
|
|
203
|
+
return self.implementation.set(bundle: fallback) && self._reload()
|
|
149
204
|
}
|
|
150
|
-
implementation.reset()
|
|
151
|
-
|
|
152
|
-
vc.setServerBasePath(path: pathPersist)
|
|
153
|
-
UserDefaults.standard.set(pathPersist, forKey: "serverBasePath")
|
|
205
|
+
self.implementation.reset()
|
|
206
|
+
vc.setServerBasePath(path: "")
|
|
154
207
|
DispatchQueue.main.async {
|
|
155
208
|
vc.loadView()
|
|
156
209
|
vc.viewDidLoad()
|
|
157
|
-
print("
|
|
210
|
+
print("\(self.implementation.TAG) Reset to builtin version")
|
|
158
211
|
}
|
|
159
212
|
return true
|
|
160
213
|
}
|
|
@@ -162,150 +215,211 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
162
215
|
}
|
|
163
216
|
|
|
164
217
|
@objc func reset(_ call: CAPPluginCall) {
|
|
165
|
-
let
|
|
166
|
-
if (self._reset(
|
|
218
|
+
let toLastSuccessful = call.getBool("toLastSuccessful") ?? false
|
|
219
|
+
if (self._reset(toLastSuccessful: toLastSuccessful)) {
|
|
167
220
|
return call.resolve()
|
|
168
221
|
}
|
|
169
|
-
call.reject("
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
@objc func versionName(_ call: CAPPluginCall) {
|
|
173
|
-
let name = implementation.getVersionName()
|
|
174
|
-
call.resolve([
|
|
175
|
-
"versionName": name
|
|
176
|
-
])
|
|
222
|
+
call.reject("\(self.implementation.TAG) Reset failed")
|
|
177
223
|
}
|
|
178
224
|
|
|
179
225
|
@objc func current(_ call: CAPPluginCall) {
|
|
180
|
-
let
|
|
181
|
-
let current = pathHot.count >= 10 ? pathHot.suffix(10) : "builtin"
|
|
226
|
+
let bundle: BundleInfo = self.implementation.getCurrentBundle()
|
|
182
227
|
call.resolve([
|
|
183
|
-
"
|
|
184
|
-
"
|
|
228
|
+
"bundle": bundle.toJSON(),
|
|
229
|
+
"native": self.currentVersionNative
|
|
185
230
|
])
|
|
186
231
|
}
|
|
187
232
|
|
|
188
233
|
@objc func notifyAppReady(_ call: CAPPluginCall) {
|
|
189
|
-
|
|
234
|
+
print("\(self.implementation.TAG) Current bundle loaded successfully. ['notifyAppReady()' was called]")
|
|
235
|
+
let version = self.implementation.getCurrentBundle()
|
|
236
|
+
self.implementation.commit(bundle: version)
|
|
190
237
|
call.resolve()
|
|
191
238
|
}
|
|
192
239
|
|
|
193
|
-
@objc func
|
|
194
|
-
|
|
240
|
+
@objc func setDelay(_ call: CAPPluginCall) {
|
|
241
|
+
guard let delay = call.getBool("delay") else {
|
|
242
|
+
print("\(self.implementation.TAG) setDelay called without delay")
|
|
243
|
+
call.reject("setDelay called without delay")
|
|
244
|
+
return
|
|
245
|
+
}
|
|
246
|
+
UserDefaults.standard.set(delay, forKey: "delayUpdate")
|
|
195
247
|
call.resolve()
|
|
196
248
|
}
|
|
197
249
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
call.resolve()
|
|
250
|
+
private func _isAutoUpdateEnabled() -> Bool {
|
|
251
|
+
return self.autoUpdate && self.updateUrl != ""
|
|
201
252
|
}
|
|
202
253
|
|
|
203
|
-
@objc func
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
254
|
+
@objc func isAutoUpdateEnabled(_ call: CAPPluginCall) {
|
|
255
|
+
call.resolve([
|
|
256
|
+
"enabled": self._isAutoUpdateEnabled()
|
|
257
|
+
])
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
func checkAppReady() {
|
|
261
|
+
self.appReadyCheck?.cancel()
|
|
262
|
+
self.appReadyCheck = DispatchWorkItem(block: {
|
|
263
|
+
self.DeferredNotifyAppReadyCheck()
|
|
264
|
+
})
|
|
265
|
+
print("\(self.implementation.TAG) Wait for \(self.appReadyTimeout) ms, then check for notifyAppReady")
|
|
266
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(self.appReadyTimeout), execute: self.appReadyCheck!)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
func DeferredNotifyAppReadyCheck() {
|
|
270
|
+
// Automatically roll back to fallback version if notifyAppReady has not been called yet
|
|
271
|
+
let current: BundleInfo = self.implementation.getCurrentBundle()
|
|
272
|
+
if(current.isBuiltin()) {
|
|
273
|
+
print("\(self.implementation.TAG) Built-in bundle is active. Nothing to do.")
|
|
274
|
+
return
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if(BundleStatus.SUCCESS.localizedString != current.getStatus()) {
|
|
278
|
+
print("\(self.implementation.TAG) notifyAppReady was not called, roll back current bundle: \(current.toString())")
|
|
279
|
+
self.implementation.rollback(bundle: current)
|
|
280
|
+
let res = self._reset(toLastSuccessful: true)
|
|
281
|
+
if (!res) {
|
|
209
282
|
return
|
|
210
283
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
284
|
+
} else {
|
|
285
|
+
print("\(self.implementation.TAG) notifyAppReady was called. This is fine: \(current.toString())")
|
|
286
|
+
}
|
|
287
|
+
self.appReadyCheck = nil
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
@objc func appMovedToForeground() {
|
|
291
|
+
if (self._isAutoUpdateEnabled()) {
|
|
292
|
+
DispatchQueue.global(qos: .background).async {
|
|
293
|
+
print("\(self.implementation.TAG) Check for update via \(self.updateUrl)")
|
|
294
|
+
let url = URL(string: self.updateUrl)!
|
|
295
|
+
let res = self.implementation.getLatest(url: url)
|
|
296
|
+
if (res == nil) {
|
|
297
|
+
print("\(self.implementation.TAG) No result found in \(self.updateUrl)")
|
|
298
|
+
return
|
|
215
299
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
300
|
+
if ((res?.message) != nil) {
|
|
301
|
+
print("\(self.implementation.TAG) message \(res!.message ?? "")")
|
|
302
|
+
if (res?.major == true) {
|
|
303
|
+
self.notifyListeners("majorAvailable", data: ["version": res?.version ?? "0.0.0"])
|
|
304
|
+
}
|
|
305
|
+
return
|
|
306
|
+
}
|
|
307
|
+
guard let downloadUrl = URL(string: res?.url ?? "") else {
|
|
308
|
+
print("\(self.implementation.TAG) Error no url or wrong format")
|
|
309
|
+
return
|
|
310
|
+
}
|
|
311
|
+
let current = self.implementation.getCurrentBundle()
|
|
312
|
+
let latestVersionName = res?.version
|
|
313
|
+
if (latestVersionName != nil && latestVersionName != "" && current.getVersionName() != latestVersionName) {
|
|
314
|
+
let latest = self.implementation.getBundleInfoByVersionName(version: latestVersionName!)
|
|
315
|
+
if (latest != nil) {
|
|
316
|
+
if(latest!.isErrorStatus()) {
|
|
317
|
+
print("\(self.implementation.TAG) Latest version already exists, and is in error state. Aborting update.")
|
|
318
|
+
return
|
|
319
|
+
}
|
|
320
|
+
if(latest!.isDownloaded()){
|
|
321
|
+
print("\(self.implementation.TAG) Latest version already exists and download is NOT required. Update will occur next time app moves to background.")
|
|
322
|
+
let _ = self.implementation.setNextVersion(next: latest!.getId())
|
|
323
|
+
return
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
do {
|
|
328
|
+
print("\(self.implementation.TAG) New bundle: \(latestVersionName!) found. Current is: \(current.getVersionName()). Update will occur next time app moves to background.")
|
|
329
|
+
let next = try self.implementation.download(url: downloadUrl, version: latestVersionName!)
|
|
330
|
+
|
|
331
|
+
let _ = self.implementation.setNextVersion(next: next.getId())
|
|
332
|
+
} catch {
|
|
333
|
+
print("\(self.implementation.TAG) Error downloading file", error.localizedDescription)
|
|
334
|
+
}
|
|
236
335
|
}
|
|
237
|
-
} else {
|
|
238
|
-
print("✨ Capacitor-updater: No need to update, \(currentVersion) is the latest")
|
|
239
336
|
}
|
|
240
337
|
}
|
|
338
|
+
|
|
339
|
+
self.checkAppReady()
|
|
241
340
|
}
|
|
242
341
|
|
|
243
342
|
@objc func appMovedToBackground() {
|
|
244
|
-
print("
|
|
343
|
+
print("\(self.implementation.TAG) Check for waiting update")
|
|
245
344
|
let delayUpdate = UserDefaults.standard.bool(forKey: "delayUpdate")
|
|
246
345
|
UserDefaults.standard.set(false, forKey: "delayUpdate")
|
|
247
346
|
if (delayUpdate) {
|
|
248
|
-
print("
|
|
347
|
+
print("\(self.implementation.TAG) Update delayed to next backgrounding")
|
|
249
348
|
return
|
|
250
349
|
}
|
|
251
|
-
|
|
252
|
-
let
|
|
253
|
-
let
|
|
254
|
-
let
|
|
255
|
-
|
|
256
|
-
let
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
UserDefaults.standard.set(curVersion, forKey: "pastVersion")
|
|
267
|
-
UserDefaults.standard.set(curVersionName, forKey: "pastVersionName")
|
|
268
|
-
UserDefaults.standard.set(false, forKey: "notifyAppReady")
|
|
350
|
+
|
|
351
|
+
let fallback: BundleInfo = self.implementation.getFallbackVersion()
|
|
352
|
+
let current: BundleInfo = self.implementation.getCurrentBundle()
|
|
353
|
+
let next: BundleInfo? = self.implementation.getNextVersion()
|
|
354
|
+
|
|
355
|
+
let success: Bool = current.getStatus() == BundleStatus.SUCCESS.localizedString
|
|
356
|
+
|
|
357
|
+
print("\(self.implementation.TAG) Fallback bundle is: \(fallback.toString())")
|
|
358
|
+
print("\(self.implementation.TAG) Current bundle is: \(current.toString())")
|
|
359
|
+
|
|
360
|
+
if (next != nil && !next!.isErrorStatus() && (next!.getVersionName() != current.getVersionName())) {
|
|
361
|
+
print("\(self.implementation.TAG) Next bundle is: \(next!.toString())")
|
|
362
|
+
if (self.implementation.set(bundle: next!) && self._reload()) {
|
|
363
|
+
print("\(self.implementation.TAG) Updated to bundle: \(next!)")
|
|
364
|
+
let _ = self.implementation.setNextVersion(next: Optional<String>.none)
|
|
269
365
|
} else {
|
|
270
|
-
print("
|
|
366
|
+
print("\(self.implementation.TAG) Updated to bundle: \(next!) Failed!")
|
|
271
367
|
}
|
|
272
|
-
} else if (!
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
368
|
+
} else if (!success) {
|
|
369
|
+
// There is a no next version, and the current version has failed
|
|
370
|
+
|
|
371
|
+
if(!current.isBuiltin()) {
|
|
372
|
+
// Don't try to roll back the builtin version. Nothing we can do.
|
|
373
|
+
|
|
374
|
+
self.implementation.rollback(bundle: current)
|
|
375
|
+
|
|
376
|
+
print("\(self.implementation.TAG) Update failed: 'notifyAppReady()' was never called.")
|
|
377
|
+
print("\(self.implementation.TAG) Version: \(current.toString()), is in error state.")
|
|
378
|
+
print("\(self.implementation.TAG) Will fallback to: \(fallback.toString()) on application restart.")
|
|
379
|
+
print("\(self.implementation.TAG) Did you forget to call 'notifyAppReady()' in your Capacitor App code?")
|
|
380
|
+
|
|
381
|
+
self.notifyListeners("updateFailed", data: [
|
|
382
|
+
"bundle": current.toJSON()
|
|
383
|
+
])
|
|
384
|
+
self.implementation.sendStats(action: "fail_update", versionName: current.getVersionName())
|
|
385
|
+
if (!fallback.isBuiltin() && !(fallback == current)) {
|
|
386
|
+
let res = self.implementation.set(bundle: fallback)
|
|
387
|
+
if (res && self._reload()) {
|
|
388
|
+
print("\(self.implementation.TAG) Revert to bundle: \(fallback.toString())")
|
|
389
|
+
} else {
|
|
390
|
+
print("\(self.implementation.TAG) Revert to bundle: \(fallback.toString()) Failed!")
|
|
391
|
+
}
|
|
286
392
|
} else {
|
|
287
|
-
|
|
393
|
+
if (self._reset(toLastSuccessful: false)) {
|
|
394
|
+
print("\(self.implementation.TAG) Reverted to 'builtin' bundle.")
|
|
395
|
+
}
|
|
288
396
|
}
|
|
289
|
-
|
|
290
|
-
if self.
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
397
|
+
|
|
398
|
+
if (self.autoDeleteFailed) {
|
|
399
|
+
print("\(self.implementation.TAG) Deleting failing bundle: \(current.toString())")
|
|
400
|
+
let res = self.implementation.delete(id: current.getId())
|
|
401
|
+
if (!res) {
|
|
402
|
+
print("\(self.implementation.TAG) Delete version deleted: \(current.toString())")
|
|
403
|
+
} else {
|
|
404
|
+
print("\(self.implementation.TAG) Failed to delete failed bundle: \(current.toString())")
|
|
405
|
+
}
|
|
294
406
|
}
|
|
407
|
+
} else {
|
|
408
|
+
// Nothing we can/should do by default if the 'builtin' bundle fails to call 'notifyAppReady()'.
|
|
295
409
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
410
|
+
} else if (!fallback.isBuiltin()) {
|
|
411
|
+
// There is a no next version, and the current version has succeeded
|
|
412
|
+
self.implementation.commit(bundle: current)
|
|
413
|
+
|
|
414
|
+
if(self.autoDeletePrevious) {
|
|
415
|
+
print("\(self.implementation.TAG) Version successfully loaded: \(current.toString())")
|
|
416
|
+
let res = self.implementation.delete(id: fallback.getId())
|
|
417
|
+
if (res) {
|
|
418
|
+
print("\(self.implementation.TAG) Deleted previous bundle: \(fallback.toString())")
|
|
419
|
+
} else {
|
|
420
|
+
print("\(self.implementation.TAG) Failed to delete previous bundle: \(fallback.toString())")
|
|
421
|
+
}
|
|
306
422
|
}
|
|
307
|
-
UserDefaults.standard.set("", forKey: "pastVersion")
|
|
308
|
-
UserDefaults.standard.set("", forKey: "pastVersionName")
|
|
309
423
|
}
|
|
310
424
|
}
|
|
311
425
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
protocol ObjectSavable {
|
|
4
|
+
func setObj<Object>(_ object: Object, forKey: String) throws where Object: Encodable
|
|
5
|
+
func getObj<Object>(forKey: String, castTo type: Object.Type) throws -> Object where Object: Decodable
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
enum ObjectSavableError: String, LocalizedError {
|
|
9
|
+
case unableToEncode = "Unable to encode object into data"
|
|
10
|
+
case noValue = "No data object found for the given key"
|
|
11
|
+
case unableToDecode = "Unable to decode object into given type"
|
|
12
|
+
|
|
13
|
+
var errorDescription: String? {
|
|
14
|
+
rawValue
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
extension UserDefaults: ObjectSavable {
|
|
19
|
+
func setObj<Object>(_ object: Object, forKey: String) throws where Object: Encodable {
|
|
20
|
+
let encoder = JSONEncoder()
|
|
21
|
+
do {
|
|
22
|
+
let data = try encoder.encode(object)
|
|
23
|
+
set(data, forKey: forKey)
|
|
24
|
+
} catch {
|
|
25
|
+
throw ObjectSavableError.unableToEncode
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
func getObj<Object>(forKey: String, castTo type: Object.Type) throws -> Object where Object: Decodable {
|
|
30
|
+
print("forKey", forKey)
|
|
31
|
+
guard let data = data(forKey: forKey) else { throw ObjectSavableError.noValue }
|
|
32
|
+
print("data", data)
|
|
33
|
+
let decoder = JSONDecoder()
|
|
34
|
+
do {
|
|
35
|
+
let object = try decoder.decode(type, from: data)
|
|
36
|
+
return object
|
|
37
|
+
} catch {
|
|
38
|
+
throw ObjectSavableError.unableToDecode
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|