@capawesome/cordova-live-update 0.1.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.
Files changed (103) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1113 -0
  3. package/dist/docs.json +1654 -0
  4. package/dist/esm/definitions.d.ts +788 -0
  5. package/dist/esm/definitions.js +7 -0
  6. package/dist/esm/definitions.js.map +1 -0
  7. package/dist/esm/exec.d.ts +1 -0
  8. package/dist/esm/exec.js +8 -0
  9. package/dist/esm/exec.js.map +1 -0
  10. package/dist/esm/index.d.ts +4 -0
  11. package/dist/esm/index.js +46 -0
  12. package/dist/esm/index.js.map +1 -0
  13. package/dist/plugin.js +56 -0
  14. package/dist/plugin.js.map +1 -0
  15. package/package.json +93 -0
  16. package/plugin.xml +268 -0
  17. package/src/android/capawesome-cordova-live-update.gradle +12 -0
  18. package/src/android/capawesome-live-update.xml +5 -0
  19. package/src/android/io/capawesome/cordova/plugins/liveupdate/LiveUpdate.java +1480 -0
  20. package/src/android/io/capawesome/cordova/plugins/liveupdate/LiveUpdateConfig.java +105 -0
  21. package/src/android/io/capawesome/cordova/plugins/liveupdate/LiveUpdateHttpClient.java +114 -0
  22. package/src/android/io/capawesome/cordova/plugins/liveupdate/LiveUpdatePathHandler.java +96 -0
  23. package/src/android/io/capawesome/cordova/plugins/liveupdate/LiveUpdatePlugin.java +550 -0
  24. package/src/android/io/capawesome/cordova/plugins/liveupdate/LiveUpdatePreferences.java +151 -0
  25. package/src/android/io/capawesome/cordova/plugins/liveupdate/classes/Manifest.java +58 -0
  26. package/src/android/io/capawesome/cordova/plugins/liveupdate/classes/ManifestItem.java +37 -0
  27. package/src/android/io/capawesome/cordova/plugins/liveupdate/classes/api/GetChannelsResponseItem.java +28 -0
  28. package/src/android/io/capawesome/cordova/plugins/liveupdate/classes/api/GetLatestBundleResponse.java +74 -0
  29. package/src/android/io/capawesome/cordova/plugins/liveupdate/classes/events/DownloadBundleProgressEvent.java +33 -0
  30. package/src/android/io/capawesome/cordova/plugins/liveupdate/classes/events/NextBundleSetEvent.java +26 -0
  31. package/src/android/io/capawesome/cordova/plugins/liveupdate/classes/options/DeleteBundleOptions.java +18 -0
  32. package/src/android/io/capawesome/cordova/plugins/liveupdate/classes/options/DownloadBundleOptions.java +66 -0
  33. package/src/android/io/capawesome/cordova/plugins/liveupdate/classes/options/FetchChannelsOptions.java +39 -0
  34. package/src/android/io/capawesome/cordova/plugins/liveupdate/classes/options/FetchLatestBundleOptions.java +25 -0
  35. package/src/android/io/capawesome/cordova/plugins/liveupdate/classes/options/SetChannelOptions.java +18 -0
  36. package/src/android/io/capawesome/cordova/plugins/liveupdate/classes/options/SetConfigOptions.java +20 -0
  37. package/src/android/io/capawesome/cordova/plugins/liveupdate/classes/options/SetCustomIdOptions.java +18 -0
  38. package/src/android/io/capawesome/cordova/plugins/liveupdate/classes/options/SetNextBundleOptions.java +21 -0
  39. package/src/android/io/capawesome/cordova/plugins/liveupdate/classes/options/SyncOptions.java +25 -0
  40. package/src/android/io/capawesome/cordova/plugins/liveupdate/classes/results/ChannelResult.java +29 -0
  41. package/src/android/io/capawesome/cordova/plugins/liveupdate/classes/results/FetchChannelsResult.java +29 -0
  42. package/src/android/io/capawesome/cordova/plugins/liveupdate/classes/results/FetchLatestBundleResult.java +69 -0
  43. package/src/android/io/capawesome/cordova/plugins/liveupdate/classes/results/GetBlockedBundlesResult.java +28 -0
  44. package/src/android/io/capawesome/cordova/plugins/liveupdate/classes/results/GetBundlesResult.java +28 -0
  45. package/src/android/io/capawesome/cordova/plugins/liveupdate/classes/results/GetChannelResult.java +22 -0
  46. package/src/android/io/capawesome/cordova/plugins/liveupdate/classes/results/GetConfigResult.java +40 -0
  47. package/src/android/io/capawesome/cordova/plugins/liveupdate/classes/results/GetCurrentBundleResult.java +22 -0
  48. package/src/android/io/capawesome/cordova/plugins/liveupdate/classes/results/GetCustomIdResult.java +22 -0
  49. package/src/android/io/capawesome/cordova/plugins/liveupdate/classes/results/GetDeviceIdResult.java +22 -0
  50. package/src/android/io/capawesome/cordova/plugins/liveupdate/classes/results/GetDownloadedBundlesResult.java +28 -0
  51. package/src/android/io/capawesome/cordova/plugins/liveupdate/classes/results/GetNextBundleResult.java +22 -0
  52. package/src/android/io/capawesome/cordova/plugins/liveupdate/classes/results/GetVersionCodeResult.java +21 -0
  53. package/src/android/io/capawesome/cordova/plugins/liveupdate/classes/results/GetVersionNameResult.java +22 -0
  54. package/src/android/io/capawesome/cordova/plugins/liveupdate/classes/results/IsSyncingResult.java +27 -0
  55. package/src/android/io/capawesome/cordova/plugins/liveupdate/classes/results/ReadyResult.java +32 -0
  56. package/src/android/io/capawesome/cordova/plugins/liveupdate/classes/results/SyncResult.java +22 -0
  57. package/src/android/io/capawesome/cordova/plugins/liveupdate/enums/ArtifactType.java +6 -0
  58. package/src/android/io/capawesome/cordova/plugins/liveupdate/interfaces/Callback.java +5 -0
  59. package/src/android/io/capawesome/cordova/plugins/liveupdate/interfaces/DownloadProgressCallback.java +5 -0
  60. package/src/android/io/capawesome/cordova/plugins/liveupdate/interfaces/EmptyCallback.java +5 -0
  61. package/src/android/io/capawesome/cordova/plugins/liveupdate/interfaces/NonEmptyCallback.java +7 -0
  62. package/src/android/io/capawesome/cordova/plugins/liveupdate/interfaces/Result.java +8 -0
  63. package/src/ios/LiveUpdate.swift +895 -0
  64. package/src/ios/LiveUpdateArtifactType.swift +4 -0
  65. package/src/ios/LiveUpdateChannelResult.swift +18 -0
  66. package/src/ios/LiveUpdateConfig.swift +11 -0
  67. package/src/ios/LiveUpdateDeleteBundleOptions.swift +13 -0
  68. package/src/ios/LiveUpdateDownloadBundleOptions.swift +41 -0
  69. package/src/ios/LiveUpdateDownloadBundleProgressEvent.swift +24 -0
  70. package/src/ios/LiveUpdateError.swift +62 -0
  71. package/src/ios/LiveUpdateFetchChannelsOptions.swift +25 -0
  72. package/src/ios/LiveUpdateFetchChannelsResult.swift +15 -0
  73. package/src/ios/LiveUpdateFetchLatestBundleOptions.swift +17 -0
  74. package/src/ios/LiveUpdateFetchLatestBundleResult.swift +42 -0
  75. package/src/ios/LiveUpdateGetBlockedBundlesResult.swift +15 -0
  76. package/src/ios/LiveUpdateGetBundlesResult.swift +15 -0
  77. package/src/ios/LiveUpdateGetChannelResult.swift +15 -0
  78. package/src/ios/LiveUpdateGetChannelsResponseItem.swift +4 -0
  79. package/src/ios/LiveUpdateGetConfigResult.swift +18 -0
  80. package/src/ios/LiveUpdateGetCurrentBundleResult.swift +15 -0
  81. package/src/ios/LiveUpdateGetCustomIdResult.swift +15 -0
  82. package/src/ios/LiveUpdateGetDeviceIdResult.swift +15 -0
  83. package/src/ios/LiveUpdateGetDownloadedBundlesResult.swift +15 -0
  84. package/src/ios/LiveUpdateGetLatestBundleResponse.swift +8 -0
  85. package/src/ios/LiveUpdateGetNextBundleResult.swift +15 -0
  86. package/src/ios/LiveUpdateGetVersionCodeResult.swift +15 -0
  87. package/src/ios/LiveUpdateGetVersionNameResult.swift +15 -0
  88. package/src/ios/LiveUpdateHttpClient.swift +58 -0
  89. package/src/ios/LiveUpdateIsSyncingResult.swift +15 -0
  90. package/src/ios/LiveUpdateManifest.swift +19 -0
  91. package/src/ios/LiveUpdateManifestItem.swift +5 -0
  92. package/src/ios/LiveUpdateNextBundleSetEvent.swift +15 -0
  93. package/src/ios/LiveUpdatePlugin.swift +521 -0
  94. package/src/ios/LiveUpdatePreferences.swift +116 -0
  95. package/src/ios/LiveUpdateReadyResult.swift +21 -0
  96. package/src/ios/LiveUpdateResult.swift +5 -0
  97. package/src/ios/LiveUpdateSchemeHandler.swift +286 -0
  98. package/src/ios/LiveUpdateSetChannelOptions.swift +13 -0
  99. package/src/ios/LiveUpdateSetConfigOptions.swift +13 -0
  100. package/src/ios/LiveUpdateSetCustomIdOptions.swift +13 -0
  101. package/src/ios/LiveUpdateSetNextBundleOptions.swift +13 -0
  102. package/src/ios/LiveUpdateSyncOptions.swift +17 -0
  103. package/src/ios/LiveUpdateSyncResult.swift +15 -0
@@ -0,0 +1,521 @@
1
+ import Cordova
2
+ import Foundation
3
+ import UIKit
4
+ import WebKit
5
+
6
+ @objc(CapawesomeLiveUpdatePlugin)
7
+ public class LiveUpdatePlugin: CDVPlugin, CDVPluginSchemeHandler {
8
+ public static let tag = "LiveUpdate"
9
+ public static let version = "0.1.0"
10
+ public static let userDefaultsPrefix = "CapawesomeLiveUpdate" // DO NOT CHANGE
11
+
12
+ private let eventDownloadBundleProgress = "downloadBundleProgress"
13
+ private let eventNextBundleSet = "nextBundleSet"
14
+
15
+ private static let errorAppIdMissing = "appId must be configured."
16
+ private static let errorBundleIdMissing = "bundleId must be provided."
17
+ private static let errorCustomIdMissing = "customId must be provided."
18
+ private static let errorUrlMissing = "url must be provided."
19
+ private static let errorListenerIdMissing = "listenerId must be provided."
20
+ private static let errorPluginNotInitialized = "LiveUpdate plugin failed to initialize."
21
+ private static let errorHttpTimeout = "Request timed out."
22
+
23
+ private var config: LiveUpdateConfig?
24
+ private var implementation: LiveUpdate?
25
+ private var schemeHandler: LiveUpdateSchemeHandler?
26
+
27
+ private struct ListenerRegistration {
28
+ let eventName: String
29
+ let callbackId: String
30
+ }
31
+
32
+ private let listenersLock = NSLock()
33
+ private var listeners: [String: ListenerRegistration] = [:]
34
+
35
+ public override func pluginInitialize() {
36
+ super.pluginInitialize()
37
+ let cfg = loadConfig()
38
+ let handler = LiveUpdateSchemeHandler()
39
+ let impl = LiveUpdate(config: cfg, plugin: self, schemeHandler: handler)
40
+ self.config = cfg
41
+ self.schemeHandler = handler
42
+ self.implementation = impl
43
+ impl.handleLoad()
44
+
45
+ NotificationCenter.default.addObserver(
46
+ self,
47
+ selector: #selector(handleAppWillEnterForeground),
48
+ name: UIApplication.willEnterForegroundNotification,
49
+ object: nil
50
+ )
51
+ }
52
+
53
+ // MARK: - CDVPluginSchemeHandler conformance
54
+
55
+ public func overrideSchemeTask(_ task: WKURLSchemeTask) -> Bool {
56
+ return schemeHandler?.handle(task: task) ?? false
57
+ }
58
+
59
+ public func stop(_ task: WKURLSchemeTask) {
60
+ schemeHandler?.stop(task: task)
61
+ }
62
+
63
+ // MARK: - Plugin → JS notify methods (called by LiveUpdate)
64
+
65
+ func notifyDownloadBundleProgressListeners(_ event: LiveUpdateDownloadBundleProgressEvent) {
66
+ notifyJSListeners(eventName: eventDownloadBundleProgress, data: event.toJSObject())
67
+ }
68
+
69
+ func notifyNextBundleSetListeners(_ event: LiveUpdateNextBundleSetEvent) {
70
+ notifyJSListeners(eventName: eventNextBundleSet, data: event.toJSObject())
71
+ }
72
+
73
+ public func reloadWebView() {
74
+ DispatchQueue.main.async { [weak self] in
75
+ guard let webView = self?.webViewEngine?.engineWebView as? WKWebView else {
76
+ return
77
+ }
78
+ webView.reload()
79
+ }
80
+ }
81
+
82
+ @objc private func handleAppWillEnterForeground() {
83
+ implementation?.handleAppWillEnterForeground()
84
+ }
85
+
86
+ // MARK: - Action methods
87
+
88
+ @objc(clearBlockedBundles:)
89
+ func clearBlockedBundles(_ command: CDVInvokedUrlCommand) {
90
+ guard let impl = implementation else {
91
+ reject(command, message: Self.errorPluginNotInitialized)
92
+ return
93
+ }
94
+ impl.clearBlockedBundles()
95
+ resolve(command)
96
+ }
97
+
98
+ @objc(deleteBundle:)
99
+ func deleteBundle(_ command: CDVInvokedUrlCommand) {
100
+ guard let impl = implementation else {
101
+ reject(command, message: Self.errorPluginNotInitialized)
102
+ return
103
+ }
104
+ let options = optionsDict(from: command)
105
+ guard let bundleId = string(options, "bundleId") else {
106
+ reject(command, message: Self.errorBundleIdMissing)
107
+ return
108
+ }
109
+ impl.deleteBundle(LiveUpdateDeleteBundleOptions(bundleId: bundleId)) { [weak self] error in
110
+ self?.completeEmpty(command, error: error)
111
+ }
112
+ }
113
+
114
+ @objc(downloadBundle:)
115
+ func downloadBundle(_ command: CDVInvokedUrlCommand) {
116
+ guard let impl = implementation else {
117
+ reject(command, message: Self.errorPluginNotInitialized)
118
+ return
119
+ }
120
+ let options = optionsDict(from: command)
121
+ let artifactType = (options["artifactType"] as? String) ?? "zip"
122
+ guard let bundleId = string(options, "bundleId") else {
123
+ reject(command, message: Self.errorBundleIdMissing)
124
+ return
125
+ }
126
+ let checksum = string(options, "checksum")
127
+ let signature = string(options, "signature")
128
+ guard let url = string(options, "url") else {
129
+ reject(command, message: Self.errorUrlMissing)
130
+ return
131
+ }
132
+ Task {
133
+ do {
134
+ try await impl.downloadBundle(LiveUpdateDownloadBundleOptions(
135
+ artifactType: artifactType,
136
+ bundleId: bundleId,
137
+ checksum: checksum,
138
+ signature: signature,
139
+ url: url
140
+ ))
141
+ resolve(command)
142
+ } catch {
143
+ reject(command, error: error)
144
+ }
145
+ }
146
+ }
147
+
148
+ @objc(fetchChannels:)
149
+ func fetchChannels(_ command: CDVInvokedUrlCommand) {
150
+ guard let impl = implementation, let cfg = config else {
151
+ reject(command, message: Self.errorPluginNotInitialized)
152
+ return
153
+ }
154
+ guard let appId = cfg.appId, !appId.isEmpty else {
155
+ reject(command, message: Self.errorAppIdMissing)
156
+ return
157
+ }
158
+ let options = optionsDict(from: command)
159
+ Task {
160
+ do {
161
+ let result = try await impl.fetchChannels(LiveUpdateFetchChannelsOptions(options))
162
+ resolve(command, data: result.toJSObject())
163
+ } catch {
164
+ reject(command, error: error)
165
+ }
166
+ }
167
+ }
168
+
169
+ @objc(fetchLatestBundle:)
170
+ func fetchLatestBundle(_ command: CDVInvokedUrlCommand) {
171
+ guard let impl = implementation, let cfg = config else {
172
+ reject(command, message: Self.errorPluginNotInitialized)
173
+ return
174
+ }
175
+ guard let appId = cfg.appId, !appId.isEmpty else {
176
+ reject(command, message: Self.errorAppIdMissing)
177
+ return
178
+ }
179
+ let options = optionsDict(from: command)
180
+ Task {
181
+ do {
182
+ let result = try await impl.fetchLatestBundle(LiveUpdateFetchLatestBundleOptions(options))
183
+ resolve(command, data: result.toJSObject())
184
+ } catch {
185
+ reject(command, error: error)
186
+ }
187
+ }
188
+ }
189
+
190
+ @objc(getBlockedBundles:)
191
+ func getBlockedBundles(_ command: CDVInvokedUrlCommand) {
192
+ completeResult(command) { impl, completion in impl.getBlockedBundles(completion: completion) }
193
+ }
194
+
195
+ @objc(getBundles:)
196
+ func getBundles(_ command: CDVInvokedUrlCommand) {
197
+ completeResult(command) { impl, completion in impl.getBundles(completion: completion) }
198
+ }
199
+
200
+ @objc(getChannel:)
201
+ func getChannel(_ command: CDVInvokedUrlCommand) {
202
+ completeResult(command) { impl, completion in impl.getChannel(completion: completion) }
203
+ }
204
+
205
+ @objc(getConfig:)
206
+ func getConfig(_ command: CDVInvokedUrlCommand) {
207
+ completeResult(command) { impl, completion in impl.getConfig(completion: completion) }
208
+ }
209
+
210
+ @objc(getCurrentBundle:)
211
+ func getCurrentBundle(_ command: CDVInvokedUrlCommand) {
212
+ completeResult(command) { impl, completion in impl.getCurrentBundle(completion: completion) }
213
+ }
214
+
215
+ @objc(getCustomId:)
216
+ func getCustomId(_ command: CDVInvokedUrlCommand) {
217
+ completeResult(command) { impl, completion in impl.getCustomId(completion: completion) }
218
+ }
219
+
220
+ @objc(getDeviceId:)
221
+ func getDeviceId(_ command: CDVInvokedUrlCommand) {
222
+ completeResult(command) { impl, completion in impl.getDeviceId(completion: completion) }
223
+ }
224
+
225
+ @objc(getDownloadedBundles:)
226
+ func getDownloadedBundles(_ command: CDVInvokedUrlCommand) {
227
+ completeResult(command) { impl, completion in impl.getDownloadedBundles(completion: completion) }
228
+ }
229
+
230
+ @objc(getNextBundle:)
231
+ func getNextBundle(_ command: CDVInvokedUrlCommand) {
232
+ completeResult(command) { impl, completion in impl.getNextBundle(completion: completion) }
233
+ }
234
+
235
+ @objc(getVersionCode:)
236
+ func getVersionCode(_ command: CDVInvokedUrlCommand) {
237
+ completeResult(command) { impl, completion in impl.getVersionCode(completion: completion) }
238
+ }
239
+
240
+ @objc(getVersionName:)
241
+ func getVersionName(_ command: CDVInvokedUrlCommand) {
242
+ completeResult(command) { impl, completion in impl.getVersionName(completion: completion) }
243
+ }
244
+
245
+ @objc(isSyncing:)
246
+ func isSyncing(_ command: CDVInvokedUrlCommand) {
247
+ completeResult(command) { impl, completion in impl.isSyncing(completion: completion) }
248
+ }
249
+
250
+ @objc(ready:)
251
+ func ready(_ command: CDVInvokedUrlCommand) {
252
+ completeResult(command) { impl, completion in impl.ready(completion: completion) }
253
+ }
254
+
255
+ @objc(reload:)
256
+ func reload(_ command: CDVInvokedUrlCommand) {
257
+ guard let impl = implementation else {
258
+ reject(command, message: Self.errorPluginNotInitialized)
259
+ return
260
+ }
261
+ impl.reload()
262
+ resolve(command)
263
+ }
264
+
265
+ @objc(reset:)
266
+ func reset(_ command: CDVInvokedUrlCommand) {
267
+ guard let impl = implementation else {
268
+ reject(command, message: Self.errorPluginNotInitialized)
269
+ return
270
+ }
271
+ impl.reset()
272
+ resolve(command)
273
+ }
274
+
275
+ @objc(resetConfig:)
276
+ func resetConfig(_ command: CDVInvokedUrlCommand) {
277
+ guard let impl = implementation else {
278
+ reject(command, message: Self.errorPluginNotInitialized)
279
+ return
280
+ }
281
+ impl.resetConfig()
282
+ resolve(command)
283
+ }
284
+
285
+ @objc(setChannel:)
286
+ func setChannel(_ command: CDVInvokedUrlCommand) {
287
+ guard let impl = implementation else {
288
+ reject(command, message: Self.errorPluginNotInitialized)
289
+ return
290
+ }
291
+ let options = optionsDict(from: command)
292
+ let channel = string(options, "channel")
293
+ impl.setChannel(LiveUpdateSetChannelOptions(channel: channel)) { [weak self] error in
294
+ self?.completeEmpty(command, error: error)
295
+ }
296
+ }
297
+
298
+ @objc(setConfig:)
299
+ func setConfig(_ command: CDVInvokedUrlCommand) {
300
+ guard let impl = implementation else {
301
+ reject(command, message: Self.errorPluginNotInitialized)
302
+ return
303
+ }
304
+ let options = optionsDict(from: command)
305
+ impl.setConfig(LiveUpdateSetConfigOptions(options))
306
+ resolve(command)
307
+ }
308
+
309
+ @objc(setCustomId:)
310
+ func setCustomId(_ command: CDVInvokedUrlCommand) {
311
+ guard let impl = implementation else {
312
+ reject(command, message: Self.errorPluginNotInitialized)
313
+ return
314
+ }
315
+ let options = optionsDict(from: command)
316
+ guard let customId = string(options, "customId") else {
317
+ reject(command, message: Self.errorCustomIdMissing)
318
+ return
319
+ }
320
+ impl.setCustomId(LiveUpdateSetCustomIdOptions(customId: customId)) { [weak self] error in
321
+ self?.completeEmpty(command, error: error)
322
+ }
323
+ }
324
+
325
+ @objc(setNextBundle:)
326
+ func setNextBundle(_ command: CDVInvokedUrlCommand) {
327
+ guard let impl = implementation else {
328
+ reject(command, message: Self.errorPluginNotInitialized)
329
+ return
330
+ }
331
+ let options = optionsDict(from: command)
332
+ impl.setNextBundle(LiveUpdateSetNextBundleOptions(options)) { [weak self] error in
333
+ self?.completeEmpty(command, error: error)
334
+ }
335
+ }
336
+
337
+ @objc(sync:)
338
+ func sync(_ command: CDVInvokedUrlCommand) {
339
+ guard let impl = implementation, let cfg = config else {
340
+ reject(command, message: Self.errorPluginNotInitialized)
341
+ return
342
+ }
343
+ guard let appId = cfg.appId, !appId.isEmpty else {
344
+ reject(command, message: Self.errorAppIdMissing)
345
+ return
346
+ }
347
+ let options = optionsDict(from: command)
348
+ Task {
349
+ do {
350
+ let result = try await impl.sync(LiveUpdateSyncOptions(options))
351
+ resolve(command, data: result.toJSObject())
352
+ } catch {
353
+ reject(command, error: error)
354
+ }
355
+ }
356
+ }
357
+
358
+ @objc(addListener:)
359
+ func addListener(_ command: CDVInvokedUrlCommand) {
360
+ guard let eventName = command.argument(at: 0) as? String,
361
+ let listenerId = command.argument(at: 1) as? String else {
362
+ reject(command, message: Self.errorListenerIdMissing)
363
+ return
364
+ }
365
+ listenersLock.lock()
366
+ listeners[listenerId] = ListenerRegistration(eventName: eventName, callbackId: command.callbackId)
367
+ listenersLock.unlock()
368
+ // Keep the callback alive so we can deliver future events to it.
369
+ let result = CDVPluginResult(status: .noResult)
370
+ result.keepCallback = NSNumber(value: true)
371
+ commandDelegate.send(result, callbackId: command.callbackId)
372
+ }
373
+
374
+ @objc(removeListener:)
375
+ func removeListener(_ command: CDVInvokedUrlCommand) {
376
+ let options = optionsDict(from: command)
377
+ guard let listenerId = string(options, "listenerId") else {
378
+ reject(command, message: Self.errorListenerIdMissing)
379
+ return
380
+ }
381
+ listenersLock.lock()
382
+ let removed = listeners.removeValue(forKey: listenerId)
383
+ listenersLock.unlock()
384
+ if let removed = removed {
385
+ // Release the JS callback that was kept alive by addListener.
386
+ let release = CDVPluginResult(status: .noResult)
387
+ release.keepCallback = NSNumber(value: false)
388
+ commandDelegate.send(release, callbackId: removed.callbackId)
389
+ }
390
+ resolve(command)
391
+ }
392
+
393
+ @objc(removeAllListeners:)
394
+ func removeAllListeners(_ command: CDVInvokedUrlCommand) {
395
+ listenersLock.lock()
396
+ let toRelease = Array(listeners.values)
397
+ listeners.removeAll()
398
+ listenersLock.unlock()
399
+ for entry in toRelease {
400
+ let release = CDVPluginResult(status: .noResult)
401
+ release.keepCallback = NSNumber(value: false)
402
+ commandDelegate.send(release, callbackId: entry.callbackId)
403
+ }
404
+ resolve(command)
405
+ }
406
+
407
+ // MARK: - Helpers
408
+
409
+ private func notifyJSListeners(eventName: String, data: [String: Any]) {
410
+ listenersLock.lock()
411
+ let recipients = listeners.values.filter { $0.eventName == eventName }
412
+ listenersLock.unlock()
413
+ for entry in recipients {
414
+ let result = CDVPluginResult(status: .ok, messageAs: data)
415
+ result.keepCallback = NSNumber(value: true)
416
+ commandDelegate.send(result, callbackId: entry.callbackId)
417
+ }
418
+ }
419
+
420
+ private func optionsDict(from command: CDVInvokedUrlCommand) -> [String: Any] {
421
+ return (command.argument(at: 0) as? [String: Any]) ?? [:]
422
+ }
423
+
424
+ private func string(_ options: [String: Any], _ key: String) -> String? {
425
+ guard let value = options[key] as? String, !value.isEmpty else { return nil }
426
+ return value
427
+ }
428
+
429
+ private func completeEmpty(_ command: CDVInvokedUrlCommand, error: Error?) {
430
+ if let error = error {
431
+ reject(command, error: error)
432
+ } else {
433
+ resolve(command)
434
+ }
435
+ }
436
+
437
+ private func completeResult(
438
+ _ command: CDVInvokedUrlCommand,
439
+ _ invoke: (LiveUpdate, @escaping (LiveUpdateResult?, Error?) -> Void) -> Void
440
+ ) {
441
+ guard let impl = implementation else {
442
+ reject(command, message: Self.errorPluginNotInitialized)
443
+ return
444
+ }
445
+ invoke(impl) { [weak self] result, error in
446
+ if let error = error {
447
+ self?.reject(command, error: error)
448
+ return
449
+ }
450
+ if let result = result {
451
+ self?.resolve(command, data: result.toJSObject())
452
+ } else {
453
+ self?.resolve(command)
454
+ }
455
+ }
456
+ }
457
+
458
+ private func resolve(_ command: CDVInvokedUrlCommand) {
459
+ let result = CDVPluginResult(status: .ok)
460
+ commandDelegate.send(result, callbackId: command.callbackId)
461
+ }
462
+
463
+ private func resolve(_ command: CDVInvokedUrlCommand, data: [String: Any]) {
464
+ let result = CDVPluginResult(status: .ok, messageAs: data)
465
+ commandDelegate.send(result, callbackId: command.callbackId)
466
+ }
467
+
468
+ private func reject(_ command: CDVInvokedUrlCommand, message: String) {
469
+ let result = CDVPluginResult(status: .error, messageAs: message)
470
+ commandDelegate.send(result, callbackId: command.callbackId)
471
+ }
472
+
473
+ private func reject(_ command: CDVInvokedUrlCommand, error: Error) {
474
+ var message = error.localizedDescription
475
+ if let urlError = error as? URLError, urlError.code == .timedOut {
476
+ message = Self.errorHttpTimeout
477
+ }
478
+ NSLog("[\(Self.tag)] \(message)")
479
+ reject(command, message: message)
480
+ }
481
+
482
+ private func loadConfig() -> LiveUpdateConfig {
483
+ var cfg = LiveUpdateConfig()
484
+ let info = Bundle.main.infoDictionary ?? [:]
485
+ // Note: plist keys keep the `CapawesomeLiveUpdate` prefix because that
486
+ // is the external contract injected by plugin.xml `<config-file>`
487
+ // entries. Cordova-lib's `default=" "` sentinel becomes a single-space
488
+ // string in the host app's Info.plist; we trim and treat whitespace-only
489
+ // as unset.
490
+ if let appId = trimmed(info["CapawesomeLiveUpdateAppId"]) { cfg.appId = appId }
491
+ if let defaultChannel = trimmed(info["CapawesomeLiveUpdateDefaultChannel"]) { cfg.defaultChannel = defaultChannel }
492
+ if let strategy = trimmed(info["CapawesomeLiveUpdateAutoUpdateStrategy"]) { cfg.autoUpdateStrategy = strategy }
493
+ if let timeout = parseInt(info["CapawesomeLiveUpdateHttpTimeout"]) { cfg.httpTimeout = timeout }
494
+ if let publicKey = trimmed(info["CapawesomeLiveUpdatePublicKey"]) { cfg.publicKey = publicKey }
495
+ if let timeout = parseInt(info["CapawesomeLiveUpdateReadyTimeout"]) { cfg.readyTimeout = timeout }
496
+ if let domain = trimmed(info["CapawesomeLiveUpdateServerDomain"]) { cfg.serverDomain = domain }
497
+ if let value = parseBool(info["CapawesomeLiveUpdateAutoDeleteBundles"]) { cfg.autoDeleteBundles = value }
498
+ if let value = parseBool(info["CapawesomeLiveUpdateAutoBlockRolledBackBundles"]) { cfg.autoBlockRolledBackBundles = value }
499
+ return cfg
500
+ }
501
+
502
+ private func trimmed(_ value: Any?) -> String? {
503
+ guard let string = value as? String else { return nil }
504
+ let trimmed = string.trimmingCharacters(in: .whitespacesAndNewlines)
505
+ return trimmed.isEmpty ? nil : trimmed
506
+ }
507
+
508
+ private func parseInt(_ value: Any?) -> Int? {
509
+ if let s = value as? String, let i = Int(s) { return i }
510
+ if let i = value as? Int { return i }
511
+ if let n = value as? NSNumber { return n.intValue }
512
+ return nil
513
+ }
514
+
515
+ private func parseBool(_ value: Any?) -> Bool? {
516
+ if let s = value as? String { return (s as NSString).boolValue }
517
+ if let b = value as? Bool { return b }
518
+ if let n = value as? NSNumber { return n.boolValue }
519
+ return nil
520
+ }
521
+ }
@@ -0,0 +1,116 @@
1
+ import Foundation
2
+
3
+ public class LiveUpdatePreferences: NSObject {
4
+ private let appIdKey = "appId" // DO NOT CHANGE
5
+ private let blockedBundleIdsKey = "blockedBundleIds" // DO NOT CHANGE
6
+ private let channelKey = "channel" // DO NOT CHANGE
7
+ private let customIdKey = "customId" // DO NOT CHANGE
8
+ private let lastVersionCodeKey = "lastVersionCode" // DO NOT CHANGE
9
+ private let lastVersionNameKey = "lastVersionName" // DO NOT CHANGE
10
+ private let nextBundleIdKey = "nextBundleId" // DO NOT CHANGE
11
+ private let previousBundleIdKey = "previousBundleIdKey" // DO NOT CHANGE
12
+
13
+ public func getAppId() -> String? {
14
+ return UserDefaults.standard.string(forKey: applyPrefix(to: appIdKey))
15
+ }
16
+
17
+ public func getBlockedBundleIds() -> String? {
18
+ return UserDefaults.standard.string(forKey: applyPrefix(to: blockedBundleIdsKey))
19
+ }
20
+
21
+ public func getChannel() -> String? {
22
+ return UserDefaults.standard.string(forKey: applyPrefix(to: channelKey))
23
+ }
24
+
25
+ public func getCustomId() -> String? {
26
+ return UserDefaults.standard.string(forKey: applyPrefix(to: customIdKey))
27
+ }
28
+
29
+ public func getLastVersionCode() -> String? {
30
+ return UserDefaults.standard.string(forKey: applyPrefix(to: lastVersionCodeKey))
31
+ }
32
+
33
+ public func getLastVersionName() -> String? {
34
+ return UserDefaults.standard.string(forKey: applyPrefix(to: lastVersionNameKey))
35
+ }
36
+
37
+ public func getNextBundleId() -> String? {
38
+ return UserDefaults.standard.string(forKey: applyPrefix(to: nextBundleIdKey))
39
+ }
40
+
41
+ public func getPreviousBundleId() -> String? {
42
+ return UserDefaults.standard.string(forKey: applyPrefix(to: previousBundleIdKey))
43
+ }
44
+
45
+ public func setAppId(_ value: String?) {
46
+ if let value = value {
47
+ UserDefaults.standard.set(value, forKey: applyPrefix(to: appIdKey))
48
+ } else {
49
+ UserDefaults.standard.removeObject(forKey: applyPrefix(to: appIdKey))
50
+ }
51
+ UserDefaults.standard.synchronize()
52
+ }
53
+
54
+ public func setBlockedBundleIds(_ value: String?) {
55
+ if let value = value {
56
+ UserDefaults.standard.set(value, forKey: applyPrefix(to: blockedBundleIdsKey))
57
+ } else {
58
+ UserDefaults.standard.removeObject(forKey: applyPrefix(to: blockedBundleIdsKey))
59
+ }
60
+ UserDefaults.standard.synchronize()
61
+ }
62
+
63
+ public func setChannel(_ value: String?) {
64
+ if let value = value {
65
+ UserDefaults.standard.set(value, forKey: applyPrefix(to: channelKey))
66
+ } else {
67
+ UserDefaults.standard.removeObject(forKey: applyPrefix(to: channelKey))
68
+ }
69
+ UserDefaults.standard.synchronize()
70
+ }
71
+
72
+ public func setCustomId(_ value: String) {
73
+ UserDefaults.standard.set(value, forKey: applyPrefix(to: customIdKey))
74
+ UserDefaults.standard.synchronize()
75
+ }
76
+
77
+ public func setLastVersionCode(_ value: String?) {
78
+ if let value = value {
79
+ UserDefaults.standard.set(value, forKey: applyPrefix(to: lastVersionCodeKey))
80
+ } else {
81
+ UserDefaults.standard.removeObject(forKey: applyPrefix(to: lastVersionCodeKey))
82
+ }
83
+ UserDefaults.standard.synchronize()
84
+ }
85
+
86
+ public func setLastVersionName(_ value: String?) {
87
+ if let value = value {
88
+ UserDefaults.standard.set(value, forKey: applyPrefix(to: lastVersionNameKey))
89
+ } else {
90
+ UserDefaults.standard.removeObject(forKey: applyPrefix(to: lastVersionNameKey))
91
+ }
92
+ UserDefaults.standard.synchronize()
93
+ }
94
+
95
+ public func setNextBundleId(_ value: String?) {
96
+ if let value = value {
97
+ UserDefaults.standard.set(value, forKey: applyPrefix(to: nextBundleIdKey))
98
+ } else {
99
+ UserDefaults.standard.removeObject(forKey: applyPrefix(to: nextBundleIdKey))
100
+ }
101
+ UserDefaults.standard.synchronize()
102
+ }
103
+
104
+ public func setPreviousBundleId(_ value: String?) {
105
+ if let value = value {
106
+ UserDefaults.standard.set(value, forKey: applyPrefix(to: previousBundleIdKey))
107
+ } else {
108
+ UserDefaults.standard.removeObject(forKey: applyPrefix(to: previousBundleIdKey))
109
+ }
110
+ UserDefaults.standard.synchronize()
111
+ }
112
+
113
+ private func applyPrefix(to key: String) -> String {
114
+ return LiveUpdatePlugin.userDefaultsPrefix + "." + key
115
+ }
116
+ }
@@ -0,0 +1,21 @@
1
+ import Foundation
2
+
3
+ @objc public class LiveUpdateReadyResult: NSObject, LiveUpdateResult {
4
+ let currentBundleId: String?
5
+ let previousBundleId: String?
6
+ let rollback: Bool
7
+
8
+ init(currentBundleId: String?, previousBundleId: String?, rollback: Bool) {
9
+ self.currentBundleId = currentBundleId
10
+ self.previousBundleId = previousBundleId
11
+ self.rollback = rollback
12
+ }
13
+
14
+ public func toJSObject() -> [String: Any] {
15
+ var result: [String: Any] = [:]
16
+ result["currentBundleId"] = currentBundleId == nil ? NSNull() : currentBundleId
17
+ result["previousBundleId"] = previousBundleId == nil ? NSNull() : previousBundleId
18
+ result["rollback"] = rollback
19
+ return result
20
+ }
21
+ }
@@ -0,0 +1,5 @@
1
+ import Foundation
2
+
3
+ @objc public protocol LiveUpdateResult {
4
+ @objc func toJSObject() -> [String: Any]
5
+ }