@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.
@@ -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 autoUpdateUrlDefault = "https://capgo.app/api/auto_update"
13
- static let statsUrlDefault = "https://capgo.app/api/stats"
14
- private var autoUpdateUrl = ""
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 statsUrl = ""
18
- private var resetWhenUpdate = true;
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("✨ Capacitor-updater: Cannot get version native \(currentVersionNative)")
29
+ print("\(self.implementation.TAG) Cannot get version native \(currentVersionNative)")
25
30
  }
26
- autoUpdateUrl = getConfigValue("autoUpdateUrl") as? String ?? CapacitorUpdaterPlugin.autoUpdateUrlDefault
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
- resetWhenUpdate = getConfigValue("resetWhenUpdate") as? Bool ?? true
45
+
36
46
  if (resetWhenUpdate) {
37
- var LatestVersionNative: Version = "0.0.0"
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
- @objc func notifyDownload(percent: Int) {
62
- self.notifyListeners("download", data: ["percent": percent])
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 url = URL(string: call.getString("url") ?? "")
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
- let pathHot = implementation.getLastPathHot()
90
- let pathPersist = implementation.getLastPathPersist()
91
- if (pathHot != "" && pathPersist != "") {
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("Cannot reload")
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 version = call.getString("version") ?? ""
113
- let versionName = call.getString("versionName") ?? version
114
- let res = implementation.set(version: version, versionName: versionName)
115
-
116
- if (res && self._reload()) {
117
- print("✨ Capacitor-updater: Set to version: \(version) versionName: \(versionName)")
118
- call.resolve()
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
- call.reject("Update failed, version \(version) doesn't exist")
162
+ self.reload(call)
121
163
  }
122
164
  }
123
165
 
124
166
  @objc func delete(_ call: CAPPluginCall) {
125
- let version = call.getString("version") ?? ""
126
- let res = implementation.delete(version: version, versionName: "")
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, version \(version) doesn't exist")
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": res
187
+ "versions": resArr
138
188
  ])
139
189
  }
140
190
 
141
- @objc func _reset(toAutoUpdate: Bool) -> Bool {
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 LatestVersionAutoUpdate = UserDefaults.standard.string(forKey: "LatestVersionAutoUpdate") ?? ""
145
- let LatestVersionNameAutoUpdate = UserDefaults.standard.string(forKey: "LatestVersionNameAutoUpdate") ?? ""
146
- if(toAutoUpdate && LatestVersionAutoUpdate != "" && LatestVersionNameAutoUpdate != "") {
147
- let res = implementation.set(version: LatestVersionAutoUpdate, versionName: LatestVersionNameAutoUpdate)
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
- let pathPersist = implementation.getLastPathPersist()
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("✨ Capacitor-updater: Reset to original version")
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 toAutoUpdate = call.getBool("toAutoUpdate") ?? false
166
- if (self._reset(toAutoUpdate: toAutoUpdate)) {
218
+ let toLastSuccessful = call.getBool("toLastSuccessful") ?? false
219
+ if (self._reset(toLastSuccessful: toLastSuccessful)) {
167
220
  return call.resolve()
168
221
  }
169
- call.reject("✨ Capacitor-updater: Reset failed")
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 pathHot = implementation.getLastPathHot()
181
- let current = pathHot.count >= 10 ? pathHot.suffix(10) : "builtin"
226
+ let bundle: BundleInfo = self.implementation.getCurrentBundle()
182
227
  call.resolve([
183
- "current": current,
184
- "currentNative": currentVersionNative
228
+ "bundle": bundle.toJSON(),
229
+ "native": self.currentVersionNative
185
230
  ])
186
231
  }
187
232
 
188
233
  @objc func notifyAppReady(_ call: CAPPluginCall) {
189
- UserDefaults.standard.set(true, forKey: "notifyAppReady")
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 delayUpdate(_ call: CAPPluginCall) {
194
- UserDefaults.standard.set(true, forKey: "delayUpdate")
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
- @objc func cancelDelay(_ call: CAPPluginCall) {
199
- UserDefaults.standard.set(false, forKey: "delayUpdate")
200
- call.resolve()
250
+ private func _isAutoUpdateEnabled() -> Bool {
251
+ return self.autoUpdate && self.updateUrl != ""
201
252
  }
202
253
 
203
- @objc func appMovedToForeground() {
204
- DispatchQueue.global(qos: .background).async {
205
- print("✨ Capacitor-updater: Check for update in the server")
206
- let url = URL(string: self.autoUpdateUrl)!
207
- let res = self.implementation.getLatest(url: url)
208
- if (res == nil) {
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
- guard let downloadUrl = URL(string: res?.url ?? "") else {
212
- print("✨ Capacitor-updater: Error \(res?.message ?? "Unknow error")")
213
- if (res?.major == true) {
214
- self.notifyListeners("majorAvailable", data: ["version": res?.version ?? "0.0.0"])
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
- return
217
- }
218
- let currentVersion = self.implementation.getVersionName()
219
- var failingVersion: Version = "0.0.0"
220
- var newVersion: Version = "0.0.0"
221
- do {
222
- newVersion = try Version(res?.version ?? "0.0.0")
223
- failingVersion = try Version(UserDefaults.standard.string(forKey: "failingVersion") ?? "0.0.0")
224
- } catch {
225
- print("✨ Capacitor-updater: Cannot get version \(failingVersion) \(newVersion)")
226
- }
227
- if (newVersion != "0.0.0" && newVersion != failingVersion) {
228
- do {
229
- let dl = try self.implementation.download(url: downloadUrl)
230
- print("✨ Capacitor-updater: New version: \(newVersion) found. Current is \(currentVersion == "" ? "builtin" : currentVersion), next backgrounding will trigger update")
231
- UserDefaults.standard.set(dl, forKey: "nextVersion")
232
- UserDefaults.standard.set(newVersion.description, forKey: "nextVersionName")
233
- self.notifyListeners("updateAvailable", data: ["version": newVersion])
234
- } catch {
235
- print("✨ Capacitor-updater: Download version \(newVersion) fail", error.localizedDescription)
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("✨ Capacitor-updater: Check for waiting update")
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("✨ Capacitor-updater: Update delayed to next backgrounding")
347
+ print("\(self.implementation.TAG) Update delayed to next backgrounding")
249
348
  return
250
349
  }
251
- let nextVersion = UserDefaults.standard.string(forKey: "nextVersion") ?? ""
252
- let nextVersionName = UserDefaults.standard.string(forKey: "nextVersionName") ?? ""
253
- let pastVersion = UserDefaults.standard.string(forKey: "pastVersion") ?? ""
254
- let pastVersionName = UserDefaults.standard.string(forKey: "pastVersionName") ?? ""
255
- let notifyAppReady = UserDefaults.standard.bool(forKey: "notifyAppReady")
256
- let curVersion = implementation.getLastPathPersist().components(separatedBy: "/").last!
257
- let curVersionName = implementation.getVersionName()
258
- if (nextVersion != "" && nextVersionName != "") {
259
- let res = implementation.set(version: nextVersion, versionName: nextVersionName)
260
- if (res && self._reload()) {
261
- print("✨ Capacitor-updater: Auto update to version: \(nextVersionName)")
262
- UserDefaults.standard.set(nextVersion, forKey: "LatestVersionAutoUpdate")
263
- UserDefaults.standard.set(nextVersionName, forKey: "LatestVersionNameAutoUpdate")
264
- UserDefaults.standard.set("", forKey: "nextVersion")
265
- UserDefaults.standard.set("", forKey: "nextVersionName")
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("✨ Capacitor-updater: Auto update to version: \(nextVersionName) Failed");
366
+ print("\(self.implementation.TAG) Updated to bundle: \(next!) Failed!")
271
367
  }
272
- } else if (!notifyAppReady && curVersionName != "") {
273
- print("✨ Capacitor-updater: notifyAppReady never trigger")
274
- print("✨ Capacitor-updater: Version: \(curVersionName), is considered broken")
275
- print("✨ Capacitor-updater: Will downgraded to version: \(pastVersionName == "" ? "builtin" : pastVersionName) for next start")
276
- print("✨ Capacitor-updater: Don't forget to trigger 'notifyAppReady()' in js code to validate a version.")
277
- implementation.sendStats(action: "revert", version: curVersionName)
278
- if (pastVersion != "" && pastVersionName != "") {
279
- let res = implementation.set(version: pastVersion, versionName: pastVersionName)
280
- if (res && self._reload()) {
281
- print("✨ Capacitor-updater: Revert to version: \(pastVersionName == "" ? "builtin" : pastVersionName)")
282
- UserDefaults.standard.set(pastVersion, forKey: "LatestVersionAutoUpdate")
283
- UserDefaults.standard.set(pastVersionName, forKey: "LatestVersionNameAutoUpdate")
284
- UserDefaults.standard.set("", forKey: "pastVersion")
285
- UserDefaults.standard.set("", forKey: "pastVersionName")
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
- print("✨ Capacitor-updater: Revert to version: \(pastVersionName == "" ? "builtin" : pastVersionName) Failed");
393
+ if (self._reset(toLastSuccessful: false)) {
394
+ print("\(self.implementation.TAG) Reverted to 'builtin' bundle.")
395
+ }
288
396
  }
289
- } else {
290
- if self._reset(toAutoUpdate: false) {
291
- UserDefaults.standard.set("", forKey: "LatestVersionAutoUpdate")
292
- UserDefaults.standard.set("", forKey: "LatestVersionNameAutoUpdate")
293
- print("✨ Capacitor-updater: Auto reset done")
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
- UserDefaults.standard.set(curVersionName, forKey: "failingVersion")
297
- let res = implementation.delete(version: curVersion, versionName: curVersionName)
298
- if (res) {
299
- print("✨ Capacitor-updater: Delete failing version: \(curVersionName)")
300
- }
301
- } else if (pastVersion != "") {
302
- print("✨ Capacitor-updater: Validated version: \(curVersionName)")
303
- let res = implementation.delete(version: pastVersion, versionName: curVersionName)
304
- if (res) {
305
- print("✨ Capacitor-updater: Delete past version: \(pastVersionName)")
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
+ }