@capgo/capacitor-updater 3.0.10 → 3.2.1-alpha.0

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