@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.
- package/README.md +207 -130
- 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 +403 -275
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +485 -268
- package/dist/docs.json +512 -109
- package/dist/esm/definitions.d.ts +201 -69
- package/dist/esm/web.d.ts +21 -16
- package/dist/esm/web.js +29 -23
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +29 -23
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +29 -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 +348 -84
- package/ios/Plugin/CapacitorUpdaterPlugin.m +4 -2
- package/ios/Plugin/CapacitorUpdaterPlugin.swift +277 -166
- package/ios/Plugin/ObjectPreferences.swift +97 -0
- package/package.json +2 -1
|
@@ -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
|
-
|
|
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
|
-
|
|
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("
|
|
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("
|
|
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("
|
|
93
|
-
|
|
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,
|
|
98
|
-
prepareFolder(source: base)
|
|
99
|
-
let destHot = base.appendingPathComponent(
|
|
100
|
-
let destUnZip =
|
|
101
|
-
SSZipArchive.unzipFile(atPath: sourceZip.path, toDestination: destUnZip.path)
|
|
102
|
-
|
|
103
|
-
|
|
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
|
-
|
|
206
|
+
public func getLatest(url: URL) -> AppVersion? {
|
|
108
207
|
let semaphore = DispatchSemaphore(value: 0)
|
|
109
208
|
let latest = AppVersion()
|
|
110
|
-
let
|
|
111
|
-
"
|
|
112
|
-
"
|
|
113
|
-
"
|
|
114
|
-
"
|
|
115
|
-
"
|
|
116
|
-
"
|
|
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,
|
|
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("
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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("
|
|
172
|
-
|
|
284
|
+
print("\(self.TAG) download error", error)
|
|
285
|
+
mainError = error as NSError
|
|
173
286
|
}
|
|
174
287
|
}
|
|
175
288
|
semaphore.signal()
|
|
176
289
|
}
|
|
177
|
-
self.
|
|
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
|
-
|
|
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
|
-
|
|
183
|
-
let dest =
|
|
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
|
-
|
|
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("
|
|
314
|
+
print("\(self.TAG) No version available \(dest.path)")
|
|
189
315
|
return []
|
|
190
316
|
}
|
|
191
317
|
}
|
|
192
318
|
|
|
193
|
-
|
|
194
|
-
let
|
|
195
|
-
let
|
|
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("
|
|
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("
|
|
331
|
+
print("\(self.TAG) Folder \(destPersist.path), not removed.")
|
|
205
332
|
return false
|
|
206
333
|
}
|
|
207
|
-
|
|
334
|
+
self.removeBundleInfo(id: id)
|
|
335
|
+
self.sendStats(action: "delete", bundle: deleted)
|
|
208
336
|
return true
|
|
209
337
|
}
|
|
210
338
|
|
|
211
|
-
|
|
212
|
-
|
|
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
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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",
|
|
361
|
+
sendStats(action: "set_fail", bundle: existing)
|
|
224
362
|
return false
|
|
225
363
|
}
|
|
226
364
|
|
|
227
|
-
|
|
228
|
-
return
|
|
365
|
+
public func getPathHot(id: String) -> URL {
|
|
366
|
+
return documentsDir.appendingPathComponent(self.bundleDirectoryHot).appendingPathComponent(id)
|
|
229
367
|
}
|
|
230
368
|
|
|
231
|
-
|
|
232
|
-
return
|
|
369
|
+
public func getPathPersist(id: String) -> URL {
|
|
370
|
+
return libraryDir.appendingPathComponent(self.bundleDirectory).appendingPathComponent(id)
|
|
233
371
|
}
|
|
234
372
|
|
|
235
|
-
|
|
236
|
-
|
|
373
|
+
public func reset() {
|
|
374
|
+
self.reset(isInternal: false)
|
|
237
375
|
}
|
|
238
376
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
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
|
-
|
|
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":
|
|
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("
|
|
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(
|
|
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
|
)
|