@capgo/capacitor-updater 8.47.1 → 8.47.2

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.
@@ -79,10 +79,16 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
79
79
  CAPPluginMethod(name: "completeFlexibleUpdate", returnType: CAPPluginReturnPromise)
80
80
  ]
81
81
  public var implementation = CapgoUpdater()
82
- private let pluginVersion: String = "8.47.1"
82
+ private let pluginVersion: String = "8.47.2"
83
83
  static let updateUrlDefault = "https://plugin.capgo.app/updates"
84
84
  static let statsUrlDefault = "https://plugin.capgo.app/stats"
85
85
  static let channelUrlDefault = "https://plugin.capgo.app/channel_self"
86
+ static let autoUpdateModeOff = "off"
87
+ static let autoUpdateModeBackground = "atBackground"
88
+ static let autoUpdateModeInstall = "atInstall"
89
+ static let autoUpdateModeLaunch = "onLaunch"
90
+ static let autoUpdateModeAlways = "always"
91
+ static let autoUpdateModeOnlyDownload = "onlyDownload"
86
92
  private let keepUrlPathFlagKey = "__capgo_keep_url_path_after_reload"
87
93
  private let customIdDefaultsKey = "CapacitorUpdater.customId"
88
94
  private let updateUrlDefaultsKey = "CapacitorUpdater.updateUrl"
@@ -102,6 +108,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
102
108
  private var currentVersionNative: Version = "0.0.0"
103
109
  private var currentBuildVersion: String = "0"
104
110
  private var autoUpdate = false
111
+ private var autoUpdateMode = CapacitorUpdaterPlugin.autoUpdateModeOff
105
112
  private var appReadyTimeout = 10000
106
113
  private var appReadyCheck: DispatchWorkItem?
107
114
  private var resetWhenUpdate = true
@@ -203,33 +210,6 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
203
210
  keepUrlPathAfterReload = getConfig().getBoolean("keepUrlPathAfterReload", false)
204
211
  syncKeepUrlPathFlag(enabled: keepUrlPathAfterReload)
205
212
 
206
- // Handle directUpdate configuration - support string values and backward compatibility
207
- if let directUpdateString = getConfig().getString("directUpdate") {
208
- // Handle backward compatibility for boolean true
209
- if directUpdateString == "true" {
210
- directUpdateMode = "always"
211
- directUpdate = true
212
- } else {
213
- directUpdateMode = directUpdateString
214
- directUpdate = directUpdateString == "always" || directUpdateString == "atInstall" || directUpdateString == "onLaunch"
215
- // Validate directUpdate value
216
- if directUpdateString != "false" && directUpdateString != "always" && directUpdateString != "atInstall" && directUpdateString != "onLaunch" {
217
- logger.error("Invalid directUpdate value: \"\(directUpdateString)\". Supported values are: \"false\", \"true\", \"always\", \"atInstall\", \"onLaunch\". Defaulting to \"false\".")
218
- directUpdateMode = "false"
219
- directUpdate = false
220
- }
221
- }
222
- } else {
223
- let directUpdateBool = getConfig().getBoolean("directUpdate", false)
224
- if directUpdateBool {
225
- directUpdateMode = "always" // backward compatibility: true = always
226
- directUpdate = true
227
- } else {
228
- directUpdateMode = "false"
229
- directUpdate = false
230
- }
231
- }
232
-
233
213
  autoSplashscreen = getConfig().getBoolean("autoSplashscreen", false)
234
214
  autoSplashscreenLoader = getConfig().getBoolean("autoSplashscreenLoader", false)
235
215
  let splashscreenTimeoutValue = getConfig().getInt("autoSplashscreenTimeout", 10000)
@@ -239,7 +219,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
239
219
  updateUrl = storedUpdateUrl
240
220
  logger.info("Loaded persisted updateUrl")
241
221
  }
242
- autoUpdate = getConfig().getBoolean("autoUpdate", true)
222
+ configureAutoUpdateModeFromConfig()
243
223
  appReadyTimeout = max(1000, getConfig().getInt("appReadyTimeout", 10000)) // Minimum 1 second
244
224
  implementation.timeout = Double(getConfig().getInt("responseTimeout", 20))
245
225
  resetWhenUpdate = getConfig().getBoolean("resetWhenUpdate", true)
@@ -787,7 +767,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
787
767
  let url = URL(string: urlString)
788
768
  logger.info("Downloading \(String(describing: url))")
789
769
  self.saveCallForAsyncHandling(call)
790
- DispatchQueue.global(qos: .background).async {
770
+ self.runBackgroundDownloadWork {
791
771
  do {
792
772
  let next: BundleInfo
793
773
  if let manifestEntries = self.manifestEntries(from: manifestArray) {
@@ -2076,6 +2056,9 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
2076
2056
  }
2077
2057
 
2078
2058
  private func shouldUseDirectUpdate() -> Bool {
2059
+ if !self.autoUpdate || self.autoUpdateMode == Self.autoUpdateModeOnlyDownload {
2060
+ return false
2061
+ }
2079
2062
  if self.autoSplashscreenTimedOut {
2080
2063
  return false
2081
2064
  }
@@ -2102,6 +2085,99 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
2102
2085
  }
2103
2086
  }
2104
2087
 
2088
+ private func configureAutoUpdateModeFromConfig() {
2089
+ if let configuredMode = getConfig().getString("autoUpdate"),
2090
+ configuredMode != "",
2091
+ configuredMode != "true",
2092
+ configuredMode != "false" {
2093
+ autoUpdateMode = Self.normalizedAutoUpdateMode(configuredMode)
2094
+ if autoUpdateMode != configuredMode {
2095
+ logger.error(
2096
+ "Invalid autoUpdate value: \"\(configuredMode)\". Supported values are: true, false, " +
2097
+ "\"off\", \"atBackground\", \"atInstall\", \"onLaunch\", \"always\", \"onlyDownload\". Defaulting to \"atBackground\"."
2098
+ )
2099
+ }
2100
+ } else {
2101
+ let configuredMode = getConfig().getString("autoUpdate")
2102
+ let enabled = configuredMode != nil ? configuredMode == "true" : getConfig().getBoolean("autoUpdate", true)
2103
+ autoUpdateMode = enabled
2104
+ ? Self.autoUpdateModeForLegacyDirectUpdateMode(resolveLegacyDirectUpdateModeFromConfig())
2105
+ : Self.autoUpdateModeOff
2106
+ }
2107
+
2108
+ autoUpdate = Self.isAutoUpdateModeEnabled(autoUpdateMode)
2109
+ directUpdateMode = Self.directUpdateModeForAutoUpdateMode(autoUpdateMode)
2110
+ directUpdate = Self.isDirectUpdateMode(directUpdateMode)
2111
+ }
2112
+
2113
+ private func resolveLegacyDirectUpdateModeFromConfig() -> String {
2114
+ if let directUpdateString = getConfig().getString("directUpdate") {
2115
+ if directUpdateString == "true" {
2116
+ return Self.autoUpdateModeAlways
2117
+ }
2118
+ if directUpdateString == "false" || Self.isDirectUpdateMode(directUpdateString) {
2119
+ return directUpdateString
2120
+ }
2121
+ logger.error(
2122
+ "Invalid directUpdate value: \"\(directUpdateString)\". Supported values are: false, true, " +
2123
+ "\"always\", \"atInstall\", \"onLaunch\". Defaulting to \"false\"."
2124
+ )
2125
+ return "false"
2126
+ }
2127
+
2128
+ return getConfig().getBoolean("directUpdate", false) ? Self.autoUpdateModeAlways : "false"
2129
+ }
2130
+
2131
+ static func normalizedAutoUpdateMode(_ value: String?) -> String {
2132
+ guard let value else {
2133
+ return autoUpdateModeBackground
2134
+ }
2135
+ switch value {
2136
+ case "false", autoUpdateModeOff:
2137
+ return autoUpdateModeOff
2138
+ case "true", autoUpdateModeBackground:
2139
+ return autoUpdateModeBackground
2140
+ case autoUpdateModeInstall, autoUpdateModeLaunch, autoUpdateModeAlways, autoUpdateModeOnlyDownload:
2141
+ return value
2142
+ default:
2143
+ return autoUpdateModeBackground
2144
+ }
2145
+ }
2146
+
2147
+ static func autoUpdateModeForLegacyDirectUpdateMode(_ directUpdateMode: String) -> String {
2148
+ switch directUpdateMode {
2149
+ case autoUpdateModeInstall, autoUpdateModeLaunch, autoUpdateModeAlways:
2150
+ return directUpdateMode
2151
+ default:
2152
+ return autoUpdateModeBackground
2153
+ }
2154
+ }
2155
+
2156
+ static func directUpdateModeForAutoUpdateMode(_ autoUpdateMode: String) -> String {
2157
+ switch autoUpdateMode {
2158
+ case autoUpdateModeInstall, autoUpdateModeLaunch, autoUpdateModeAlways:
2159
+ return autoUpdateMode
2160
+ default:
2161
+ return "false"
2162
+ }
2163
+ }
2164
+
2165
+ static func isAutoUpdateModeEnabled(_ autoUpdateMode: String) -> Bool {
2166
+ autoUpdateMode != autoUpdateModeOff
2167
+ }
2168
+
2169
+ static func shouldAutoUpdateModeSetNextBundle(_ autoUpdateMode: String) -> Bool {
2170
+ isAutoUpdateModeEnabled(autoUpdateMode) && autoUpdateMode != autoUpdateModeOnlyDownload
2171
+ }
2172
+
2173
+ static func isDirectUpdateMode(_ directUpdateMode: String) -> Bool {
2174
+ directUpdateMode == autoUpdateModeInstall || directUpdateMode == autoUpdateModeLaunch || directUpdateMode == autoUpdateModeAlways
2175
+ }
2176
+
2177
+ private func shouldAutoSetNextBundle() -> Bool {
2178
+ Self.shouldAutoUpdateModeSetNextBundle(autoUpdateMode)
2179
+ }
2180
+
2105
2181
  static func shouldConsumeOnLaunchDirectUpdate(directUpdateMode: String, plannedDirectUpdate: Bool) -> Bool {
2106
2182
  plannedDirectUpdate && directUpdateMode == "onLaunch"
2107
2183
  }
@@ -2135,6 +2211,9 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
2135
2211
 
2136
2212
  func configureDirectUpdateModeForTesting(_ directUpdateMode: String, onLaunchDirectUpdateUsed: Bool = false) {
2137
2213
  self.directUpdateMode = directUpdateMode
2214
+ self.autoUpdateMode = Self.autoUpdateModeForLegacyDirectUpdateMode(directUpdateMode)
2215
+ self.autoUpdate = Self.isAutoUpdateModeEnabled(self.autoUpdateMode)
2216
+ self.directUpdate = Self.isDirectUpdateMode(self.directUpdateMode)
2138
2217
  self.setOnLaunchDirectUpdateUsed(onLaunchDirectUpdateUsed)
2139
2218
  }
2140
2219
 
@@ -2142,6 +2221,13 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
2142
2221
  self.updateUrl = updateUrl
2143
2222
  }
2144
2223
 
2224
+ func setAutoUpdateModeForTesting(_ autoUpdateMode: String) {
2225
+ self.autoUpdateMode = Self.normalizedAutoUpdateMode(autoUpdateMode)
2226
+ self.autoUpdate = Self.isAutoUpdateModeEnabled(self.autoUpdateMode)
2227
+ self.directUpdateMode = Self.directUpdateModeForAutoUpdateMode(self.autoUpdateMode)
2228
+ self.directUpdate = Self.isDirectUpdateMode(self.directUpdateMode)
2229
+ }
2230
+
2145
2231
  func setCurrentBuildVersionForTesting(_ currentBuildVersion: String) {
2146
2232
  self.currentBuildVersion = currentBuildVersion
2147
2233
  }
@@ -2237,7 +2323,8 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
2237
2323
  plannedDirectUpdate: Bool = false,
2238
2324
  failureAction: String = "download_fail",
2239
2325
  failureEvent: String = "downloadFailed",
2240
- sendStats: Bool = true
2326
+ sendStats: Bool = true,
2327
+ notifyNoNeedUpdate: Bool = true
2241
2328
  ) {
2242
2329
  // Clear download in progress flag - this is called at the end of every download attempt
2243
2330
  // whether it succeeds, fails, or is skipped (e.g., already up to date)
@@ -2254,7 +2341,9 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
2254
2341
  }
2255
2342
  self.notifyListeners(failureEvent, data: ["version": latestVersionName])
2256
2343
  }
2257
- self.notifyListeners("noNeedUpdate", data: ["bundle": current.toJSON()])
2344
+ if notifyNoNeedUpdate {
2345
+ self.notifyListeners("noNeedUpdate", data: ["bundle": current.toJSON()])
2346
+ }
2258
2347
  self.sendReadyToJs(current: current, msg: msg)
2259
2348
  logger.info("endBackGroundTaskWithNotif \(msg) current: \(current.getVersionName()) latestVersionName: \(latestVersionName)")
2260
2349
  self.endBackGroundTask()
@@ -2314,7 +2403,14 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
2314
2403
  downloadLock.unlock()
2315
2404
 
2316
2405
  let plannedDirectUpdate = self.shouldUseDirectUpdate()
2317
- let messageUpdate = plannedDirectUpdate ? "Update will occur now." : "Update will occur next time app moves to background."
2406
+ let messageUpdate: String
2407
+ if plannedDirectUpdate {
2408
+ messageUpdate = "Update will occur now."
2409
+ } else if self.shouldAutoSetNextBundle() {
2410
+ messageUpdate = "Update will occur next time app moves to background."
2411
+ } else {
2412
+ messageUpdate = "Update will be downloaded and made available."
2413
+ }
2318
2414
  guard let url = URL(string: self.updateUrl) else {
2319
2415
  logger.error("Error no url or wrong format")
2320
2416
  // Clear the flag if we return early
@@ -2358,7 +2454,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
2358
2454
  error: false,
2359
2455
  plannedDirectUpdate: plannedDirectUpdate
2360
2456
  )
2361
- } else {
2457
+ } else if self.shouldAutoSetNextBundle() {
2362
2458
  if plannedDirectUpdate && !directUpdateAllowed {
2363
2459
  self.logger.info("Direct update skipped because splashscreen timeout occurred. Update will apply later.")
2364
2460
  }
@@ -2371,6 +2467,21 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
2371
2467
  error: false,
2372
2468
  plannedDirectUpdate: plannedDirectUpdate
2373
2469
  )
2470
+ } else {
2471
+ self.logger.info("autoUpdate is set to onlyDownload, builtin version will not be set as next bundle")
2472
+ let builtinUpdateAvailable = !current.isBuiltin()
2473
+ if builtinUpdateAvailable {
2474
+ let builtinBundle = self.implementation.getBundleInfo(id: BundleInfo.ID_BUILTIN)
2475
+ self.notifyListeners("updateAvailable", data: ["bundle": builtinBundle.toJSON()], retainUntilConsumed: true)
2476
+ }
2477
+ self.endBackGroundTaskWithNotif(
2478
+ msg: "Latest version is builtin, autoUpdate onlyDownload",
2479
+ latestVersionName: res.version,
2480
+ current: current,
2481
+ error: false,
2482
+ plannedDirectUpdate: plannedDirectUpdate,
2483
+ notifyNoNeedUpdate: !builtinUpdateAvailable
2484
+ )
2374
2485
  }
2375
2486
  return
2376
2487
  }
@@ -2483,7 +2594,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
2483
2594
  plannedDirectUpdate: plannedDirectUpdate
2484
2595
  )
2485
2596
  }
2486
- } else {
2597
+ } else if self.shouldAutoSetNextBundle() {
2487
2598
  if plannedDirectUpdate && !directUpdateAllowed {
2488
2599
  self.logger.info("Direct update skipped because splashscreen timeout occurred. Update will install on next app background.")
2489
2600
  }
@@ -2496,6 +2607,17 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
2496
2607
  error: false,
2497
2608
  plannedDirectUpdate: plannedDirectUpdate
2498
2609
  )
2610
+ } else {
2611
+ self.logger.info("autoUpdate is set to onlyDownload, downloaded update will not be set as next bundle")
2612
+ self.notifyListeners("updateAvailable", data: ["bundle": next.toJSON()], retainUntilConsumed: true)
2613
+ self.endBackGroundTaskWithNotif(
2614
+ msg: "update downloaded, autoUpdate onlyDownload",
2615
+ latestVersionName: latestVersionName,
2616
+ current: current,
2617
+ error: false,
2618
+ plannedDirectUpdate: plannedDirectUpdate,
2619
+ notifyNoNeedUpdate: false
2620
+ )
2499
2621
  }
2500
2622
  return
2501
2623
  } catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/capacitor-updater",
3
- "version": "8.47.1",
3
+ "version": "8.47.2",
4
4
  "license": "MPL-2.0",
5
5
  "description": "Live update for capacitor apps",
6
6
  "main": "dist/plugin.cjs.js",