@capgo/capacitor-updater 4.0.0-alpha.9 → 4.0.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.
@@ -1,6 +1,7 @@
1
1
  import Foundation
2
2
  import SSZipArchive
3
3
  import Alamofire
4
+ import zlib
4
5
 
5
6
  extension URL {
6
7
  var isDirectory: Bool {
@@ -47,10 +48,10 @@ extension OperatingSystemVersion {
47
48
  }
48
49
  }
49
50
  extension Bundle {
50
- var releaseVersionNumber: String? {
51
+ var versionName: String? {
51
52
  return infoDictionary?["CFBundleShortVersionString"] as? String
52
53
  }
53
- var buildVersionNumber: String? {
54
+ var versionCode: String? {
54
55
  return infoDictionary?["CFBundleVersion"] as? String
55
56
  }
56
57
  }
@@ -131,8 +132,8 @@ extension CustomError: LocalizedError {
131
132
 
132
133
  @objc public class CapacitorUpdater: NSObject {
133
134
 
134
- private let versionBuild = Bundle.main.releaseVersionNumber ?? ""
135
- private let versionCode = Bundle.main.buildVersionNumber ?? ""
135
+ private let versionName = Bundle.main.versionName ?? ""
136
+ private let versionCode = Bundle.main.versionCode ?? ""
136
137
  private let versionOs = ProcessInfo().operatingSystemVersion.getFullVersion()
137
138
  private let documentsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
138
139
  private let libraryDir = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first!
@@ -142,13 +143,10 @@ extension CustomError: LocalizedError {
142
143
  private let INFO_SUFFIX = "_info"
143
144
  private let FALLBACK_VERSION = "pastVersion"
144
145
  private let NEXT_VERSION = "nextVersion"
145
-
146
- private var lastPathHot = ""
147
- private var lastPathPersist = ""
148
146
 
149
147
  public let TAG = "✨ Capacitor-updater:";
150
148
  public let CAP_SERVER_PATH = "serverBasePath"
151
- public let pluginVersion = "4.0.0-alpha.9"
149
+ public let pluginVersion = "4.0.0"
152
150
  public var statsUrl = ""
153
151
  public var appId = ""
154
152
  public var deviceID = UIDevice.current.identifierForVendor?.uuidString ?? ""
@@ -204,8 +202,19 @@ extension CustomError: LocalizedError {
204
202
  throw CustomError.cannotUnflat
205
203
  }
206
204
  }
205
+
206
+ private func getChecksum(filePath: URL) -> String {
207
+ do {
208
+ let fileData = try Data.init(contentsOf: filePath)
209
+ let checksum = fileData.withUnsafeBytes { crc32(0, $0.bindMemory(to: Bytef.self).baseAddress, uInt(fileData.count)) }
210
+ return String(format:"%08X", checksum).lowercased()
211
+ } catch {
212
+ print("\(self.TAG) Cannot get checksum: \(filePath.path)", error)
213
+ return ""
214
+ }
215
+ }
207
216
 
208
- private func saveDownloaded(sourceZip: URL, id: String, base: URL) throws {
217
+ private func saveDownloaded(sourceZip: URL, id: String, base: URL) throws {
209
218
  try prepareFolder(source: base)
210
219
  let destHot = base.appendingPathComponent(id)
211
220
  let destUnZip = documentsDir.appendingPathComponent(randomString(length: 10))
@@ -217,19 +226,20 @@ extension CustomError: LocalizedError {
217
226
  }
218
227
  }
219
228
 
220
- public func getLatest(url: URL) -> AppVersion? {
229
+ public func getLatest(url: URL) -> AppVersion {
221
230
  let semaphore = DispatchSemaphore(value: 0)
222
231
  let latest = AppVersion()
223
232
  let parameters: [String: String] = [
224
233
  "platform": "ios",
225
234
  "device_id": self.deviceID,
226
235
  "app_id": self.appId,
227
- "version_build": self.versionBuild,
236
+ "version_build": self.versionName,
228
237
  "version_code": self.versionCode,
229
238
  "version_os": self.versionOs,
230
239
  "plugin_version": self.pluginVersion,
231
240
  "version_name": self.getCurrentBundle().getVersionName()
232
241
  ]
242
+ print("\(self.TAG) Auto-update parameters: \(parameters)")
233
243
  let request = AF.request(url, method: .post,parameters: parameters, encoder: JSONParameterEncoder.default)
234
244
 
235
245
  request.validate().responseDecodable(of: AppVersionDec.self) { response in
@@ -246,7 +256,6 @@ extension CustomError: LocalizedError {
246
256
  }
247
257
  if let message = response.value?.message {
248
258
  latest.message = message
249
- print("\(self.TAG) Auto-update message: \(message)")
250
259
  }
251
260
  case let .failure(error):
252
261
  print("\(self.TAG) Error getting Latest", error )
@@ -254,18 +263,19 @@ extension CustomError: LocalizedError {
254
263
  semaphore.signal()
255
264
  }
256
265
  semaphore.wait()
257
- return latest.url != "" ? latest : nil
266
+ return latest
258
267
  }
259
268
 
260
269
  private func setCurrentBundle(bundle: String) {
261
270
  UserDefaults.standard.set(bundle, forKey: self.CAP_SERVER_PATH)
262
- print("\(self.TAG) Current bundle set to: \(bundle)")
263
271
  UserDefaults.standard.synchronize()
272
+ print("\(self.TAG) Current bundle set to: \(bundle == "" ? BundleInfo.ID_BUILTIN : bundle)")
264
273
  }
265
274
 
266
275
  public func download(url: URL, version: String) throws -> BundleInfo {
267
276
  let semaphore = DispatchSemaphore(value: 0)
268
277
  let id: String = self.randomString(length: 10)
278
+ var checksum = ""
269
279
  var mainError: NSError? = nil
270
280
  let destination: DownloadRequest.Destination = { _, _ in
271
281
  let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
@@ -285,6 +295,7 @@ extension CustomError: LocalizedError {
285
295
  case .success:
286
296
  self.notifyDownload(id, 71)
287
297
  do {
298
+ checksum = self.getChecksum(filePath: fileURL)
288
299
  try self.saveDownloaded(sourceZip: fileURL, id: id, base: self.documentsDir.appendingPathComponent(self.bundleDirectoryHot))
289
300
  self.notifyDownload(id, 85)
290
301
  try self.saveDownloaded(sourceZip: fileURL, id: id, base: self.libraryDir.appendingPathComponent(self.bundleDirectory))
@@ -301,13 +312,13 @@ extension CustomError: LocalizedError {
301
312
  }
302
313
  semaphore.signal()
303
314
  }
304
- self.saveBundleInfo(id: id, bundle: BundleInfo(id: id, version: version, status: BundleStatus.DOWNLOADING, downloaded: Date()))
315
+ self.saveBundleInfo(id: id, bundle: BundleInfo(id: id, version: version, status: BundleStatus.DOWNLOADING, downloaded: Date(), checksum: checksum))
305
316
  self.notifyDownload(id, 0)
306
317
  semaphore.wait()
307
318
  if (mainError != nil) {
308
319
  throw mainError!
309
320
  }
310
- let info: BundleInfo = BundleInfo(id: id, version: version, status: BundleStatus.PENDING, downloaded: Date())
321
+ let info: BundleInfo = BundleInfo(id: id, version: version, status: BundleStatus.PENDING, downloaded: Date(), checksum: checksum)
311
322
  self.saveBundleInfo(id: id, bundle: info)
312
323
  return info
313
324
  }
@@ -330,8 +341,12 @@ extension CustomError: LocalizedError {
330
341
  }
331
342
  }
332
343
 
333
- public func delete(id: String) -> Bool {
344
+ public func delete(id: String, removeInfo: Bool) -> Bool {
334
345
  let deleted: BundleInfo = self.getBundleInfo(id: id)
346
+ if (deleted.isBuiltin() || self.getCurrentBundleId() == id) {
347
+ print("\(self.TAG) Cannot delete \(id)")
348
+ return false
349
+ }
335
350
  let destHot = documentsDir.appendingPathComponent(bundleDirectoryHot).appendingPathComponent(id)
336
351
  let destPersist = libraryDir.appendingPathComponent(bundleDirectory).appendingPathComponent(id)
337
352
  do {
@@ -345,11 +360,18 @@ extension CustomError: LocalizedError {
345
360
  print("\(self.TAG) Folder \(destPersist.path), not removed.")
346
361
  return false
347
362
  }
363
+ if (removeInfo) {
364
+ self.removeBundleInfo(id: id)
365
+ }
348
366
  self.removeBundleInfo(id: id)
349
- self.sendStats(action: "delete", bundle: deleted)
367
+ self.sendStats(action: "delete", versionName: deleted.getVersionName())
350
368
  return true
351
369
  }
352
370
 
371
+ public func delete(id: String) -> Bool {
372
+ return self.delete(id: id, removeInfo: true)
373
+ }
374
+
353
375
  public func getBundleDirectory(id: String) -> URL {
354
376
  return libraryDir.appendingPathComponent(self.bundleDirectory).appendingPathComponent(id)
355
377
  }
@@ -358,21 +380,31 @@ extension CustomError: LocalizedError {
358
380
  return self.set(id: bundle.getId());
359
381
  }
360
382
 
361
- public func set(id: String) -> Bool {
383
+ private func bundleExists(id: String) -> Bool {
362
384
  let destHot = self.getPathHot(id: id)
363
385
  let destHotPersist = self.getPathPersist(id: id)
364
386
  let indexHot = destHot.appendingPathComponent("index.html")
365
387
  let indexPersist = destHotPersist.appendingPathComponent("index.html")
366
- let existing: BundleInfo = self.getBundleInfo(id: id)
367
- let bundle: URL = self.getBundleDirectory(id: id)
368
- print("bundle", bundle.path)
369
- if (bundle.isDirectory && destHotPersist.isDirectory && indexHot.exist && indexPersist.exist) {
370
- self.setCurrentBundle(bundle: String(bundle.path.suffix(10)))
388
+ let url: URL = self.getBundleDirectory(id: id)
389
+ if(url.isDirectory && destHotPersist.isDirectory && indexHot.exist && indexPersist.exist) {
390
+ return true;
391
+ }
392
+ return false;
393
+ }
394
+
395
+ public func set(id: String) -> Bool {
396
+ let newBundle: BundleInfo = self.getBundleInfo(id: id)
397
+ if(newBundle.isBuiltin()) {
398
+ self.reset()
399
+ return true
400
+ }
401
+ if (bundleExists(id: id)) {
402
+ self.setCurrentBundle(bundle: self.getBundleDirectory(id: id).path)
371
403
  self.setBundleStatus(id: id, status: BundleStatus.PENDING)
372
- sendStats(action: "set", bundle: existing)
404
+ sendStats(action: "set", versionName: newBundle.getVersionName())
373
405
  return true
374
406
  }
375
- sendStats(action: "set_fail", bundle: existing)
407
+ sendStats(action: "set_fail", versionName: newBundle.getVersionName())
376
408
  return false
377
409
  }
378
410
 
@@ -389,32 +421,43 @@ extension CustomError: LocalizedError {
389
421
  }
390
422
 
391
423
  public func reset(isInternal: Bool) {
424
+ print("\(self.TAG) reset: \(isInternal)")
392
425
  self.setCurrentBundle(bundle: "")
393
- self.setFallbackVersion(fallback: Optional<BundleInfo>.none)
394
- let _ = self.setNextVersion(next: Optional<String>.none)
395
- UserDefaults.standard.synchronize()
426
+ self.setFallbackBundle(fallback: Optional<BundleInfo>.none)
427
+ let _ = self.setNextBundle(next: Optional<String>.none)
396
428
  if(!isInternal) {
397
- sendStats(action: "reset", bundle: self.getCurrentBundle())
429
+ sendStats(action: "reset", versionName: self.getCurrentBundle().getVersionName())
398
430
  }
399
431
  }
400
432
 
401
- public func commit(bundle: BundleInfo) {
433
+ public func setSuccess(bundle: BundleInfo, autoDeletePrevious: Bool) {
402
434
  self.setBundleStatus(id: bundle.getId(), status: BundleStatus.SUCCESS)
403
- self.setFallbackVersion(fallback: bundle)
435
+ let fallback: BundleInfo = self.getFallbackBundle()
436
+ print("\(self.TAG) Fallback bundle is: \(fallback.toString())")
437
+ print("\(self.TAG) Version successfully loaded: \(bundle.toString())")
438
+ if(autoDeletePrevious && !fallback.isBuiltin()) {
439
+ let res = self.delete(id: fallback.getId())
440
+ if (res) {
441
+ print("\(self.TAG) Deleted previous bundle: \(fallback.toString())")
442
+ } else {
443
+ print("\(self.TAG) Failed to delete previous bundle: \(fallback.toString())")
444
+ }
445
+ }
446
+ self.setFallbackBundle(fallback: bundle)
404
447
  }
405
448
 
406
- public func rollback(bundle: BundleInfo) {
449
+ public func setError(bundle: BundleInfo) {
407
450
  self.setBundleStatus(id: bundle.getId(), status: BundleStatus.ERROR);
408
451
  }
409
452
 
410
- func sendStats(action: String, bundle: BundleInfo) {
453
+ func sendStats(action: String, versionName: String) {
411
454
  if (statsUrl == "") { return }
412
455
  let parameters: [String: String] = [
413
456
  "platform": "ios",
414
457
  "action": action,
415
458
  "device_id": self.deviceID,
416
- "version_name": bundle.getVersionName(),
417
- "version_build": self.versionBuild,
459
+ "version_name": versionName,
460
+ "version_build": self.versionName,
418
461
  "version_code": self.versionCode,
419
462
  "version_os": self.versionOs,
420
463
  "plugin_version": self.pluginVersion,
@@ -423,23 +466,31 @@ extension CustomError: LocalizedError {
423
466
 
424
467
  DispatchQueue.global(qos: .background).async {
425
468
  let _ = AF.request(self.statsUrl, method: .post,parameters: parameters, encoder: JSONParameterEncoder.default)
426
- print("\(self.TAG) Stats send for \(action), version \(bundle.getVersionName())")
469
+ print("\(self.TAG) Stats send for \(action), version \(versionName)")
427
470
  }
428
471
  }
429
472
 
430
- public func getBundleInfo(id: String = BundleInfo.ID_BUILTIN) -> BundleInfo {
431
- print("\(self.TAG) Getting info for bundle [\(id)]")
432
- if(BundleInfo.ID_BUILTIN == id) {
433
- return BundleInfo(id: id, version: "", status: BundleStatus.SUCCESS)
473
+ public func getBundleInfo(id: String?) -> BundleInfo {
474
+ var trueId = BundleInfo.VERSION_UNKNOWN
475
+ if(id != nil) {
476
+ trueId = id!
434
477
  }
435
- do {
436
- let result: BundleInfo = try UserDefaults.standard.getObj(forKey: "\(id)\(self.INFO_SUFFIX)", castTo: BundleInfo.self)
437
- print("\(self.TAG) Returning info bundle [\(id)]", result.toString())
438
- return result
439
- } catch {
440
- print("\(self.TAG) Failed to parse info for bundle [\(id)]", error.localizedDescription)
441
- return BundleInfo(id: id, version: "", status: BundleStatus.PENDING)
478
+ print("\(self.TAG) Getting info for bundle [\(trueId)]")
479
+ let result: BundleInfo;
480
+ if(BundleInfo.ID_BUILTIN == trueId) {
481
+ result = BundleInfo(id: trueId, version: "", status: BundleStatus.SUCCESS, checksum: "")
482
+ } else if (BundleInfo.VERSION_UNKNOWN == trueId) {
483
+ result = BundleInfo(id: trueId, version: "", status: BundleStatus.ERROR, checksum: "")
484
+ } else {
485
+ do {
486
+ result = try UserDefaults.standard.getObj(forKey: "\(trueId)\(self.INFO_SUFFIX)", castTo: BundleInfo.self)
487
+ } catch {
488
+ print("\(self.TAG) Failed to parse info for bundle [\(trueId)]", error.localizedDescription)
489
+ result = BundleInfo(id: trueId, version: "", status: BundleStatus.PENDING, checksum: "")
490
+ }
442
491
  }
492
+ print("\(self.TAG) Returning info bundle [\(result.toString())]")
493
+ return result;
443
494
  }
444
495
 
445
496
  public func getBundleInfoByVersionName(version: String) -> BundleInfo? {
@@ -488,57 +539,54 @@ extension CustomError: LocalizedError {
488
539
  self.saveBundleInfo(id: id, bundle: info.setStatus(status: status.localizedString))
489
540
  }
490
541
 
491
- private func getCurrentBundleVersion() -> String {
492
- if(self.isUsingBuiltin()) {
493
- return BundleInfo.ID_BUILTIN
494
- } else {
495
- let path: String = self.getCurrentBundleId()
496
- return path.lastPathComponent
497
- }
498
- }
499
-
500
542
  public func getCurrentBundle() -> BundleInfo {
501
543
  return self.getBundleInfo(id: self.getCurrentBundleId());
502
544
  }
503
545
 
504
546
  public func getCurrentBundleId() -> String {
505
- return UserDefaults.standard.string(forKey: self.CAP_SERVER_PATH) ?? self.DEFAULT_FOLDER
547
+ guard let bundlePath = UserDefaults.standard.string(forKey: self.CAP_SERVER_PATH) else {
548
+ return BundleInfo.ID_BUILTIN
549
+ }
550
+ if (bundlePath == "") {
551
+ return BundleInfo.ID_BUILTIN
552
+ }
553
+ let bundleID = bundlePath.components(separatedBy: "/").last ?? bundlePath
554
+ return bundleID
506
555
  }
507
556
 
508
557
  public func isUsingBuiltin() -> Bool {
509
- return self.getCurrentBundleId() == self.DEFAULT_FOLDER
558
+ return (UserDefaults.standard.string(forKey: self.CAP_SERVER_PATH) ?? "") == self.DEFAULT_FOLDER
510
559
  }
511
560
 
512
- public func getFallbackVersion() -> BundleInfo {
561
+ public func getFallbackBundle() -> BundleInfo {
513
562
  let id: String = UserDefaults.standard.string(forKey: self.FALLBACK_VERSION) ?? BundleInfo.ID_BUILTIN
514
563
  return self.getBundleInfo(id: id)
515
564
  }
516
565
 
517
- private func setFallbackVersion(fallback: BundleInfo?) {
566
+ private func setFallbackBundle(fallback: BundleInfo?) {
518
567
  UserDefaults.standard.set(fallback == nil ? BundleInfo.ID_BUILTIN : fallback!.getId(), forKey: self.FALLBACK_VERSION)
568
+ UserDefaults.standard.synchronize()
519
569
  }
520
570
 
521
- public func getNextVersion() -> BundleInfo? {
522
- let id: String = UserDefaults.standard.string(forKey: self.NEXT_VERSION) ?? ""
523
- if(id != "") {
524
- return self.getBundleInfo(id: id)
525
- } else {
526
- return nil
527
- }
571
+ public func getNextBundle() -> BundleInfo? {
572
+ let id: String? = UserDefaults.standard.string(forKey: self.NEXT_VERSION)
573
+ return self.getBundleInfo(id: id)
528
574
  }
529
575
 
530
- public func setNextVersion(next: String?) -> Bool {
531
- if (next == nil) {
576
+ public func setNextBundle(next: String?) -> Bool {
577
+ guard let nextId = next else {
532
578
  UserDefaults.standard.removeObject(forKey: self.NEXT_VERSION)
533
- } else {
534
- let bundle: URL = self.getBundleDirectory(id: next!)
535
- if (!bundle.exist) {
536
- return false
537
- }
538
- UserDefaults.standard.set(next, forKey: self.NEXT_VERSION)
539
- self.setBundleStatus(id: next!, status: BundleStatus.PENDING);
579
+ UserDefaults.standard.synchronize()
580
+ return false
581
+ }
582
+ let newBundle: BundleInfo = self.getBundleInfo(id: nextId)
583
+ let bundle: URL = self.getBundleDirectory(id: nextId)
584
+ if (!newBundle.isBuiltin() && !bundle.exist) {
585
+ return false
540
586
  }
587
+ UserDefaults.standard.set(nextId, forKey: self.NEXT_VERSION)
541
588
  UserDefaults.standard.synchronize()
589
+ self.setBundleStatus(id: nextId, status: BundleStatus.PENDING)
542
590
  return true
543
591
  }
544
592
  }
@@ -13,8 +13,9 @@ CAP_PLUGIN(CapacitorUpdaterPlugin, "CapacitorUpdater",
13
13
  CAP_PLUGIN_METHOD(reload, CAPPluginReturnPromise);
14
14
  CAP_PLUGIN_METHOD(notifyAppReady, CAPPluginReturnPromise);
15
15
  CAP_PLUGIN_METHOD(setDelay, CAPPluginReturnPromise);
16
+ CAP_PLUGIN_METHOD(cancelDelay, CAPPluginReturnPromise);
16
17
  CAP_PLUGIN_METHOD(getLatest, CAPPluginReturnPromise);
17
- CAP_PLUGIN_METHOD(getId, CAPPluginReturnPromise);
18
+ CAP_PLUGIN_METHOD(getDeviceId, CAPPluginReturnPromise);
18
19
  CAP_PLUGIN_METHOD(getPluginVersion, CAPPluginReturnPromise);
19
20
  CAP_PLUGIN_METHOD(next, CAPPluginReturnPromise);
20
21
  CAP_PLUGIN_METHOD(isAutoUpdateEnabled, CAPPluginReturnPromise);