@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.
- 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 +285 -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,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
|
-
|
|
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
|
+
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
|
|
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
|
-
|
|
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
|
-
}
|
|
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("
|
|
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
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
160
|
+
self.reload(call)
|
|
121
161
|
}
|
|
122
162
|
}
|
|
123
163
|
|
|
124
164
|
@objc func delete(_ call: CAPPluginCall) {
|
|
125
|
-
let
|
|
126
|
-
|
|
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,
|
|
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":
|
|
185
|
+
"versions": resArr
|
|
138
186
|
])
|
|
139
187
|
}
|
|
140
188
|
|
|
141
|
-
@objc func
|
|
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
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
|
|
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("
|
|
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
|
|
166
|
-
if (self._reset(
|
|
216
|
+
let toLastSuccessful = call.getBool("toLastSuccessful") ?? false
|
|
217
|
+
if (self._reset(toLastSuccessful: toLastSuccessful)) {
|
|
167
218
|
return call.resolve()
|
|
168
219
|
}
|
|
169
|
-
call.reject("
|
|
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
|
|
181
|
-
let current = pathHot.count >= 10 ? pathHot.suffix(10) : "builtin"
|
|
224
|
+
let bundle: BundleInfo = self.implementation.getCurrentBundle()
|
|
182
225
|
call.resolve([
|
|
183
|
-
"
|
|
184
|
-
"
|
|
226
|
+
"bundle": bundle.toJSON(),
|
|
227
|
+
"native": self.currentVersionNative
|
|
185
228
|
])
|
|
186
229
|
}
|
|
187
230
|
|
|
188
231
|
@objc func notifyAppReady(_ call: CAPPluginCall) {
|
|
189
|
-
|
|
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
|
|
194
|
-
|
|
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
|
-
|
|
199
|
-
|
|
200
|
-
call.resolve()
|
|
248
|
+
private func _isAutoUpdateEnabled() -> Bool {
|
|
249
|
+
return self.autoUpdate && self.updateUrl != ""
|
|
201
250
|
}
|
|
202
251
|
|
|
203
|
-
@objc func
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
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("
|
|
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("
|
|
345
|
+
print("\(self.implementation.TAG) Update delayed to next backgrounding")
|
|
249
346
|
return
|
|
250
347
|
}
|
|
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")
|
|
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("
|
|
364
|
+
print("\(self.implementation.TAG) Updated to bundle: \(next!) Failed!")
|
|
271
365
|
}
|
|
272
|
-
} else if (!
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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
|
-
|
|
391
|
+
if (self._reset(toLastSuccessful: false)) {
|
|
392
|
+
print("\(self.implementation.TAG) Reverted to 'builtin' bundle.")
|
|
393
|
+
}
|
|
288
394
|
}
|
|
289
|
-
|
|
290
|
-
if self.
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
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
|
+
}
|