@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
|
@@ -10,6 +10,11 @@ extension URL {
|
|
|
10
10
|
return FileManager().fileExists(atPath: self.path)
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
|
+
extension Date {
|
|
14
|
+
func adding(minutes: Int) -> Date {
|
|
15
|
+
return Calendar.current.date(byAdding: .minute, value: minutes, to: self)!
|
|
16
|
+
}
|
|
17
|
+
}
|
|
13
18
|
struct AppVersionDec: Decodable {
|
|
14
19
|
let version: String?
|
|
15
20
|
let url: String?
|
|
@@ -22,6 +27,20 @@ public class AppVersion: NSObject {
|
|
|
22
27
|
var message: String?
|
|
23
28
|
var major: Bool?
|
|
24
29
|
}
|
|
30
|
+
|
|
31
|
+
extension AppVersion {
|
|
32
|
+
func toDict() -> [String:Any] {
|
|
33
|
+
var dict = [String:Any]()
|
|
34
|
+
let otherSelf = Mirror(reflecting: self)
|
|
35
|
+
for child in otherSelf.children {
|
|
36
|
+
if let key = child.label {
|
|
37
|
+
dict[key] = child.value
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return dict
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
25
44
|
extension OperatingSystemVersion {
|
|
26
45
|
func getFullVersion(separator: String = ".") -> String {
|
|
27
46
|
return "\(majorVersion)\(separator)\(minorVersion)\(separator)\(patchVersion)"
|
|
@@ -36,9 +55,43 @@ extension Bundle {
|
|
|
36
55
|
}
|
|
37
56
|
}
|
|
38
57
|
|
|
58
|
+
extension ISO8601DateFormatter {
|
|
59
|
+
convenience init(_ formatOptions: Options) {
|
|
60
|
+
self.init()
|
|
61
|
+
self.formatOptions = formatOptions
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
extension Formatter {
|
|
65
|
+
static let iso8601withFractionalSeconds = ISO8601DateFormatter([.withInternetDateTime, .withFractionalSeconds])
|
|
66
|
+
}
|
|
67
|
+
extension Date {
|
|
68
|
+
var iso8601withFractionalSeconds: String { return Formatter.iso8601withFractionalSeconds.string(from: self) }
|
|
69
|
+
}
|
|
70
|
+
extension String {
|
|
71
|
+
|
|
72
|
+
var fileURL: URL {
|
|
73
|
+
return URL(fileURLWithPath: self)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
var lastPathComponent:String {
|
|
77
|
+
get {
|
|
78
|
+
return fileURL.lastPathComponent
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
var iso8601withFractionalSeconds: Date? {
|
|
82
|
+
return Formatter.iso8601withFractionalSeconds.date(from: self)
|
|
83
|
+
}
|
|
84
|
+
func trim(using characterSet: CharacterSet = .whitespacesAndNewlines) -> String {
|
|
85
|
+
return trimmingCharacters(in: characterSet)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
39
89
|
enum CustomError: Error {
|
|
40
90
|
// Throw when an unzip fail
|
|
41
91
|
case cannotUnzip
|
|
92
|
+
case cannotUnflat
|
|
93
|
+
case cannotCreateDirectory
|
|
94
|
+
case cannotDeleteDirectory
|
|
42
95
|
|
|
43
96
|
// Throw in all other cases
|
|
44
97
|
case unexpected(code: Int)
|
|
@@ -52,6 +105,21 @@ extension CustomError: LocalizedError {
|
|
|
52
105
|
"The file cannot be unzip",
|
|
53
106
|
comment: "Invalid zip"
|
|
54
107
|
)
|
|
108
|
+
case .cannotCreateDirectory:
|
|
109
|
+
return NSLocalizedString(
|
|
110
|
+
"The folder cannot be created",
|
|
111
|
+
comment: "Invalid folder"
|
|
112
|
+
)
|
|
113
|
+
case .cannotDeleteDirectory:
|
|
114
|
+
return NSLocalizedString(
|
|
115
|
+
"The folder cannot be deleted",
|
|
116
|
+
comment: "Invalid folder"
|
|
117
|
+
)
|
|
118
|
+
case .cannotUnflat:
|
|
119
|
+
return NSLocalizedString(
|
|
120
|
+
"The file cannot be unflat",
|
|
121
|
+
comment: "Invalid folder"
|
|
122
|
+
)
|
|
55
123
|
case .unexpected(_):
|
|
56
124
|
return NSLocalizedString(
|
|
57
125
|
"An unexpected error occurred.",
|
|
@@ -63,21 +131,29 @@ extension CustomError: LocalizedError {
|
|
|
63
131
|
|
|
64
132
|
@objc public class CapacitorUpdater: NSObject {
|
|
65
133
|
|
|
66
|
-
private
|
|
67
|
-
private
|
|
68
|
-
private
|
|
134
|
+
private let versionBuild = Bundle.main.releaseVersionNumber ?? ""
|
|
135
|
+
private let versionCode = Bundle.main.buildVersionNumber ?? ""
|
|
136
|
+
private let versionOs = ProcessInfo().operatingSystemVersion.getFullVersion()
|
|
137
|
+
private let documentsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
|
|
138
|
+
private let libraryDir = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first!
|
|
139
|
+
private let bundleDirectoryHot = "versions"
|
|
140
|
+
private let DEFAULT_FOLDER = ""
|
|
141
|
+
private let bundleDirectory = "NoCloud/ionic_built_snapshots"
|
|
142
|
+
private let INFO_SUFFIX = "_info"
|
|
143
|
+
private let FALLBACK_VERSION = "pastVersion"
|
|
144
|
+
private let NEXT_VERSION = "nextVersion"
|
|
145
|
+
|
|
69
146
|
private var lastPathHot = ""
|
|
70
147
|
private var lastPathPersist = ""
|
|
71
|
-
private let basePathHot = "versions"
|
|
72
|
-
private let basePathPersist = "NoCloud/ionic_built_snapshots"
|
|
73
|
-
private let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
|
|
74
|
-
private let libraryUrl = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first!
|
|
75
148
|
|
|
149
|
+
public let TAG = "✨ Capacitor-updater:";
|
|
150
|
+
public let CAP_SERVER_PATH = "serverBasePath"
|
|
151
|
+
public let pluginVersion = "4.0.0-alpha.10"
|
|
76
152
|
public var statsUrl = ""
|
|
77
153
|
public var appId = ""
|
|
78
154
|
public var deviceID = UIDevice.current.identifierForVendor?.uuidString ?? ""
|
|
79
|
-
|
|
80
|
-
public var
|
|
155
|
+
|
|
156
|
+
public var notifyDownload: (String, Int) -> Void = { _,_ in }
|
|
81
157
|
|
|
82
158
|
private func calcTotalPercent(percent: Int, min: Int, max: Int) -> Int {
|
|
83
159
|
return (percent * (max - min)) / 100 + min;
|
|
@@ -92,25 +168,27 @@ extension CustomError: LocalizedError {
|
|
|
92
168
|
// Hot Reload path /var/mobile/Containers/Data/Application/8C0C07BE-0FD3-4FD4-B7DF-90A88E12B8C3/Documents/FOLDER
|
|
93
169
|
// Normal /private/var/containers/Bundle/Application/8C0C07BE-0FD3-4FD4-B7DF-90A88E12B8C3/App.app/public
|
|
94
170
|
|
|
95
|
-
private func prepareFolder(source: URL) {
|
|
171
|
+
private func prepareFolder(source: URL) throws {
|
|
96
172
|
if (!FileManager.default.fileExists(atPath: source.path)) {
|
|
97
173
|
do {
|
|
98
174
|
try FileManager.default.createDirectory(atPath: source.path, withIntermediateDirectories: true, attributes: nil)
|
|
99
175
|
} catch {
|
|
100
|
-
print("
|
|
176
|
+
print("\(self.TAG) Cannot createDirectory \(source.path)")
|
|
177
|
+
throw CustomError.cannotCreateDirectory
|
|
101
178
|
}
|
|
102
179
|
}
|
|
103
180
|
}
|
|
104
181
|
|
|
105
|
-
private func deleteFolder(source: URL) {
|
|
182
|
+
private func deleteFolder(source: URL) throws {
|
|
106
183
|
do {
|
|
107
184
|
try FileManager.default.removeItem(atPath: source.path)
|
|
108
185
|
} catch {
|
|
109
|
-
print("
|
|
186
|
+
print("\(self.TAG) File not removed. \(source.path)")
|
|
187
|
+
throw CustomError.cannotDeleteDirectory
|
|
110
188
|
}
|
|
111
189
|
}
|
|
112
190
|
|
|
113
|
-
private func unflatFolder(source: URL, dest: URL) -> Bool {
|
|
191
|
+
private func unflatFolder(source: URL, dest: URL) throws -> Bool {
|
|
114
192
|
let index = source.appendingPathComponent("index.html")
|
|
115
193
|
do {
|
|
116
194
|
let files = try FileManager.default.contentsOfDirectory(atPath: source.path)
|
|
@@ -122,37 +200,37 @@ extension CustomError: LocalizedError {
|
|
|
122
200
|
return false
|
|
123
201
|
}
|
|
124
202
|
} catch {
|
|
125
|
-
print("
|
|
126
|
-
|
|
203
|
+
print("\(self.TAG) File not moved. source: \(source.path) dest: \(dest.path)")
|
|
204
|
+
throw CustomError.cannotUnflat
|
|
127
205
|
}
|
|
128
206
|
}
|
|
129
207
|
|
|
130
|
-
private func saveDownloaded(sourceZip: URL,
|
|
131
|
-
prepareFolder(source: base)
|
|
132
|
-
let destHot = base.appendingPathComponent(
|
|
133
|
-
let destUnZip =
|
|
208
|
+
private func saveDownloaded(sourceZip: URL, id: String, base: URL) throws {
|
|
209
|
+
try prepareFolder(source: base)
|
|
210
|
+
let destHot = base.appendingPathComponent(id)
|
|
211
|
+
let destUnZip = documentsDir.appendingPathComponent(randomString(length: 10))
|
|
134
212
|
if (!SSZipArchive.unzipFile(atPath: sourceZip.path, toDestination: destUnZip.path)) {
|
|
135
213
|
throw CustomError.cannotUnzip
|
|
136
214
|
}
|
|
137
|
-
if (unflatFolder(source: destUnZip, dest: destHot)) {
|
|
138
|
-
deleteFolder(source: destUnZip)
|
|
215
|
+
if (try unflatFolder(source: destUnZip, dest: destHot)) {
|
|
216
|
+
try deleteFolder(source: destUnZip)
|
|
139
217
|
}
|
|
140
218
|
}
|
|
141
219
|
|
|
142
220
|
public func getLatest(url: URL) -> AppVersion? {
|
|
143
221
|
let semaphore = DispatchSemaphore(value: 0)
|
|
144
222
|
let latest = AppVersion()
|
|
145
|
-
let
|
|
146
|
-
"
|
|
147
|
-
"
|
|
148
|
-
"
|
|
149
|
-
"
|
|
150
|
-
"
|
|
151
|
-
"
|
|
152
|
-
"
|
|
153
|
-
"
|
|
223
|
+
let parameters: [String: String] = [
|
|
224
|
+
"platform": "ios",
|
|
225
|
+
"device_id": self.deviceID,
|
|
226
|
+
"app_id": self.appId,
|
|
227
|
+
"version_build": self.versionBuild,
|
|
228
|
+
"version_code": self.versionCode,
|
|
229
|
+
"version_os": self.versionOs,
|
|
230
|
+
"plugin_version": self.pluginVersion,
|
|
231
|
+
"version_name": self.getCurrentBundle().getVersionName()
|
|
154
232
|
]
|
|
155
|
-
let request = AF.request(url,
|
|
233
|
+
let request = AF.request(url, method: .post,parameters: parameters, encoder: JSONParameterEncoder.default)
|
|
156
234
|
|
|
157
235
|
request.validate().responseDecodable(of: AppVersionDec.self) { response in
|
|
158
236
|
switch response.result {
|
|
@@ -168,9 +246,10 @@ extension CustomError: LocalizedError {
|
|
|
168
246
|
}
|
|
169
247
|
if let message = response.value?.message {
|
|
170
248
|
latest.message = message
|
|
249
|
+
print("\(self.TAG) Auto-update message: \(message)")
|
|
171
250
|
}
|
|
172
251
|
case let .failure(error):
|
|
173
|
-
print("
|
|
252
|
+
print("\(self.TAG) Error getting Latest", error )
|
|
174
253
|
}
|
|
175
254
|
semaphore.signal()
|
|
176
255
|
}
|
|
@@ -178,9 +257,15 @@ extension CustomError: LocalizedError {
|
|
|
178
257
|
return latest.url != "" ? latest : nil
|
|
179
258
|
}
|
|
180
259
|
|
|
181
|
-
|
|
260
|
+
private func setCurrentBundle(bundle: String) {
|
|
261
|
+
UserDefaults.standard.set(bundle, forKey: self.CAP_SERVER_PATH)
|
|
262
|
+
print("\(self.TAG) Current bundle set to: \(bundle)")
|
|
263
|
+
UserDefaults.standard.synchronize()
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
public func download(url: URL, version: String) throws -> BundleInfo {
|
|
182
267
|
let semaphore = DispatchSemaphore(value: 0)
|
|
183
|
-
|
|
268
|
+
let id: String = self.randomString(length: 10)
|
|
184
269
|
var mainError: NSError? = nil
|
|
185
270
|
let destination: DownloadRequest.Destination = { _, _ in
|
|
186
271
|
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
|
|
@@ -192,112 +277,150 @@ extension CustomError: LocalizedError {
|
|
|
192
277
|
|
|
193
278
|
request.downloadProgress { progress in
|
|
194
279
|
let percent = self.calcTotalPercent(percent: Int(progress.fractionCompleted * 100), min: 10, max: 70)
|
|
195
|
-
self.notifyDownload(percent)
|
|
280
|
+
self.notifyDownload(id, percent)
|
|
196
281
|
}
|
|
197
282
|
request.responseURL { (response) in
|
|
198
283
|
if let fileURL = response.fileURL {
|
|
199
284
|
switch response.result {
|
|
200
285
|
case .success:
|
|
201
|
-
self.notifyDownload(71)
|
|
202
|
-
version = self.randomString(length: 10)
|
|
286
|
+
self.notifyDownload(id, 71)
|
|
203
287
|
do {
|
|
204
|
-
try self.saveDownloaded(sourceZip: fileURL,
|
|
205
|
-
self.notifyDownload(85)
|
|
206
|
-
try self.saveDownloaded(sourceZip: fileURL,
|
|
207
|
-
self.notifyDownload(100)
|
|
208
|
-
self.deleteFolder(source: fileURL)
|
|
288
|
+
try self.saveDownloaded(sourceZip: fileURL, id: id, base: self.documentsDir.appendingPathComponent(self.bundleDirectoryHot))
|
|
289
|
+
self.notifyDownload(id, 85)
|
|
290
|
+
try self.saveDownloaded(sourceZip: fileURL, id: id, base: self.libraryDir.appendingPathComponent(self.bundleDirectory))
|
|
291
|
+
self.notifyDownload(id, 100)
|
|
292
|
+
try self.deleteFolder(source: fileURL)
|
|
209
293
|
} catch {
|
|
210
|
-
print("
|
|
294
|
+
print("\(self.TAG) download unzip error", error)
|
|
211
295
|
mainError = error as NSError
|
|
212
296
|
}
|
|
213
297
|
case let .failure(error):
|
|
214
|
-
print("
|
|
298
|
+
print("\(self.TAG) download error", error)
|
|
215
299
|
mainError = error as NSError
|
|
216
300
|
}
|
|
217
301
|
}
|
|
218
302
|
semaphore.signal()
|
|
219
303
|
}
|
|
220
|
-
self.
|
|
304
|
+
self.saveBundleInfo(id: id, bundle: BundleInfo(id: id, version: version, status: BundleStatus.DOWNLOADING, downloaded: Date()))
|
|
305
|
+
self.notifyDownload(id, 0)
|
|
221
306
|
semaphore.wait()
|
|
222
307
|
if (mainError != nil) {
|
|
223
308
|
throw mainError!
|
|
224
309
|
}
|
|
225
|
-
|
|
310
|
+
let info: BundleInfo = BundleInfo(id: id, version: version, status: BundleStatus.PENDING, downloaded: Date())
|
|
311
|
+
self.saveBundleInfo(id: id, bundle: info)
|
|
312
|
+
return info
|
|
226
313
|
}
|
|
227
314
|
|
|
228
|
-
public func list() -> [
|
|
229
|
-
let dest =
|
|
315
|
+
public func list() -> [BundleInfo] {
|
|
316
|
+
let dest = documentsDir.appendingPathComponent(bundleDirectoryHot)
|
|
230
317
|
do {
|
|
231
318
|
let files = try FileManager.default.contentsOfDirectory(atPath: dest.path)
|
|
232
|
-
|
|
319
|
+
var res: [BundleInfo] = []
|
|
320
|
+
print("\(self.TAG) list File : \(dest.path)")
|
|
321
|
+
if (dest.exist) {
|
|
322
|
+
for id in files {
|
|
323
|
+
res.append(self.getBundleInfo(id: id));
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return res
|
|
233
327
|
} catch {
|
|
234
|
-
print("
|
|
328
|
+
print("\(self.TAG) No version available \(dest.path)")
|
|
235
329
|
return []
|
|
236
330
|
}
|
|
237
331
|
}
|
|
238
332
|
|
|
239
|
-
public func delete(
|
|
240
|
-
let
|
|
241
|
-
let
|
|
333
|
+
public func delete(id: String) -> Bool {
|
|
334
|
+
let deleted: BundleInfo = self.getBundleInfo(id: id)
|
|
335
|
+
let destHot = documentsDir.appendingPathComponent(bundleDirectoryHot).appendingPathComponent(id)
|
|
336
|
+
let destPersist = libraryDir.appendingPathComponent(bundleDirectory).appendingPathComponent(id)
|
|
242
337
|
do {
|
|
243
338
|
try FileManager.default.removeItem(atPath: destHot.path)
|
|
244
339
|
} catch {
|
|
245
|
-
print("
|
|
340
|
+
print("\(self.TAG) Hot Folder \(destHot.path), not removed.")
|
|
246
341
|
}
|
|
247
342
|
do {
|
|
248
343
|
try FileManager.default.removeItem(atPath: destPersist.path)
|
|
249
344
|
} catch {
|
|
250
|
-
print("
|
|
345
|
+
print("\(self.TAG) Folder \(destPersist.path), not removed.")
|
|
251
346
|
return false
|
|
252
347
|
}
|
|
253
|
-
|
|
348
|
+
self.removeBundleInfo(id: id)
|
|
349
|
+
self.sendStats(action: "delete", versionName: deleted.getVersionName())
|
|
254
350
|
return true
|
|
255
351
|
}
|
|
256
352
|
|
|
257
|
-
public func
|
|
258
|
-
|
|
353
|
+
public func getBundleDirectory(id: String) -> URL {
|
|
354
|
+
return libraryDir.appendingPathComponent(self.bundleDirectory).appendingPathComponent(id)
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
public func set(bundle: BundleInfo) -> Bool {
|
|
358
|
+
return self.set(id: bundle.getId());
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
private func bundleExists(id: String) -> Bool {
|
|
362
|
+
let destHot = self.getPathHot(id: id)
|
|
363
|
+
let destHotPersist = self.getPathPersist(id: id)
|
|
259
364
|
let indexHot = destHot.appendingPathComponent("index.html")
|
|
260
|
-
let destHotPersist = libraryUrl.appendingPathComponent(basePathPersist).appendingPathComponent(version)
|
|
261
365
|
let indexPersist = destHotPersist.appendingPathComponent("index.html")
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
366
|
+
let url: URL = self.getBundleDirectory(id: id)
|
|
367
|
+
if(url.isDirectory && destHotPersist.isDirectory && indexHot.exist && indexPersist.exist) {
|
|
368
|
+
return true;
|
|
369
|
+
}
|
|
370
|
+
return false;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
public func set(id: String) -> Bool {
|
|
374
|
+
let newBundle: BundleInfo = self.getBundleInfo(id: id)
|
|
375
|
+
if (bundleExists(id: id)) {
|
|
376
|
+
let url: URL = self.getBundleDirectory(id: id)
|
|
377
|
+
self.setCurrentBundle(bundle: String(url.path.suffix(10)))
|
|
378
|
+
self.setBundleStatus(id: id, status: BundleStatus.PENDING)
|
|
379
|
+
sendStats(action: "set", versionName: newBundle.getVersionName())
|
|
267
380
|
return true
|
|
268
381
|
}
|
|
269
|
-
sendStats(action: "set_fail",
|
|
382
|
+
sendStats(action: "set_fail", versionName: newBundle.getVersionName())
|
|
270
383
|
return false
|
|
271
384
|
}
|
|
272
385
|
|
|
273
|
-
public func
|
|
274
|
-
return
|
|
386
|
+
public func getPathHot(id: String) -> URL {
|
|
387
|
+
return documentsDir.appendingPathComponent(self.bundleDirectoryHot).appendingPathComponent(id)
|
|
275
388
|
}
|
|
276
389
|
|
|
277
|
-
public func
|
|
278
|
-
return
|
|
390
|
+
public func getPathPersist(id: String) -> URL {
|
|
391
|
+
return libraryDir.appendingPathComponent(self.bundleDirectory).appendingPathComponent(id)
|
|
279
392
|
}
|
|
280
393
|
|
|
281
|
-
public func
|
|
282
|
-
|
|
394
|
+
public func reset() {
|
|
395
|
+
self.reset(isInternal: false)
|
|
283
396
|
}
|
|
284
397
|
|
|
285
|
-
public func reset() {
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
UserDefaults.standard.set("", forKey: "lastPathPersist")
|
|
290
|
-
UserDefaults.standard.set("", forKey: "versionName")
|
|
398
|
+
public func reset(isInternal: Bool) {
|
|
399
|
+
self.setCurrentBundle(bundle: "")
|
|
400
|
+
self.setFallbackVersion(fallback: Optional<BundleInfo>.none)
|
|
401
|
+
let _ = self.setNextVersion(next: Optional<String>.none)
|
|
291
402
|
UserDefaults.standard.synchronize()
|
|
403
|
+
if(!isInternal) {
|
|
404
|
+
sendStats(action: "reset", versionName: self.getCurrentBundle().getVersionName())
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
public func commit(bundle: BundleInfo) {
|
|
409
|
+
self.setBundleStatus(id: bundle.getId(), status: BundleStatus.SUCCESS)
|
|
410
|
+
self.setFallbackVersion(fallback: bundle)
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
public func rollback(bundle: BundleInfo) {
|
|
414
|
+
self.setBundleStatus(id: bundle.getId(), status: BundleStatus.ERROR);
|
|
292
415
|
}
|
|
293
416
|
|
|
294
|
-
func sendStats(action: String,
|
|
417
|
+
func sendStats(action: String, versionName: String) {
|
|
295
418
|
if (statsUrl == "") { return }
|
|
296
419
|
let parameters: [String: String] = [
|
|
297
420
|
"platform": "ios",
|
|
298
421
|
"action": action,
|
|
299
422
|
"device_id": self.deviceID,
|
|
300
|
-
"version_name":
|
|
423
|
+
"version_name": versionName,
|
|
301
424
|
"version_build": self.versionBuild,
|
|
302
425
|
"version_code": self.versionCode,
|
|
303
426
|
"version_os": self.versionOs,
|
|
@@ -307,8 +430,122 @@ extension CustomError: LocalizedError {
|
|
|
307
430
|
|
|
308
431
|
DispatchQueue.global(qos: .background).async {
|
|
309
432
|
let _ = AF.request(self.statsUrl, method: .post,parameters: parameters, encoder: JSONParameterEncoder.default)
|
|
310
|
-
print("
|
|
433
|
+
print("\(self.TAG) Stats send for \(action), version \(versionName)")
|
|
311
434
|
}
|
|
312
435
|
}
|
|
313
|
-
|
|
436
|
+
|
|
437
|
+
public func getBundleInfo(id: String = BundleInfo.ID_BUILTIN) -> BundleInfo {
|
|
438
|
+
print("\(self.TAG) Getting info for bundle [\(id)]")
|
|
439
|
+
if(BundleInfo.ID_BUILTIN == id) {
|
|
440
|
+
return BundleInfo(id: id, version: "", status: BundleStatus.SUCCESS)
|
|
441
|
+
}
|
|
442
|
+
do {
|
|
443
|
+
let result: BundleInfo = try UserDefaults.standard.getObj(forKey: "\(id)\(self.INFO_SUFFIX)", castTo: BundleInfo.self)
|
|
444
|
+
print("\(self.TAG) Returning info bundle [\(id)]", result.toString())
|
|
445
|
+
return result
|
|
446
|
+
} catch {
|
|
447
|
+
print("\(self.TAG) Failed to parse info for bundle [\(id)]", error.localizedDescription)
|
|
448
|
+
return BundleInfo(id: id, version: "", status: BundleStatus.PENDING)
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
public func getBundleInfoByVersionName(version: String) -> BundleInfo? {
|
|
453
|
+
let installed : Array<BundleInfo> = self.list()
|
|
454
|
+
for i in installed {
|
|
455
|
+
if(i.getVersionName() == version) {
|
|
456
|
+
return i
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
return nil
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
private func removeBundleInfo(id: String) {
|
|
463
|
+
self.saveBundleInfo(id: id, bundle: nil)
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
private func saveBundleInfo(id: String, bundle: BundleInfo?) {
|
|
467
|
+
if (bundle != nil && (bundle!.isBuiltin() || bundle!.isUnknown())) {
|
|
468
|
+
print("\(self.TAG) Not saving info for bundle [\(id)]", bundle!.toString())
|
|
469
|
+
return
|
|
470
|
+
}
|
|
471
|
+
if(bundle == nil) {
|
|
472
|
+
print("\(self.TAG) Removing info for bundle [\(id)]")
|
|
473
|
+
UserDefaults.standard.removeObject(forKey: "\(id)\(self.INFO_SUFFIX)")
|
|
474
|
+
} else {
|
|
475
|
+
let update = bundle!.setId(id: id)
|
|
476
|
+
print("\(self.TAG) Storing info for bundle [\(id)]", update.toString())
|
|
477
|
+
do {
|
|
478
|
+
try UserDefaults.standard.setObj(update, forKey: "\(id)\(self.INFO_SUFFIX)")
|
|
479
|
+
} catch {
|
|
480
|
+
print("\(self.TAG) Failed to save info for bundle [\(id)]", error.localizedDescription)
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
UserDefaults.standard.synchronize()
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
public func setVersionName(id: String, version: String) {
|
|
487
|
+
print("\(self.TAG) Setting version for folder [\(id)] to \(version)")
|
|
488
|
+
let info = self.getBundleInfo(id: id)
|
|
489
|
+
self.saveBundleInfo(id: id, bundle: info.setVersionName(version: version))
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
private func setBundleStatus(id: String, status: BundleStatus) {
|
|
493
|
+
print("\(self.TAG) Setting status for bundle [\(id)] to \(status)")
|
|
494
|
+
let info = self.getBundleInfo(id: id)
|
|
495
|
+
self.saveBundleInfo(id: id, bundle: info.setStatus(status: status.localizedString))
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
private func getCurrentBundleVersion() -> String {
|
|
499
|
+
if(self.isUsingBuiltin()) {
|
|
500
|
+
return BundleInfo.ID_BUILTIN
|
|
501
|
+
} else {
|
|
502
|
+
let path: String = self.getCurrentBundleId()
|
|
503
|
+
return path.lastPathComponent
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
public func getCurrentBundle() -> BundleInfo {
|
|
508
|
+
return self.getBundleInfo(id: self.getCurrentBundleId());
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
public func getCurrentBundleId() -> String {
|
|
512
|
+
return UserDefaults.standard.string(forKey: self.CAP_SERVER_PATH) ?? self.DEFAULT_FOLDER
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
public func isUsingBuiltin() -> Bool {
|
|
516
|
+
return self.getCurrentBundleId() == self.DEFAULT_FOLDER
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
public func getFallbackVersion() -> BundleInfo {
|
|
520
|
+
let id: String = UserDefaults.standard.string(forKey: self.FALLBACK_VERSION) ?? BundleInfo.ID_BUILTIN
|
|
521
|
+
return self.getBundleInfo(id: id)
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
private func setFallbackVersion(fallback: BundleInfo?) {
|
|
525
|
+
UserDefaults.standard.set(fallback == nil ? BundleInfo.ID_BUILTIN : fallback!.getId(), forKey: self.FALLBACK_VERSION)
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
public func getNextVersion() -> BundleInfo? {
|
|
529
|
+
let id: String = UserDefaults.standard.string(forKey: self.NEXT_VERSION) ?? ""
|
|
530
|
+
if(id != "") {
|
|
531
|
+
return self.getBundleInfo(id: id)
|
|
532
|
+
} else {
|
|
533
|
+
return nil
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
public func setNextVersion(next: String?) -> Bool {
|
|
538
|
+
if (next == nil) {
|
|
539
|
+
UserDefaults.standard.removeObject(forKey: self.NEXT_VERSION)
|
|
540
|
+
} else {
|
|
541
|
+
let bundle: URL = self.getBundleDirectory(id: next!)
|
|
542
|
+
if (!bundle.exist) {
|
|
543
|
+
return false
|
|
544
|
+
}
|
|
545
|
+
UserDefaults.standard.set(next, forKey: self.NEXT_VERSION)
|
|
546
|
+
self.setBundleStatus(id: next!, status: BundleStatus.PENDING);
|
|
547
|
+
}
|
|
548
|
+
UserDefaults.standard.synchronize()
|
|
549
|
+
return true
|
|
550
|
+
}
|
|
314
551
|
}
|
|
@@ -11,9 +11,11 @@ CAP_PLUGIN(CapacitorUpdaterPlugin, "CapacitorUpdater",
|
|
|
11
11
|
CAP_PLUGIN_METHOD(reset, CAPPluginReturnPromise);
|
|
12
12
|
CAP_PLUGIN_METHOD(current, CAPPluginReturnPromise);
|
|
13
13
|
CAP_PLUGIN_METHOD(reload, CAPPluginReturnPromise);
|
|
14
|
-
CAP_PLUGIN_METHOD(versionName, CAPPluginReturnPromise);
|
|
15
14
|
CAP_PLUGIN_METHOD(notifyAppReady, CAPPluginReturnPromise);
|
|
16
|
-
CAP_PLUGIN_METHOD(
|
|
15
|
+
CAP_PLUGIN_METHOD(setDelay, CAPPluginReturnPromise);
|
|
16
|
+
CAP_PLUGIN_METHOD(getLatest, CAPPluginReturnPromise);
|
|
17
17
|
CAP_PLUGIN_METHOD(getId, CAPPluginReturnPromise);
|
|
18
18
|
CAP_PLUGIN_METHOD(getPluginVersion, CAPPluginReturnPromise);
|
|
19
|
+
CAP_PLUGIN_METHOD(next, CAPPluginReturnPromise);
|
|
20
|
+
CAP_PLUGIN_METHOD(isAutoUpdateEnabled, CAPPluginReturnPromise);
|
|
19
21
|
)
|