@capacitor/background-runner 1.0.0-rc.1
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/CapacitorBackgroundRunner.podspec +17 -0
- package/README.md +310 -0
- package/android/build.gradle +67 -0
- package/android/src/main/AndroidManifest.xml +12 -0
- package/android/src/main/java/io/ionic/backgroundrunner/plugin/BackgroundRunner.kt +166 -0
- package/android/src/main/java/io/ionic/backgroundrunner/plugin/BackgroundRunnerPlugin.kt +102 -0
- package/android/src/main/java/io/ionic/backgroundrunner/plugin/CoroutineHelper.kt +21 -0
- package/android/src/main/java/io/ionic/backgroundrunner/plugin/RunnerConfig.kt +33 -0
- package/android/src/main/java/io/ionic/backgroundrunner/plugin/RunnerWorker.kt +46 -0
- package/android/src/main/java/io/ionic/backgroundrunner/plugin/api/Device.kt +99 -0
- package/android/src/main/java/io/ionic/backgroundrunner/plugin/api/Geolocation.kt +55 -0
- package/android/src/main/java/io/ionic/backgroundrunner/plugin/api/KV.kt +29 -0
- package/android/src/main/java/io/ionic/backgroundrunner/plugin/api/NetworkStatus.kt +10 -0
- package/android/src/main/java/io/ionic/backgroundrunner/plugin/api/Notification.kt +59 -0
- package/android/src/main/java/io/ionic/backgroundrunner/plugin/api/Notifications.kt +158 -0
- package/android/src/main/java/io/ionic/backgroundrunner/plugin/api/TimedNotificationPublisher.kt +40 -0
- package/android/src/main/libs/android-js-engine-release.aar +0 -0
- package/android/src/main/res/.gitkeep +0 -0
- package/dist/docs.json +208 -0
- package/dist/esm/apis.d.ts +52 -0
- package/dist/esm/apis.js +2 -0
- package/dist/esm/apis.js.map +1 -0
- package/dist/esm/definitions.d.ts +82 -0
- package/dist/esm/definitions.js +3 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +5 -0
- package/dist/esm/index.js +8 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/web.d.ts +8 -0
- package/dist/esm/web.js +16 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +32 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +35 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Plugin/BackgroundRunner.swift +223 -0
- package/ios/Plugin/BackgroundRunnerErrors.swift +8 -0
- package/ios/Plugin/BackgroundRunnerPlugin.h +10 -0
- package/ios/Plugin/BackgroundRunnerPlugin.m +11 -0
- package/ios/Plugin/BackgroundRunnerPlugin.swift +161 -0
- package/ios/Plugin/CapacitorAPI/Device.swift +116 -0
- package/ios/Plugin/CapacitorAPI/Geolocation.swift +190 -0
- package/ios/Plugin/CapacitorAPI/KV.swift +22 -0
- package/ios/Plugin/CapacitorAPI/Notifications.swift +165 -0
- package/ios/Plugin/CapacitorAPI/Reachability.swift +395 -0
- package/ios/Plugin/CapacitorAPI/Wearable.swift +48 -0
- package/ios/Plugin/Context+CapacitorAPI.swift +11 -0
- package/ios/Plugin/Info.plist +24 -0
- package/ios/Plugin/RunnerConfig.swift +36 -0
- package/ios/Plugin/RunnerEngine/Context.swift +151 -0
- package/ios/Plugin/RunnerEngine/Errors.swift +8 -0
- package/ios/Plugin/RunnerEngine/JSConsole.swift +38 -0
- package/ios/Plugin/RunnerEngine/JSCrypto.swift +38 -0
- package/ios/Plugin/RunnerEngine/JSError.swift +33 -0
- package/ios/Plugin/RunnerEngine/JSFetch.swift +50 -0
- package/ios/Plugin/RunnerEngine/JSResponse.swift +69 -0
- package/ios/Plugin/RunnerEngine/JSTextDecoder.swift +60 -0
- package/ios/Plugin/RunnerEngine/JSTextEncoder.swift +12 -0
- package/ios/Plugin/RunnerEngine/Runner.swift +23 -0
- package/ios/Plugin/WatchConnectivtySessionDelegate.swift +34 -0
- package/package.json +81 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import Capacitor
|
|
3
|
+
import BackgroundTasks
|
|
4
|
+
import JavaScriptCore
|
|
5
|
+
|
|
6
|
+
public class BackgroundRunner {
|
|
7
|
+
public static let shared = BackgroundRunner()
|
|
8
|
+
|
|
9
|
+
private var runner: Runner?
|
|
10
|
+
private var config: RunnerConfig?
|
|
11
|
+
|
|
12
|
+
private var context: Context?
|
|
13
|
+
|
|
14
|
+
private var started: Bool {
|
|
15
|
+
return runner != nil && context != nil
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
public init() {
|
|
19
|
+
do {
|
|
20
|
+
config = try self.loadRunnerConfig()
|
|
21
|
+
} catch {
|
|
22
|
+
print("could not initialize BackgroundRunner: \(error)")
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public func registerBackgroundTask() {
|
|
27
|
+
guard let config = config else {
|
|
28
|
+
print("no runner to register")
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if config.event == nil {
|
|
33
|
+
print("cannot register background task without a event to call")
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
BGTaskScheduler.shared.register(forTaskWithIdentifier: config.label, using: nil) { task in
|
|
38
|
+
do {
|
|
39
|
+
task.expirationHandler = {
|
|
40
|
+
print("task timed out")
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
guard let event = config.event else {
|
|
44
|
+
throw BackgroundRunnerPluginError.invalidRunnerConfig(reason: "runner event is missing or invalid")
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
_ = try BackgroundRunner.shared.execute(config: config, event: event)
|
|
48
|
+
|
|
49
|
+
task.setTaskCompleted(success: true)
|
|
50
|
+
} catch {
|
|
51
|
+
print("background task error: \(error)")
|
|
52
|
+
task.setTaskCompleted(success: false)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
public func scheduleBackgroundTasks() {
|
|
58
|
+
guard let config = config else {
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
guard let interval = config.interval else {
|
|
63
|
+
print("cannot register background task without a configured interval")
|
|
64
|
+
return
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
let request = BGAppRefreshTaskRequest(identifier: config.label)
|
|
68
|
+
request.earliestBeginDate = Date(timeIntervalSinceNow: Double(interval) * 60)
|
|
69
|
+
|
|
70
|
+
do {
|
|
71
|
+
print("Scheduling \(config.label)")
|
|
72
|
+
|
|
73
|
+
try BGTaskScheduler.shared.submit(request)
|
|
74
|
+
} catch {
|
|
75
|
+
print("Could not schedule app refresh: \(error)")
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
public func start() throws {
|
|
80
|
+
print("starting runner and loading contexts...")
|
|
81
|
+
|
|
82
|
+
guard let config = config else {
|
|
83
|
+
print("...no runner config to start")
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
self.runner = Runner()
|
|
88
|
+
context = try initContext(config: config)
|
|
89
|
+
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
public func stop() {
|
|
93
|
+
print("...stopping runner and removing all contexts")
|
|
94
|
+
guard let runner = runner, let config = config else {
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
runner.destroyContext(name: config.label)
|
|
99
|
+
|
|
100
|
+
context = nil
|
|
101
|
+
self.runner = nil
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
public func getConfig() -> RunnerConfig? {
|
|
105
|
+
return config
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
public func dispatchEvent(event: String, inputArgs: [String: Any]?) throws {
|
|
109
|
+
if !started {
|
|
110
|
+
try start()
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if let config = config {
|
|
114
|
+
let waitGroup = DispatchGroup()
|
|
115
|
+
waitGroup.enter()
|
|
116
|
+
|
|
117
|
+
var err: Error?
|
|
118
|
+
|
|
119
|
+
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
|
|
120
|
+
do {
|
|
121
|
+
_ = try self.execute(config: config, event: event, inputArgs: inputArgs)
|
|
122
|
+
} catch {
|
|
123
|
+
err = error
|
|
124
|
+
print("[\(config.label)]: \(error)")
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
waitGroup.leave()
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
waitGroup.wait()
|
|
131
|
+
|
|
132
|
+
if let err = err {
|
|
133
|
+
throw err
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
public func execute(config: RunnerConfig, event: String, inputArgs: [String: Any]? = nil) throws -> [String: Any]? {
|
|
139
|
+
do {
|
|
140
|
+
if !started {
|
|
141
|
+
try self.start()
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
guard let context = context else {
|
|
145
|
+
throw BackgroundRunnerPluginError.runnerError(reason: "no loaded context for config")
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
var inputArgsWithCallback = inputArgs
|
|
149
|
+
|
|
150
|
+
if inputArgsWithCallback == nil {
|
|
151
|
+
inputArgsWithCallback = [:]
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
let waitGroup = DispatchGroup()
|
|
155
|
+
|
|
156
|
+
var result: [String: Any]?
|
|
157
|
+
|
|
158
|
+
let completedFunc: @convention(block)(JavaScriptCore.JSValue) -> Void = { returnObj in
|
|
159
|
+
if !returnObj.isUndefined && !returnObj.isNull {
|
|
160
|
+
var dict: [String: Any] = [:]
|
|
161
|
+
|
|
162
|
+
returnObj.toDictionary().forEach { key, value in
|
|
163
|
+
dict[String(describing: key)] = value
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
result = dict
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
waitGroup.leave()
|
|
170
|
+
return
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
inputArgsWithCallback?["__ebr::completed"] = completedFunc
|
|
174
|
+
|
|
175
|
+
waitGroup.enter()
|
|
176
|
+
|
|
177
|
+
try context.dispatchEvent(event: event, details: inputArgsWithCallback)
|
|
178
|
+
|
|
179
|
+
waitGroup.wait()
|
|
180
|
+
|
|
181
|
+
return result
|
|
182
|
+
} catch {
|
|
183
|
+
print("error executing task: \(error)")
|
|
184
|
+
throw error
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
private func loadRunnerConfig() throws -> RunnerConfig? {
|
|
189
|
+
guard let capacitorConfigFileURL = Bundle.main.url(forResource: "capacitor.config.json", withExtension: nil) else {
|
|
190
|
+
throw BackgroundRunnerPluginError.runnerError(reason: "capacitor.config.json file not found")
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
let capacitorConfigFileData = try Data(contentsOf: capacitorConfigFileURL)
|
|
194
|
+
|
|
195
|
+
if let capConfig = JSTypes.coerceDictionaryToJSObject(try JSONSerialization.jsonObject(with: capacitorConfigFileData) as? [String: Any]) {
|
|
196
|
+
if let jsonConfig = capConfig[keyPath: "plugins.BackgroundRunner"] as? JSObject {
|
|
197
|
+
return try RunnerConfig(from: jsonConfig)
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return nil
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
private func initContext(config: RunnerConfig) throws -> Context {
|
|
205
|
+
guard let runner = runner else {
|
|
206
|
+
throw BackgroundRunnerPluginError.runnerError(reason: "runner is not initialized")
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
guard let srcFileURL = Bundle.main.url(forResource: config.src, withExtension: nil, subdirectory: "public") else {
|
|
210
|
+
throw BackgroundRunnerPluginError.runnerError(reason: "source file not found")
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
let srcFile = try String(contentsOf: srcFileURL)
|
|
214
|
+
|
|
215
|
+
let context = try runner.createContext(name: config.label)
|
|
216
|
+
|
|
217
|
+
context.setupCapacitorAPI()
|
|
218
|
+
|
|
219
|
+
_ = try context.execute(code: srcFile)
|
|
220
|
+
|
|
221
|
+
return context
|
|
222
|
+
}
|
|
223
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#import <UIKit/UIKit.h>
|
|
2
|
+
|
|
3
|
+
//! Project version number for Plugin.
|
|
4
|
+
FOUNDATION_EXPORT double PluginVersionNumber;
|
|
5
|
+
|
|
6
|
+
//! Project version string for Plugin.
|
|
7
|
+
FOUNDATION_EXPORT const unsigned char PluginVersionString[];
|
|
8
|
+
|
|
9
|
+
// In this header, you should import all the public headers of your framework using statements like #import <Plugin/PublicHeader.h>
|
|
10
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#import <Foundation/Foundation.h>
|
|
2
|
+
#import <Capacitor/Capacitor.h>
|
|
3
|
+
|
|
4
|
+
// Define the plugin using the CAP_PLUGIN Macro, and
|
|
5
|
+
// each method the plugin supports using the CAP_PLUGIN_METHOD macro.
|
|
6
|
+
CAP_PLUGIN(BackgroundRunnerPlugin, "BackgroundRunner",
|
|
7
|
+
CAP_PLUGIN_METHOD(dispatchEvent, CAPPluginReturnPromise);
|
|
8
|
+
CAP_PLUGIN_METHOD(registerBackgroundTask, CAPPluginReturnPromise);
|
|
9
|
+
CAP_PLUGIN_METHOD(checkPermissions, CAPPluginReturnPromise);
|
|
10
|
+
CAP_PLUGIN_METHOD(requestPermissions, CAPPluginReturnPromise);
|
|
11
|
+
)
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import Capacitor
|
|
3
|
+
import WatchConnectivity
|
|
4
|
+
|
|
5
|
+
@objc(BackgroundRunnerPlugin)
|
|
6
|
+
public class BackgroundRunnerPlugin: CAPPlugin {
|
|
7
|
+
private let impl = BackgroundRunner()
|
|
8
|
+
|
|
9
|
+
override public func load() {
|
|
10
|
+
NotificationCenter.default.addObserver(
|
|
11
|
+
self,
|
|
12
|
+
selector: #selector(didEnterBackground),
|
|
13
|
+
name: UIApplication.didEnterBackgroundNotification,
|
|
14
|
+
object: nil
|
|
15
|
+
)
|
|
16
|
+
initWatchConnectivity()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@objc override public func checkPermissions(_ call: CAPPluginCall) {
|
|
20
|
+
// check geolocation permissions
|
|
21
|
+
let geolocationState = CapacitorGeolocation.checkPermission()
|
|
22
|
+
|
|
23
|
+
// check notification permissions
|
|
24
|
+
let notificationState = CapacitorNotifications.checkPermission()
|
|
25
|
+
|
|
26
|
+
call.resolve([
|
|
27
|
+
"geolocation": geolocationState,
|
|
28
|
+
"notifications": notificationState
|
|
29
|
+
])
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@objc override public func requestPermissions(_ call: CAPPluginCall) {
|
|
33
|
+
Task {
|
|
34
|
+
let permissions = call.getArray("apis") as? [String] ?? []
|
|
35
|
+
|
|
36
|
+
do {
|
|
37
|
+
if permissions.contains("notifications") {
|
|
38
|
+
try CapacitorNotifications.requestPermission()
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if permissions.contains("geolocation") {
|
|
42
|
+
let geolocation = CapacitorGeolocation()
|
|
43
|
+
print("geolocation requested...")
|
|
44
|
+
try await geolocation.requestPermission()
|
|
45
|
+
print("geolocation requested...done")
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
self.checkPermissions(call)
|
|
49
|
+
} catch {
|
|
50
|
+
call.reject("\(error)")
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@objc func dispatchEvent(_ call: CAPPluginCall) {
|
|
56
|
+
do {
|
|
57
|
+
guard let runnerEvent = call.getString("event") else {
|
|
58
|
+
throw BackgroundRunnerPluginError.invalidArgument(reason: "event is missing or invalid")
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
let details = call.getObject("details", JSObject())
|
|
62
|
+
|
|
63
|
+
guard let config = impl.getConfig() else {
|
|
64
|
+
throw BackgroundRunnerPluginError.runnerError(reason: "no runner config loaded")
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
|
|
68
|
+
do {
|
|
69
|
+
let result = try self.impl.execute(
|
|
70
|
+
config: config,
|
|
71
|
+
event: runnerEvent,
|
|
72
|
+
inputArgs: details as [String: Any]
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
if let result = result {
|
|
76
|
+
call.resolve(result)
|
|
77
|
+
return
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
call.resolve()
|
|
81
|
+
} catch {
|
|
82
|
+
call.reject("\(error)")
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
} catch {
|
|
86
|
+
call.reject("\(error)")
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
@objc func registerBackgroundTask(_ call: CAPPluginCall) {
|
|
91
|
+
call.resolve()
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@objc private func didEnterBackground() {
|
|
95
|
+
impl.scheduleBackgroundTasks()
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
public static func registerBackgroundTask() {
|
|
99
|
+
BackgroundRunner.shared.registerBackgroundTask()
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
public static func dispatchEvent(event: String, eventArgs: [AnyHashable: Any], completionHandler: ((Result<Bool, Error>) -> Void)) {
|
|
103
|
+
var args: [String: Any] = [:]
|
|
104
|
+
|
|
105
|
+
eventArgs.forEach { (key: AnyHashable, value: Any) in
|
|
106
|
+
if let strKey = key as? String {
|
|
107
|
+
args[strKey] = value
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
do {
|
|
112
|
+
try BackgroundRunner.shared.dispatchEvent(event: event, inputArgs: args)
|
|
113
|
+
completionHandler(.success(true))
|
|
114
|
+
} catch {
|
|
115
|
+
completionHandler(.failure(error))
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
public static func handleApplicationDidFinishLaunching(launchOptions: [UIApplication.LaunchOptionsKey: Any]?) {
|
|
120
|
+
guard let launchOptions = launchOptions else {
|
|
121
|
+
return
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
var events: [String: [String: Any]?] = [:]
|
|
125
|
+
|
|
126
|
+
if launchOptions[.location] != nil {
|
|
127
|
+
// application was launched for a location event
|
|
128
|
+
var details: [String: Any] = [:]
|
|
129
|
+
details["locations"] = nil
|
|
130
|
+
|
|
131
|
+
if let location = CapacitorGeolocation.getCurrentKnownLocation() {
|
|
132
|
+
var details: [String: Any] = [:]
|
|
133
|
+
details["locations"] = [location]
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
events["currentLocation"] = details
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
print("emit events: \(events)")
|
|
140
|
+
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private func initWatchConnectivity() {
|
|
144
|
+
if !WCSession.isSupported() {
|
|
145
|
+
return
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
guard let config = impl.getConfig() else {
|
|
149
|
+
return
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if !config.enableWatchConnectivity {
|
|
153
|
+
return
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
WCSession.default.delegate = self
|
|
157
|
+
WCSession.default.activate()
|
|
158
|
+
|
|
159
|
+
print("Watch Connectivity Enabled")
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import UIKit
|
|
3
|
+
import JavaScriptCore
|
|
4
|
+
|
|
5
|
+
@objc protocol CapacitorDeviceExports: JSExport {
|
|
6
|
+
func getNetworkStatus() -> [String: Any]?
|
|
7
|
+
func getBatteryStatus() -> [String: Any]
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
enum CapacitorDeviceErrors: Error, Equatable {
|
|
11
|
+
case unknownError(reason: String)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
class CapacitorDevice: NSObject, CapacitorDeviceExports {
|
|
15
|
+
var reachability: Reachability?
|
|
16
|
+
var initError: Error?
|
|
17
|
+
|
|
18
|
+
enum Connection {
|
|
19
|
+
case unavailable, wifi, cellular
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
override init() {
|
|
23
|
+
super.init()
|
|
24
|
+
|
|
25
|
+
do {
|
|
26
|
+
reachability = try Reachability()
|
|
27
|
+
} catch {
|
|
28
|
+
initError = error
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
private func checkError() throws {
|
|
33
|
+
if let err = initError {
|
|
34
|
+
throw CapacitorDeviceErrors.unknownError(reason: "\(err)")
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if reachability == nil {
|
|
38
|
+
throw CapacitorDeviceErrors.unknownError(reason: "reachability failed to initialize")
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
func getNetworkStatus() -> [String: Any]? {
|
|
43
|
+
do {
|
|
44
|
+
try checkError()
|
|
45
|
+
|
|
46
|
+
let status = reachability?.connection.equivalentEnum ?? Connection.unavailable
|
|
47
|
+
|
|
48
|
+
var networkStatusDict: [String: Any] = [:]
|
|
49
|
+
networkStatusDict["connected"] = status.isConnected
|
|
50
|
+
networkStatusDict["connectionType"] = status.jsStringValue
|
|
51
|
+
|
|
52
|
+
return networkStatusDict
|
|
53
|
+
} catch {
|
|
54
|
+
JSContext.current().exception = JSValue(newErrorFromMessage: "\(error)", in: JSContext.current())
|
|
55
|
+
return nil
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
func getBatteryStatus() -> [String: Any] {
|
|
60
|
+
UIDevice.current.isBatteryMonitoringEnabled = true
|
|
61
|
+
|
|
62
|
+
var batteryInfoDict: [String: Any] = [:]
|
|
63
|
+
batteryInfoDict["batteryLevel"] = UIDevice.current.batteryLevel
|
|
64
|
+
batteryInfoDict["isCharging"] = UIDevice.current.batteryState == .charging || UIDevice.current.batteryState == .full
|
|
65
|
+
|
|
66
|
+
UIDevice.current.isBatteryMonitoringEnabled = false
|
|
67
|
+
|
|
68
|
+
return batteryInfoDict
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
fileprivate extension Reachability.Connection {
|
|
73
|
+
var equivalentEnum: CapacitorDevice.Connection {
|
|
74
|
+
switch self {
|
|
75
|
+
case .unavailable:
|
|
76
|
+
return .unavailable
|
|
77
|
+
case .wifi:
|
|
78
|
+
return .wifi
|
|
79
|
+
case .cellular:
|
|
80
|
+
return .cellular
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
extension CapacitorDevice.Connection {
|
|
86
|
+
internal var jsStringValue: String {
|
|
87
|
+
switch self {
|
|
88
|
+
case .cellular:
|
|
89
|
+
return "cellular"
|
|
90
|
+
case .wifi:
|
|
91
|
+
return "wifi"
|
|
92
|
+
case .unavailable:
|
|
93
|
+
return "none"
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
internal var isConnected: Bool {
|
|
98
|
+
switch self {
|
|
99
|
+
case .cellular, .wifi:
|
|
100
|
+
return true
|
|
101
|
+
case .unavailable:
|
|
102
|
+
return false
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
internal var logMessage: String {
|
|
107
|
+
switch self {
|
|
108
|
+
case .cellular:
|
|
109
|
+
return "Reachable via Cellular"
|
|
110
|
+
case .wifi:
|
|
111
|
+
return "Reachable via WiFi"
|
|
112
|
+
case .unavailable:
|
|
113
|
+
return "Not reachable"
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|