@capgo/capacitor-updater 3.0.3

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.
@@ -0,0 +1,307 @@
1
+ import Foundation
2
+ import Capacitor
3
+ import Version
4
+
5
+ /**
6
+ * Please read the Capacitor iOS Plugin Development Guide
7
+ * here: https://capacitorjs.com/docs/plugins/ios
8
+ */
9
+ @objc(CapacitorUpdaterPlugin)
10
+ public class CapacitorUpdaterPlugin: CAPPlugin {
11
+ private var implementation = CapacitorUpdater()
12
+ static let autoUpdateUrlDefault = "https://capgo.app/api/auto_update"
13
+ static let statsUrlDefault = "https://capgo.app/api/stats"
14
+ private var autoUpdateUrl = ""
15
+ private var currentVersionNative: Version = "0.0.0"
16
+ private var autoUpdate = false
17
+ private var statsUrl = ""
18
+ private var resetWhenUpdate = true;
19
+
20
+ override public func load() {
21
+ do {
22
+ currentVersionNative = try Version(Bundle.main.buildVersionNumber ?? "0.0.0")
23
+ } catch {
24
+ print("✨ Capacitor-updater: Cannot get version native \(currentVersionNative)")
25
+ }
26
+ autoUpdateUrl = getConfigValue("autoUpdateUrl") as? String ?? CapacitorUpdaterPlugin.autoUpdateUrlDefault
27
+ autoUpdate = getConfigValue("autoUpdate") as? Bool ?? false
28
+ implementation.appId = Bundle.main.bundleIdentifier ?? ""
29
+ implementation.notifyDownload = notifyDownload
30
+ let config = (self.bridge?.viewController as? CAPBridgeViewController)?.instanceDescriptor().legacyConfig
31
+ if (config?["appId"] != nil) {
32
+ implementation.appId = config?["appId"] as! String
33
+ }
34
+ implementation.statsUrl = getConfigValue("statsUrl") as? String ?? CapacitorUpdaterPlugin.statsUrlDefault
35
+ if (resetWhenUpdate) {
36
+ var LatestVersionNative: Version = "0.0.0"
37
+ do {
38
+ LatestVersionNative = try Version(UserDefaults.standard.string(forKey: "LatestVersionNative") ?? "0.0.0")
39
+ } catch {
40
+ print("✨ Capacitor-updater: Cannot get version native \(currentVersionNative)")
41
+ }
42
+ if (LatestVersionNative != "0.0.0" && currentVersionNative.major > LatestVersionNative.major) {
43
+ _ = self._reset(toAutoUpdate: false)
44
+ UserDefaults.standard.set("", forKey: "LatestVersionAutoUpdate")
45
+ UserDefaults.standard.set("", forKey: "LatestVersionNameAutoUpdate")
46
+ let res = implementation.list()
47
+ res.forEach { version in
48
+ _ = implementation.delete(version: version, versionName: "")
49
+ }
50
+ }
51
+ UserDefaults.standard.set( Bundle.main.buildVersionNumber, forKey: "LatestVersionNative")
52
+ }
53
+ if (!autoUpdate || autoUpdateUrl == "") { return }
54
+ resetWhenUpdate = getConfigValue("resetWhenUpdate") as? Bool ?? true
55
+ let nc = NotificationCenter.default
56
+ nc.addObserver(self, selector: #selector(appMovedToBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
57
+ nc.addObserver(self, selector: #selector(appMovedToForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
58
+ self.appMovedToForeground()
59
+ }
60
+
61
+ @objc func notifyDownload(percent: Int) {
62
+ self.notifyListeners("download", data: ["percent": percent])
63
+ }
64
+
65
+ @objc func getId(_ call: CAPPluginCall) {
66
+ call.resolve(["id": implementation.deviceID])
67
+ }
68
+
69
+ @objc func download(_ call: CAPPluginCall) {
70
+ let url = URL(string: call.getString("url") ?? "")
71
+ let res = implementation.download(url: url!)
72
+ if ((res) != nil) {
73
+ call.resolve([
74
+ "version": res!
75
+ ])
76
+ } else {
77
+ call.reject("download failed")
78
+ }
79
+ }
80
+
81
+ private func _reload() -> Bool {
82
+ guard let bridge = self.bridge else { return false }
83
+
84
+ if let vc = bridge.viewController as? CAPBridgeViewController {
85
+ let pathHot = implementation.getLastPathHot()
86
+ let pathPersist = implementation.getLastPathPersist()
87
+ if (pathHot != "" && pathPersist != "") {
88
+ UserDefaults.standard.set(String(pathPersist.suffix(10)), forKey: "serverBasePath")
89
+ vc.setServerBasePath(path: pathHot)
90
+ print("✨ Capacitor-updater: Reload app done")
91
+ return true
92
+ } else {
93
+ return false
94
+ }
95
+ }
96
+ return false
97
+ }
98
+
99
+ @objc func reload(_ call: CAPPluginCall) {
100
+ if (self._reload()) {
101
+ call.resolve()
102
+ } else {
103
+ call.reject("Cannot reload")
104
+ }
105
+ }
106
+
107
+ @objc func set(_ call: CAPPluginCall) {
108
+ let version = call.getString("version") ?? ""
109
+ let versionName = call.getString("versionName") ?? version
110
+ let res = implementation.set(version: version, versionName: versionName)
111
+
112
+ if (res && self._reload()) {
113
+ print("✨ Capacitor-updater: Set to version: \(version) versionName: \(versionName)")
114
+ call.resolve()
115
+ } else {
116
+ call.reject("Update failed, version \(version) doesn't exist")
117
+ }
118
+ }
119
+
120
+ @objc func delete(_ call: CAPPluginCall) {
121
+ let version = call.getString("version") ?? ""
122
+ let res = implementation.delete(version: version, versionName: "")
123
+ if (res) {
124
+ call.resolve()
125
+ } else {
126
+ call.reject("Delete failed, version \(version) doesn't exist")
127
+ }
128
+ }
129
+
130
+ @objc func list(_ call: CAPPluginCall) {
131
+ let res = implementation.list()
132
+ call.resolve([
133
+ "versions": res
134
+ ])
135
+ }
136
+
137
+ @objc func _reset(toAutoUpdate: Bool) -> Bool {
138
+ guard let bridge = self.bridge else { return false }
139
+ if let vc = bridge.viewController as? CAPBridgeViewController {
140
+ let LatestVersionAutoUpdate = UserDefaults.standard.string(forKey: "LatestVersionAutoUpdate") ?? ""
141
+ let LatestVersionNameAutoUpdate = UserDefaults.standard.string(forKey: "LatestVersionNameAutoUpdate") ?? ""
142
+ if(toAutoUpdate && LatestVersionAutoUpdate != "" && LatestVersionNameAutoUpdate != "") {
143
+ let res = implementation.set(version: LatestVersionAutoUpdate, versionName: LatestVersionNameAutoUpdate)
144
+ return res && self._reload()
145
+ }
146
+ implementation.reset()
147
+ let pathPersist = implementation.getLastPathPersist()
148
+ vc.setServerBasePath(path: pathPersist)
149
+ UserDefaults.standard.set(pathPersist, forKey: "serverBasePath")
150
+ DispatchQueue.main.async {
151
+ vc.loadView()
152
+ vc.viewDidLoad()
153
+ print("✨ Capacitor-updater: Reset to original version")
154
+ }
155
+ return true
156
+ }
157
+ return false
158
+ }
159
+
160
+ @objc func reset(_ call: CAPPluginCall) {
161
+ let toAutoUpdate = call.getBool("toAutoUpdate") ?? false
162
+ if (self._reset(toAutoUpdate: toAutoUpdate)) {
163
+ return call.resolve()
164
+ }
165
+ call.reject("✨ Capacitor-updater: Reset failed")
166
+ }
167
+
168
+ @objc func versionName(_ call: CAPPluginCall) {
169
+ let name = implementation.getVersionName()
170
+ call.resolve([
171
+ "versionName": name
172
+ ])
173
+ }
174
+
175
+ @objc func current(_ call: CAPPluginCall) {
176
+ let pathHot = implementation.getLastPathHot()
177
+ let current = pathHot.count >= 10 ? pathHot.suffix(10) : "builtin"
178
+ call.resolve([
179
+ "current": current,
180
+ "currentNative": currentVersionNative
181
+ ])
182
+ }
183
+
184
+ @objc func notifyAppReady(_ call: CAPPluginCall) {
185
+ UserDefaults.standard.set(true, forKey: "notifyAppReady")
186
+ call.resolve()
187
+ }
188
+
189
+ @objc func delayUpdate(_ call: CAPPluginCall) {
190
+ UserDefaults.standard.set(true, forKey: "delayUpdate")
191
+ call.resolve()
192
+ }
193
+
194
+ @objc func cancelDelay(_ call: CAPPluginCall) {
195
+ UserDefaults.standard.set(false, forKey: "delayUpdate")
196
+ call.resolve()
197
+ }
198
+
199
+ @objc func appMovedToForeground() {
200
+ DispatchQueue.global(qos: .background).async {
201
+ print("✨ Capacitor-updater: Check for update in the server")
202
+ let url = URL(string: self.autoUpdateUrl)!
203
+ let res = self.implementation.getLatest(url: url)
204
+ if (res == nil) {
205
+ return
206
+ }
207
+ guard let downloadUrl = URL(string: res?.url ?? "") else {
208
+ print("✨ Capacitor-updater: Error \(res?.message ?? "Unknow error")")
209
+ if (res?.major == true) {
210
+ self.notifyListeners("majorAvailable", data: ["version": res?.version ?? "0.0.0"])
211
+ }
212
+ return
213
+ }
214
+ let currentVersion = self.implementation.getVersionName()
215
+ var failingVersion: Version = "0.0.0"
216
+ var newVersion: Version = "0.0.0"
217
+ do {
218
+ newVersion = try Version(res?.version ?? "0.0.0")
219
+ failingVersion = try Version(UserDefaults.standard.string(forKey: "failingVersion") ?? "0.0.0")
220
+ } catch {
221
+ print("✨ Capacitor-updater: Cannot get version \(failingVersion) \(newVersion)")
222
+ }
223
+ if (newVersion != "0.0.0" && newVersion != failingVersion) {
224
+ let dlOp = self.implementation.download(url: downloadUrl)
225
+ if let dl = dlOp {
226
+ print("✨ Capacitor-updater: New version: \(newVersion) found. Current is \(currentVersion == "" ? "builtin" : currentVersion), next backgrounding will trigger update")
227
+ UserDefaults.standard.set(dl, forKey: "nextVersion")
228
+ UserDefaults.standard.set(newVersion.description, forKey: "nextVersionName")
229
+ self.notifyListeners("updateAvailable", data: ["version": newVersion])
230
+ } else {
231
+ print("✨ Capacitor-updater: Download version \(newVersion) fail")
232
+ }
233
+ } else {
234
+ print("✨ Capacitor-updater: No need to update, \(currentVersion) is the latest")
235
+ }
236
+ }
237
+ }
238
+
239
+ @objc func appMovedToBackground() {
240
+ print("✨ Capacitor-updater: Check for waiting update")
241
+ let delayUpdate = UserDefaults.standard.bool(forKey: "delayUpdate")
242
+ UserDefaults.standard.set(false, forKey: "delayUpdate")
243
+ if (delayUpdate) {
244
+ print("✨ Capacitor-updater: Update delayed to next backgrounding")
245
+ return
246
+ }
247
+ let nextVersion = UserDefaults.standard.string(forKey: "nextVersion") ?? ""
248
+ let nextVersionName = UserDefaults.standard.string(forKey: "nextVersionName") ?? ""
249
+ let pastVersion = UserDefaults.standard.string(forKey: "pastVersion") ?? ""
250
+ let pastVersionName = UserDefaults.standard.string(forKey: "pastVersionName") ?? ""
251
+ let notifyAppReady = UserDefaults.standard.bool(forKey: "notifyAppReady")
252
+ let curVersion = implementation.getLastPathPersist().components(separatedBy: "/").last!
253
+ let curVersionName = implementation.getVersionName()
254
+ if (nextVersion != "" && nextVersionName != "") {
255
+ let res = implementation.set(version: nextVersion, versionName: nextVersionName)
256
+ if (res && self._reload()) {
257
+ print("✨ Capacitor-updater: Auto update to version: \(nextVersionName)")
258
+ UserDefaults.standard.set(nextVersion, forKey: "LatestVersionAutoUpdate")
259
+ UserDefaults.standard.set(nextVersionName, forKey: "LatestVersionNameAutoUpdate")
260
+ UserDefaults.standard.set("", forKey: "nextVersion")
261
+ UserDefaults.standard.set("", forKey: "nextVersionName")
262
+ UserDefaults.standard.set(curVersion, forKey: "pastVersion")
263
+ UserDefaults.standard.set(curVersionName, forKey: "pastVersionName")
264
+ UserDefaults.standard.set(false, forKey: "notifyAppReady")
265
+ } else {
266
+ print("✨ Capacitor-updater: Auto update to version: \(nextVersionName) Failed");
267
+ }
268
+ } else if (!notifyAppReady && curVersionName != "") {
269
+ print("✨ Capacitor-updater: notifyAppReady never trigger")
270
+ print("✨ Capacitor-updater: Version: \(curVersionName), is considered broken")
271
+ print("✨ Capacitor-updater: Will downgraded to version: \(pastVersionName == "" ? "builtin" : pastVersionName) for next start")
272
+ print("✨ Capacitor-updater: Don't forget to trigger 'notifyAppReady()' in js code to validate a version.")
273
+ implementation.sendStats(action: "revert", version: curVersionName)
274
+ if (pastVersion != "" && pastVersionName != "") {
275
+ let res = implementation.set(version: pastVersion, versionName: pastVersionName)
276
+ if (res && self._reload()) {
277
+ print("✨ Capacitor-updater: Revert to version: \(pastVersionName == "" ? "builtin" : pastVersionName)")
278
+ UserDefaults.standard.set(pastVersion, forKey: "LatestVersionAutoUpdate")
279
+ UserDefaults.standard.set(pastVersionName, forKey: "LatestVersionNameAutoUpdate")
280
+ UserDefaults.standard.set("", forKey: "pastVersion")
281
+ UserDefaults.standard.set("", forKey: "pastVersionName")
282
+ } else {
283
+ print("✨ Capacitor-updater: Revert to version: \(pastVersionName == "" ? "builtin" : pastVersionName) Failed");
284
+ }
285
+ } else {
286
+ if self._reset(toAutoUpdate: false) {
287
+ UserDefaults.standard.set("", forKey: "LatestVersionAutoUpdate")
288
+ UserDefaults.standard.set("", forKey: "LatestVersionNameAutoUpdate")
289
+ print("✨ Capacitor-updater: Auto reset done")
290
+ }
291
+ }
292
+ UserDefaults.standard.set(curVersionName, forKey: "failingVersion")
293
+ let res = implementation.delete(version: curVersion, versionName: curVersionName)
294
+ if (res) {
295
+ print("✨ Capacitor-updater: Delete failing version: \(curVersionName)")
296
+ }
297
+ } else if (pastVersion != "") {
298
+ print("✨ Capacitor-updater: Validated version: \(curVersionName)")
299
+ let res = implementation.delete(version: pastVersion, versionName: curVersionName)
300
+ if (res) {
301
+ print("✨ Capacitor-updater: Delete past version: \(pastVersionName)")
302
+ }
303
+ UserDefaults.standard.set("", forKey: "pastVersion")
304
+ UserDefaults.standard.set("", forKey: "pastVersionName")
305
+ }
306
+ }
307
+ }
@@ -0,0 +1,28 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>CFBundleDevelopmentRegion</key>
6
+ <string>$(DEVELOPMENT_LANGUAGE)</string>
7
+ <key>CFBundleExecutable</key>
8
+ <string>$(EXECUTABLE_NAME)</string>
9
+ <key>CFBundleIdentifier</key>
10
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
11
+ <key>CFBundleInfoDictionaryVersion</key>
12
+ <string>6.0</string>
13
+ <key>CFBundleName</key>
14
+ <string>$(PRODUCT_NAME)</string>
15
+ <key>CFBundlePackageType</key>
16
+ <string>FMWK</string>
17
+ <key>CFBundleShortVersionString</key>
18
+ <string>1.0</string>
19
+ <key>CFBundleVersion</key>
20
+ <string>$(CURRENT_PROJECT_VERSION)</string>
21
+ <key>NSPrincipalClass</key>
22
+ <string></string>
23
+ <key>UIFileSharingEnabled</key>
24
+ <true/>
25
+ <key>LSSupportsOpeningDocumentsInPlace</key>
26
+ <true/>
27
+ </dict>
28
+ </plist>
package/package.json ADDED
@@ -0,0 +1,85 @@
1
+ {
2
+ "name": "@capgo/capacitor-updater",
3
+ "version": "3.0.3",
4
+ "license": "AGPL-3.0-only",
5
+ "description": "OTA update for capacitor apps",
6
+ "main": "dist/plugin.cjs.js",
7
+ "module": "dist/esm/index.js",
8
+ "types": "dist/esm/index.d.ts",
9
+ "unpkg": "dist/plugin.js",
10
+ "files": [
11
+ "android/src/main/",
12
+ "android/build.gradle",
13
+ "dist/",
14
+ "ios/Plugin/",
15
+ "CapacitorUpdater.podspec"
16
+ ],
17
+ "author": "Martin Donadieu",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/Cap-go/capacitor-updater.git"
21
+ },
22
+ "bugs": {
23
+ "url": "https://github.com/Cap-go/capacitor-updater/issues"
24
+ },
25
+ "keywords": [
26
+ "capacitor",
27
+ "plugin",
28
+ "OTA",
29
+ "manual update",
30
+ "live update",
31
+ "auto update",
32
+ "ionic",
33
+ "appflow alternative",
34
+ "capgo",
35
+ "native"
36
+ ],
37
+ "scripts": {
38
+ "verify": "npm run verify:ios && npm run verify:android && npm run verify:web",
39
+ "verify:ios": "cd ios && pod install && xcodebuild -workspace Plugin.xcworkspace -scheme Plugin && cd ..",
40
+ "verify:android": "cd android && ./gradlew clean build test && cd ..",
41
+ "verify:web": "npm run build",
42
+ "lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint",
43
+ "fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- autocorrect --format",
44
+ "eslint": "eslint . --ext ts",
45
+ "prettier": "prettier \"**/*.{css,html,ts,js,java}\"",
46
+ "swiftlint": "node-swiftlint",
47
+ "docgen": "docgen --api CapacitorUpdaterPlugin --output-readme README.md --output-json dist/docs.json",
48
+ "build": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.js",
49
+ "clean": "rimraf ./dist",
50
+ "watch": "tsc --watch",
51
+ "prepublishOnly": "npm run build"
52
+ },
53
+ "devDependencies": {
54
+ "@capacitor/android": "^3.4.3",
55
+ "@capacitor/core": "^3.4.3",
56
+ "@capacitor/docgen": "^0.1.1",
57
+ "@capacitor/ios": "^3.4.3",
58
+ "@ionic/eslint-config": "^0.3.0",
59
+ "@ionic/prettier-config": "^2.0.0",
60
+ "@ionic/swiftlint-config": "^1.1.2",
61
+ "eslint": "^7.32.0",
62
+ "prettier": "^2.5.1",
63
+ "prettier-plugin-java": "^1.6.1",
64
+ "rimraf": "^3.0.2",
65
+ "rollup": "^2.70.0",
66
+ "swiftlint": "^1.0.1",
67
+ "typescript": "^4.6.2"
68
+ },
69
+ "peerDependencies": {
70
+ "@capacitor/core": "^3.0.0"
71
+ },
72
+ "prettier": "@ionic/prettier-config",
73
+ "swiftlint": "@ionic/swiftlint-config",
74
+ "eslintConfig": {
75
+ "extends": "@ionic/eslint-config/recommended"
76
+ },
77
+ "capacitor": {
78
+ "ios": {
79
+ "src": "ios"
80
+ },
81
+ "android": {
82
+ "src": "android"
83
+ }
84
+ }
85
+ }