@capgo/capacitor-updater 3.3.12 → 4.0.0-alpha.11

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,41 @@ 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
+ let res = implementation.list()
65
+ res.forEach { version in
66
+ print("\(self.implementation.TAG) Deleting obsolete bundle: \(version)")
67
+ _ = implementation.delete(id: version.getId())
68
+ }
69
+ }
70
+ UserDefaults.standard.set( self.currentVersionNative.description, forKey: "LatestVersionNative")
71
+ UserDefaults.standard.synchronize()
72
+ }
73
+
74
+ @objc func notifyDownload(id: String, percent: Int) {
75
+ let bundle = self.implementation.getBundleInfo(id: id)
76
+ self.notifyListeners("download", data: ["percent": percent, "bundle": bundle.toJSON()])
77
+ if (percent == 100) {
78
+ self.notifyListeners("downloadComplete", data: ["bundle": bundle.toJSON()])
79
+ }
63
80
  }
64
81
 
65
82
  @objc func getId(_ call: CAPPluginCall) {
@@ -71,12 +88,21 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
71
88
  }
72
89
 
73
90
  @objc func download(_ call: CAPPluginCall) {
74
- let url = URL(string: call.getString("url") ?? "")
91
+ guard let urlString = call.getString("url") else {
92
+ print("\(self.implementation.TAG) Download called without url")
93
+ call.reject("Download called without url")
94
+ return
95
+ }
96
+ guard let version = call.getString("version") else {
97
+ print("\(self.implementation.TAG) Download called without version")
98
+ call.reject("Download called without version")
99
+ return
100
+ }
101
+ let url = URL(string: urlString)
102
+ print("\(self.implementation.TAG) Downloading \(url!)")
75
103
  do {
76
- let res = try implementation.download(url: url!)
77
- call.resolve([
78
- "version": res
79
- ])
104
+ let res = try implementation.download(url: url!, version: version)
105
+ call.resolve(res.toJSON())
80
106
  } catch {
81
107
  call.reject("download failed", error.localizedDescription)
82
108
  }
@@ -84,18 +110,13 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
84
110
 
85
111
  private func _reload() -> Bool {
86
112
  guard let bridge = self.bridge else { return false }
87
-
113
+ let id = self.implementation.getCurrentBundleId()
114
+ let destHot = self.implementation.getPathHot(id: id)
115
+ print("\(self.implementation.TAG) Reloading \(id)")
88
116
  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
- }
117
+ vc.setServerBasePath(path: destHot.path)
118
+ self.checkAppReady()
119
+ return true
99
120
  }
100
121
  return false
101
122
  }
@@ -104,57 +125,87 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
104
125
  if (self._reload()) {
105
126
  call.resolve()
106
127
  } else {
107
- call.reject("Cannot reload")
128
+ call.reject("Reload failed")
129
+ print("\(self.implementation.TAG) Reload failed")
130
+ }
131
+ }
132
+
133
+ @objc func next(_ call: CAPPluginCall) {
134
+ guard let id = call.getString("id") else {
135
+ print("\(self.implementation.TAG) Next called without id")
136
+ call.reject("Next called without id")
137
+ return
138
+ }
139
+
140
+ print("\(self.implementation.TAG) Setting next active id \(id)")
141
+ if (!self.implementation.setNextVersion(next: id)) {
142
+ call.reject("Set next version failed. id \(id) does not exist.")
143
+ } else {
144
+ call.resolve(self.implementation.getBundleInfo(id: id).toJSON())
108
145
  }
109
146
  }
110
147
 
111
148
  @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()
149
+ guard let id = call.getString("id") else {
150
+ print("\(self.implementation.TAG) Set called without id")
151
+ call.reject("Set called without id")
152
+ return
153
+ }
154
+ let res = implementation.set(id: id)
155
+ print("\(self.implementation.TAG) Set active bundle: \(id)")
156
+ if (!res) {
157
+ print("\(self.implementation.TAG) Bundle successfully set to: \(id) ")
158
+ call.reject("Update failed, id \(id) doesn't exist")
119
159
  } else {
120
- call.reject("Update failed, version \(version) doesn't exist")
160
+ self.reload(call)
121
161
  }
122
162
  }
123
163
 
124
164
  @objc func delete(_ call: CAPPluginCall) {
125
- let version = call.getString("version") ?? ""
126
- let res = implementation.delete(version: version, versionName: "")
165
+ guard let id = call.getString("id") else {
166
+ print("\(self.implementation.TAG) Delete called without version")
167
+ call.reject("Delete called without id")
168
+ return
169
+ }
170
+ let res = implementation.delete(id: id)
127
171
  if (res) {
128
172
  call.resolve()
129
173
  } else {
130
- call.reject("Delete failed, version \(version) doesn't exist")
174
+ call.reject("Delete failed, id \(id) doesn't exist")
131
175
  }
132
176
  }
133
177
 
134
178
  @objc func list(_ call: CAPPluginCall) {
135
179
  let res = implementation.list()
180
+ var resArr: [[String: String]] = []
181
+ for v in res {
182
+ resArr.append(v.toJSON())
183
+ }
136
184
  call.resolve([
137
- "versions": res
185
+ "versions": resArr
138
186
  ])
139
187
  }
140
188
 
141
- @objc func _reset(toAutoUpdate: Bool) -> Bool {
189
+ @objc func getLatest(_ call: CAPPluginCall) {
190
+ let res = self.implementation.getLatest(url: URL(string: self.updateUrl)!)
191
+ call.resolve((res?.toDict())!)
192
+ }
193
+
194
+ @objc func _reset(toLastSuccessful: Bool) -> Bool {
142
195
  guard let bridge = self.bridge else { return false }
196
+
143
197
  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()
198
+ let fallback: BundleInfo = self.implementation.getFallbackVersion()
199
+ if (toLastSuccessful && !fallback.isBuiltin()) {
200
+ print("\(self.implementation.TAG) Resetting to: \(fallback.toString())")
201
+ return self.implementation.set(bundle: fallback) && self._reload()
149
202
  }
150
- implementation.reset()
151
- let pathPersist = implementation.getLastPathPersist()
152
- vc.setServerBasePath(path: pathPersist)
153
- UserDefaults.standard.set(pathPersist, forKey: "serverBasePath")
203
+ self.implementation.reset()
204
+ vc.setServerBasePath(path: "")
154
205
  DispatchQueue.main.async {
155
206
  vc.loadView()
156
207
  vc.viewDidLoad()
157
- print("✨ Capacitor-updater: Reset to original version")
208
+ print("\(self.implementation.TAG) Reset to builtin version")
158
209
  }
159
210
  return true
160
211
  }
@@ -162,150 +213,211 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
162
213
  }
163
214
 
164
215
  @objc func reset(_ call: CAPPluginCall) {
165
- let toAutoUpdate = call.getBool("toAutoUpdate") ?? false
166
- if (self._reset(toAutoUpdate: toAutoUpdate)) {
216
+ let toLastSuccessful = call.getBool("toLastSuccessful") ?? false
217
+ if (self._reset(toLastSuccessful: toLastSuccessful)) {
167
218
  return call.resolve()
168
219
  }
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
- ])
220
+ call.reject("\(self.implementation.TAG) Reset failed")
177
221
  }
178
222
 
179
223
  @objc func current(_ call: CAPPluginCall) {
180
- let pathHot = implementation.getLastPathHot()
181
- let current = pathHot.count >= 10 ? pathHot.suffix(10) : "builtin"
224
+ let bundle: BundleInfo = self.implementation.getCurrentBundle()
182
225
  call.resolve([
183
- "current": current,
184
- "currentNative": currentVersionNative
226
+ "bundle": bundle.toJSON(),
227
+ "native": self.currentVersionNative
185
228
  ])
186
229
  }
187
230
 
188
231
  @objc func notifyAppReady(_ call: CAPPluginCall) {
189
- UserDefaults.standard.set(true, forKey: "notifyAppReady")
232
+ print("\(self.implementation.TAG) Current bundle loaded successfully. ['notifyAppReady()' was called]")
233
+ let version = self.implementation.getCurrentBundle()
234
+ self.implementation.commit(bundle: version)
190
235
  call.resolve()
191
236
  }
192
237
 
193
- @objc func delayUpdate(_ call: CAPPluginCall) {
194
- UserDefaults.standard.set(true, forKey: "delayUpdate")
238
+ @objc func setDelay(_ call: CAPPluginCall) {
239
+ guard let delay = call.getBool("delay") else {
240
+ print("\(self.implementation.TAG) setDelay called without delay")
241
+ call.reject("setDelay called without delay")
242
+ return
243
+ }
244
+ UserDefaults.standard.set(delay, forKey: "delayUpdate")
195
245
  call.resolve()
196
246
  }
197
247
 
198
- @objc func cancelDelay(_ call: CAPPluginCall) {
199
- UserDefaults.standard.set(false, forKey: "delayUpdate")
200
- call.resolve()
248
+ private func _isAutoUpdateEnabled() -> Bool {
249
+ return self.autoUpdate && self.updateUrl != ""
201
250
  }
202
251
 
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) {
252
+ @objc func isAutoUpdateEnabled(_ call: CAPPluginCall) {
253
+ call.resolve([
254
+ "enabled": self._isAutoUpdateEnabled()
255
+ ])
256
+ }
257
+
258
+ func checkAppReady() {
259
+ self.appReadyCheck?.cancel()
260
+ self.appReadyCheck = DispatchWorkItem(block: {
261
+ self.DeferredNotifyAppReadyCheck()
262
+ })
263
+ print("\(self.implementation.TAG) Wait for \(self.appReadyTimeout) ms, then check for notifyAppReady")
264
+ DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(self.appReadyTimeout), execute: self.appReadyCheck!)
265
+ }
266
+
267
+ func DeferredNotifyAppReadyCheck() {
268
+ // Automatically roll back to fallback version if notifyAppReady has not been called yet
269
+ let current: BundleInfo = self.implementation.getCurrentBundle()
270
+ if(current.isBuiltin()) {
271
+ print("\(self.implementation.TAG) Built-in bundle is active. Nothing to do.")
272
+ return
273
+ }
274
+
275
+ if(BundleStatus.SUCCESS.localizedString != current.getStatus()) {
276
+ print("\(self.implementation.TAG) notifyAppReady was not called, roll back current bundle: \(current.toString())")
277
+ self.implementation.rollback(bundle: current)
278
+ let res = self._reset(toLastSuccessful: true)
279
+ if (!res) {
209
280
  return
210
281
  }
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"])
282
+ } else {
283
+ print("\(self.implementation.TAG) notifyAppReady was called. This is fine: \(current.toString())")
284
+ }
285
+ self.appReadyCheck = nil
286
+ }
287
+
288
+ @objc func appMovedToForeground() {
289
+ if (self._isAutoUpdateEnabled()) {
290
+ DispatchQueue.global(qos: .background).async {
291
+ print("\(self.implementation.TAG) Check for update via \(self.updateUrl)")
292
+ let url = URL(string: self.updateUrl)!
293
+ let res = self.implementation.getLatest(url: url)
294
+ if (res == nil) {
295
+ print("\(self.implementation.TAG) No result found in \(self.updateUrl)")
296
+ return
215
297
  }
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)
298
+ if ((res?.message) != nil) {
299
+ print("\(self.implementation.TAG) message \(res!.message ?? "")")
300
+ if (res?.major == true) {
301
+ self.notifyListeners("majorAvailable", data: ["version": res?.version ?? "0.0.0"])
302
+ }
303
+ return
304
+ }
305
+ guard let downloadUrl = URL(string: res?.url ?? "") else {
306
+ print("\(self.implementation.TAG) Error no url or wrong format")
307
+ return
308
+ }
309
+ let current = self.implementation.getCurrentBundle()
310
+ let latestVersionName = res?.version
311
+ if (latestVersionName != nil && latestVersionName != "" && current.getVersionName() != latestVersionName) {
312
+ let latest = self.implementation.getBundleInfoByVersionName(version: latestVersionName!)
313
+ if (latest != nil) {
314
+ if(latest!.isErrorStatus()) {
315
+ print("\(self.implementation.TAG) Latest version already exists, and is in error state. Aborting update.")
316
+ return
317
+ }
318
+ if(latest!.isDownloaded()){
319
+ print("\(self.implementation.TAG) Latest version already exists and download is NOT required. Update will occur next time app moves to background.")
320
+ let _ = self.implementation.setNextVersion(next: latest!.getId())
321
+ return
322
+ }
323
+ }
324
+
325
+ do {
326
+ print("\(self.implementation.TAG) New bundle: \(latestVersionName!) found. Current is: \(current.getVersionName()). Update will occur next time app moves to background.")
327
+ let next = try self.implementation.download(url: downloadUrl, version: latestVersionName!)
328
+
329
+ let _ = self.implementation.setNextVersion(next: next.getId())
330
+ } catch {
331
+ print("\(self.implementation.TAG) Error downloading file", error.localizedDescription)
332
+ }
236
333
  }
237
- } else {
238
- print("✨ Capacitor-updater: No need to update, \(currentVersion) is the latest")
239
334
  }
240
335
  }
336
+
337
+ self.checkAppReady()
241
338
  }
242
339
 
243
340
  @objc func appMovedToBackground() {
244
- print("✨ Capacitor-updater: Check for waiting update")
341
+ print("\(self.implementation.TAG) Check for waiting update")
245
342
  let delayUpdate = UserDefaults.standard.bool(forKey: "delayUpdate")
246
343
  UserDefaults.standard.set(false, forKey: "delayUpdate")
247
344
  if (delayUpdate) {
248
- print("✨ Capacitor-updater: Update delayed to next backgrounding")
345
+ print("\(self.implementation.TAG) Update delayed to next backgrounding")
249
346
  return
250
347
  }
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")
348
+
349
+ let fallback: BundleInfo = self.implementation.getFallbackVersion()
350
+ let current: BundleInfo = self.implementation.getCurrentBundle()
351
+ let next: BundleInfo? = self.implementation.getNextVersion()
352
+
353
+ let success: Bool = current.getStatus() == BundleStatus.SUCCESS.localizedString
354
+
355
+ print("\(self.implementation.TAG) Fallback bundle is: \(fallback.toString())")
356
+ print("\(self.implementation.TAG) Current bundle is: \(current.toString())")
357
+
358
+ if (next != nil && !next!.isErrorStatus() && (next!.getVersionName() != current.getVersionName())) {
359
+ print("\(self.implementation.TAG) Next bundle is: \(next!.toString())")
360
+ if (self.implementation.set(bundle: next!) && self._reload()) {
361
+ print("\(self.implementation.TAG) Updated to bundle: \(next!)")
362
+ let _ = self.implementation.setNextVersion(next: Optional<String>.none)
269
363
  } else {
270
- print("✨ Capacitor-updater: Auto update to version: \(nextVersionName) Failed");
364
+ print("\(self.implementation.TAG) Updated to bundle: \(next!) Failed!")
271
365
  }
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")
366
+ } else if (!success) {
367
+ // There is a no next version, and the current version has failed
368
+
369
+ if(!current.isBuiltin()) {
370
+ // Don't try to roll back the builtin version. Nothing we can do.
371
+
372
+ self.implementation.rollback(bundle: current)
373
+
374
+ print("\(self.implementation.TAG) Update failed: 'notifyAppReady()' was never called.")
375
+ print("\(self.implementation.TAG) Version: \(current.toString()), is in error state.")
376
+ print("\(self.implementation.TAG) Will fallback to: \(fallback.toString()) on application restart.")
377
+ print("\(self.implementation.TAG) Did you forget to call 'notifyAppReady()' in your Capacitor App code?")
378
+
379
+ self.notifyListeners("updateFailed", data: [
380
+ "bundle": current.toJSON()
381
+ ])
382
+ self.implementation.sendStats(action: "fail_update", versionName: current.getVersionName())
383
+ if (!fallback.isBuiltin() && !(fallback == current)) {
384
+ let res = self.implementation.set(bundle: fallback)
385
+ if (res && self._reload()) {
386
+ print("\(self.implementation.TAG) Revert to bundle: \(fallback.toString())")
387
+ } else {
388
+ print("\(self.implementation.TAG) Revert to bundle: \(fallback.toString()) Failed!")
389
+ }
286
390
  } else {
287
- print("✨ Capacitor-updater: Revert to version: \(pastVersionName == "" ? "builtin" : pastVersionName) Failed");
391
+ if (self._reset(toLastSuccessful: false)) {
392
+ print("\(self.implementation.TAG) Reverted to 'builtin' bundle.")
393
+ }
288
394
  }
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")
395
+
396
+ if (self.autoDeleteFailed) {
397
+ print("\(self.implementation.TAG) Deleting failing bundle: \(current.toString())")
398
+ let res = self.implementation.delete(id: current.getId())
399
+ if (!res) {
400
+ print("\(self.implementation.TAG) Delete version deleted: \(current.toString())")
401
+ } else {
402
+ print("\(self.implementation.TAG) Failed to delete failed bundle: \(current.toString())")
403
+ }
294
404
  }
405
+ } else {
406
+ // Nothing we can/should do by default if the 'builtin' bundle fails to call 'notifyAppReady()'.
295
407
  }
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)")
408
+ } else if (!fallback.isBuiltin()) {
409
+ // There is a no next version, and the current version has succeeded
410
+ self.implementation.commit(bundle: current)
411
+
412
+ if(self.autoDeletePrevious) {
413
+ print("\(self.implementation.TAG) Version successfully loaded: \(current.toString())")
414
+ let res = self.implementation.delete(id: fallback.getId())
415
+ if (res) {
416
+ print("\(self.implementation.TAG) Deleted previous bundle: \(fallback.toString())")
417
+ } else {
418
+ print("\(self.implementation.TAG) Failed to delete previous bundle: \(fallback.toString())")
419
+ }
306
420
  }
307
- UserDefaults.standard.set("", forKey: "pastVersion")
308
- UserDefaults.standard.set("", forKey: "pastVersionName")
309
421
  }
310
422
  }
311
423
  }
@@ -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
+ }