@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.
Files changed (61) hide show
  1. package/CapacitorBackgroundRunner.podspec +17 -0
  2. package/README.md +310 -0
  3. package/android/build.gradle +67 -0
  4. package/android/src/main/AndroidManifest.xml +12 -0
  5. package/android/src/main/java/io/ionic/backgroundrunner/plugin/BackgroundRunner.kt +166 -0
  6. package/android/src/main/java/io/ionic/backgroundrunner/plugin/BackgroundRunnerPlugin.kt +102 -0
  7. package/android/src/main/java/io/ionic/backgroundrunner/plugin/CoroutineHelper.kt +21 -0
  8. package/android/src/main/java/io/ionic/backgroundrunner/plugin/RunnerConfig.kt +33 -0
  9. package/android/src/main/java/io/ionic/backgroundrunner/plugin/RunnerWorker.kt +46 -0
  10. package/android/src/main/java/io/ionic/backgroundrunner/plugin/api/Device.kt +99 -0
  11. package/android/src/main/java/io/ionic/backgroundrunner/plugin/api/Geolocation.kt +55 -0
  12. package/android/src/main/java/io/ionic/backgroundrunner/plugin/api/KV.kt +29 -0
  13. package/android/src/main/java/io/ionic/backgroundrunner/plugin/api/NetworkStatus.kt +10 -0
  14. package/android/src/main/java/io/ionic/backgroundrunner/plugin/api/Notification.kt +59 -0
  15. package/android/src/main/java/io/ionic/backgroundrunner/plugin/api/Notifications.kt +158 -0
  16. package/android/src/main/java/io/ionic/backgroundrunner/plugin/api/TimedNotificationPublisher.kt +40 -0
  17. package/android/src/main/libs/android-js-engine-release.aar +0 -0
  18. package/android/src/main/res/.gitkeep +0 -0
  19. package/dist/docs.json +208 -0
  20. package/dist/esm/apis.d.ts +52 -0
  21. package/dist/esm/apis.js +2 -0
  22. package/dist/esm/apis.js.map +1 -0
  23. package/dist/esm/definitions.d.ts +82 -0
  24. package/dist/esm/definitions.js +3 -0
  25. package/dist/esm/definitions.js.map +1 -0
  26. package/dist/esm/index.d.ts +5 -0
  27. package/dist/esm/index.js +8 -0
  28. package/dist/esm/index.js.map +1 -0
  29. package/dist/esm/web.d.ts +8 -0
  30. package/dist/esm/web.js +16 -0
  31. package/dist/esm/web.js.map +1 -0
  32. package/dist/plugin.cjs.js +32 -0
  33. package/dist/plugin.cjs.js.map +1 -0
  34. package/dist/plugin.js +35 -0
  35. package/dist/plugin.js.map +1 -0
  36. package/ios/Plugin/BackgroundRunner.swift +223 -0
  37. package/ios/Plugin/BackgroundRunnerErrors.swift +8 -0
  38. package/ios/Plugin/BackgroundRunnerPlugin.h +10 -0
  39. package/ios/Plugin/BackgroundRunnerPlugin.m +11 -0
  40. package/ios/Plugin/BackgroundRunnerPlugin.swift +161 -0
  41. package/ios/Plugin/CapacitorAPI/Device.swift +116 -0
  42. package/ios/Plugin/CapacitorAPI/Geolocation.swift +190 -0
  43. package/ios/Plugin/CapacitorAPI/KV.swift +22 -0
  44. package/ios/Plugin/CapacitorAPI/Notifications.swift +165 -0
  45. package/ios/Plugin/CapacitorAPI/Reachability.swift +395 -0
  46. package/ios/Plugin/CapacitorAPI/Wearable.swift +48 -0
  47. package/ios/Plugin/Context+CapacitorAPI.swift +11 -0
  48. package/ios/Plugin/Info.plist +24 -0
  49. package/ios/Plugin/RunnerConfig.swift +36 -0
  50. package/ios/Plugin/RunnerEngine/Context.swift +151 -0
  51. package/ios/Plugin/RunnerEngine/Errors.swift +8 -0
  52. package/ios/Plugin/RunnerEngine/JSConsole.swift +38 -0
  53. package/ios/Plugin/RunnerEngine/JSCrypto.swift +38 -0
  54. package/ios/Plugin/RunnerEngine/JSError.swift +33 -0
  55. package/ios/Plugin/RunnerEngine/JSFetch.swift +50 -0
  56. package/ios/Plugin/RunnerEngine/JSResponse.swift +69 -0
  57. package/ios/Plugin/RunnerEngine/JSTextDecoder.swift +60 -0
  58. package/ios/Plugin/RunnerEngine/JSTextEncoder.swift +12 -0
  59. package/ios/Plugin/RunnerEngine/Runner.swift +23 -0
  60. package/ios/Plugin/WatchConnectivtySessionDelegate.swift +34 -0
  61. package/package.json +81 -0
@@ -0,0 +1,190 @@
1
+ import Foundation
2
+ import JavaScriptCore
3
+ import CoreLocation
4
+
5
+ @objc protocol CapacitorGeolocationExports: JSExport {
6
+ var isWatchingPosition: Bool { get }
7
+ func startWatchingPosition(_ highAccuracy: Bool)
8
+ func stopWatchingPosition()
9
+ func getCurrentPosition() -> [String: Any]?
10
+ }
11
+
12
+ enum CapacitorGeolocationErrors: Error, Equatable {
13
+ case unauthorized
14
+ }
15
+
16
+ class CapacitorGeolocation: NSObject, CapacitorGeolocationExports, CLLocationManagerDelegate {
17
+ private weak var context: Context?
18
+ private var pendingCurrentLocationCalls: [Int: (Result<CLLocation?, Error>) -> Void] = [:]
19
+ private var isWatchingLocation: Bool = false
20
+ private let locationManager: CLLocationManager
21
+
22
+ private var permissionContinuation: CheckedContinuation<Void, Error>?
23
+
24
+ var isWatchingPosition: Bool {
25
+ return isWatchingLocation
26
+ }
27
+
28
+ init(context: Context? = nil) {
29
+ self.locationManager = CLLocationManager()
30
+ super.init()
31
+
32
+ self.context = context
33
+ self.locationManager.delegate = self
34
+
35
+ self.locationManager.allowsBackgroundLocationUpdates = true
36
+ self.locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
37
+ self.locationManager.pausesLocationUpdatesAutomatically = false
38
+ }
39
+
40
+ static func getCurrentKnownLocation() -> CLLocation? {
41
+ return CLLocationManager().location
42
+ }
43
+
44
+ static func checkPermission() -> String {
45
+ if !CLLocationManager.locationServicesEnabled() {
46
+ return "denied"
47
+ }
48
+
49
+ var permission: String = "prompt"
50
+
51
+ switch CLLocationManager.authorizationStatus() {
52
+ case .authorized, .authorizedWhenInUse, .authorizedAlways:
53
+ permission = "granted"
54
+ case .notDetermined:
55
+ permission = "prompt"
56
+ case .denied, .restricted:
57
+ permission = "denied"
58
+ default:
59
+ permission = "prompt"
60
+ }
61
+
62
+ return permission
63
+ }
64
+
65
+ func requestPermission() async throws {
66
+ if !CLLocationManager.locationServicesEnabled() {
67
+ return
68
+ }
69
+
70
+ if CLLocationManager.authorizationStatus() != .notDetermined {
71
+ return
72
+ }
73
+
74
+ return try await withCheckedThrowingContinuation({ (continuation: CheckedContinuation<Void, Error>) in
75
+ permissionContinuation = continuation
76
+
77
+ DispatchQueue.main.async {
78
+ self.locationManager.requestAlwaysAuthorization()
79
+ }
80
+ })
81
+ }
82
+
83
+ func startWatchingPosition(_ highAccuracy: Bool) {
84
+ do {
85
+ if CapacitorGeolocation.checkPermission() != "granted" {
86
+ throw CapacitorGeolocationErrors.unauthorized
87
+ }
88
+
89
+ DispatchQueue.main.sync {
90
+ if highAccuracy {
91
+ self.locationManager.startUpdatingLocation()
92
+ } else {
93
+ self.locationManager.startMonitoringSignificantLocationChanges()
94
+ }
95
+ }
96
+
97
+ self.isWatchingLocation = true
98
+ } catch {
99
+ JSContext.current().exception = JSValue(newErrorFromMessage: "\(error)", in: JSContext.current())
100
+ }
101
+ }
102
+
103
+ func stopWatchingPosition() {
104
+ DispatchQueue.main.sync {
105
+ self.locationManager.stopUpdatingLocation()
106
+ self.locationManager.stopMonitoringSignificantLocationChanges()
107
+ }
108
+
109
+ self.isWatchingLocation = false
110
+ }
111
+
112
+ func getCurrentPosition() -> [String: Any]? {
113
+ do {
114
+ if CapacitorGeolocation.checkPermission() != "granted" {
115
+ throw CapacitorGeolocationErrors.unauthorized
116
+ }
117
+
118
+ guard let lastLocation = self.locationManager.location else {
119
+ return nil
120
+ }
121
+
122
+ return buildLocationDict(location: lastLocation)
123
+ } catch {
124
+ JSContext.current().exception = JSValue(newErrorFromMessage: "\(error)", in: JSContext.current())
125
+
126
+ return nil
127
+ }
128
+ }
129
+
130
+ func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
131
+ if let context = self.context, self.isWatchingLocation {
132
+ let locations: [[String: Any]] = locations.map { location in
133
+ return buildLocationDict(location: location)
134
+ }
135
+
136
+ var details: [String: Any] = [:]
137
+ details["locations"] = locations
138
+
139
+ try? context.dispatchEvent(event: "currentLocation", details: details)
140
+ }
141
+
142
+ pendingCurrentLocationCalls.forEach { (callId: Int, callback: (Result<CLLocation?, Error>) -> Void) in
143
+ pendingCurrentLocationCalls.removeValue(forKey: callId)
144
+ callback(.success(locations.last))
145
+ }
146
+
147
+ if let location = locations.last {
148
+ print("\(location.coordinate.latitude),\(location.coordinate.longitude)")
149
+ }
150
+ }
151
+
152
+ func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
153
+ if let continuation = permissionContinuation {
154
+ continuation.resume(throwing: error)
155
+ permissionContinuation = nil
156
+ }
157
+
158
+ if let context = context, self.isWatchingLocation {
159
+ var details: [String: Any] = [:]
160
+ details["error"] = error
161
+
162
+ try? context.dispatchEvent(event: "currentLocation", details: details)
163
+ }
164
+
165
+ pendingCurrentLocationCalls.forEach { (callId: Int, callback: (Result<CLLocation?, Error>) -> Void) in
166
+ pendingCurrentLocationCalls.removeValue(forKey: callId)
167
+ callback(.failure(error))
168
+ }
169
+ }
170
+
171
+ func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
172
+ if let continuation = permissionContinuation {
173
+ continuation.resume()
174
+ permissionContinuation = nil
175
+ }
176
+ }
177
+
178
+ private func buildLocationDict(location: CLLocation) -> [String: Any] {
179
+ var coords: [String: Any] = [:]
180
+ coords["latitude"] = location.coordinate.latitude
181
+ coords["longitude"] = location.coordinate.longitude
182
+ coords["accuracy"] = location.horizontalAccuracy
183
+ coords["altitude"] = location.altitude
184
+ coords["altitudeAccuracy"] = location.verticalAccuracy
185
+ coords["speed"] = location.speed
186
+ coords["heading"] = location.course
187
+
188
+ return coords
189
+ }
190
+ }
@@ -0,0 +1,22 @@
1
+ import Foundation
2
+ import JavaScriptCore
3
+
4
+ @objc protocol CapacitorKVStoreExports: JSExport {
5
+ static func set(_ key: String, _ value: String)
6
+ static func get(_ key: String) -> String?
7
+ static func remove(_ key: String)
8
+ }
9
+
10
+ class CapacitorKVStore: NSObject, CapacitorKVStoreExports {
11
+ static func set(_ key: String, _ value: String) {
12
+ UserDefaults.standard.set(value, forKey: key)
13
+ }
14
+
15
+ static func get(_ key: String) -> String? {
16
+ return UserDefaults.standard.string(forKey: key)
17
+ }
18
+
19
+ static func remove(_ key: String) {
20
+ return UserDefaults.standard.removeObject(forKey: key)
21
+ }
22
+ }
@@ -0,0 +1,165 @@
1
+ import Foundation
2
+ import JavaScriptCore
3
+ import UserNotifications
4
+
5
+ enum CapacitorNotificationsErrors: Error, Equatable {
6
+ case invalidOptions(reason: String)
7
+ case unknownError(reason: String)
8
+ case permissionDenied
9
+ }
10
+
11
+ struct NotificationOption {
12
+ let id: Int
13
+ let title: String
14
+ let body: String
15
+ let scheduleAt: Date
16
+ let sound: String?
17
+ let actionTypeId: String?
18
+ let extra: [String: Any]?
19
+ let threadIdentifier: String?
20
+ let summaryArgument: String?
21
+ let groupSummary: String?
22
+
23
+ init(from dict: [String: Any?]) throws {
24
+ guard let id = dict["id"] as? Int else {
25
+ throw CapacitorNotificationsErrors.invalidOptions(reason: "notification id is required")
26
+ }
27
+
28
+ guard let title = dict["title"] as? String else {
29
+ throw CapacitorNotificationsErrors.invalidOptions(reason: "notification title is required")
30
+ }
31
+
32
+ guard let body = dict["body"] as? String else {
33
+ throw CapacitorNotificationsErrors.invalidOptions(reason: "notification body is required")
34
+ }
35
+
36
+ guard let scheduleAt = dict["scheduleAt"] as? Date else {
37
+ throw CapacitorNotificationsErrors.invalidOptions(reason: "notification schedule date is required")
38
+ }
39
+
40
+ self.id = id
41
+ self.title = title
42
+ self.body = body
43
+ self.scheduleAt = scheduleAt
44
+ self.threadIdentifier = dict["threadIdentifier"] as? String
45
+ self.sound = dict["sound"] as? String
46
+ self.actionTypeId = dict["actionTypeId"] as? String
47
+ self.extra = dict["extra"] as? [String: Any]
48
+ self.summaryArgument = dict["summaryArgument"] as? String
49
+ self.groupSummary = dict["groupSummary"] as? String
50
+ }
51
+ }
52
+
53
+ @objc protocol CapacitorNotificationsExports: JSExport {
54
+ static func schedule(_ options: JSValue)
55
+ }
56
+
57
+ class CapacitorNotifications: NSObject, CapacitorNotificationsExports {
58
+ static func checkPermission() -> String {
59
+ let group = DispatchGroup()
60
+ var permission: String = "prompt"
61
+
62
+ group.enter()
63
+ UNUserNotificationCenter.current().getNotificationSettings { settings in
64
+ switch settings.authorizationStatus {
65
+ case .authorized, .ephemeral, .provisional:
66
+ permission = "granted"
67
+ case .denied:
68
+ permission = "denied"
69
+ case .notDetermined:
70
+ permission = "prompt"
71
+ @unknown default:
72
+ permission = "prompt"
73
+ }
74
+ group.leave()
75
+ }
76
+
77
+ group.wait()
78
+
79
+ return permission
80
+ }
81
+
82
+ static func requestPermission() throws {
83
+ var permissionsError: CapacitorNotificationsErrors?
84
+
85
+ let group = DispatchGroup()
86
+
87
+ group.enter()
88
+ UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { _, error in
89
+ if let error = error {
90
+ permissionsError = CapacitorNotificationsErrors.unknownError(reason: "\(error)")
91
+ }
92
+
93
+ group.leave()
94
+ }
95
+
96
+ group.wait()
97
+
98
+ if let err = permissionsError {
99
+ throw err
100
+ }
101
+ }
102
+
103
+ static func schedule(_ options: JSValue) {
104
+ do {
105
+ if CapacitorNotifications.checkPermission() != "granted" {
106
+ throw CapacitorNotificationsErrors.permissionDenied
107
+ }
108
+
109
+ if options.isUndefined || options.isNull {
110
+ throw CapacitorNotificationsErrors.invalidOptions(reason: "options are null")
111
+ }
112
+
113
+ if !options.isArray {
114
+ throw CapacitorNotificationsErrors.invalidOptions(reason: "options must be an array")
115
+ }
116
+
117
+ if let notificationOptions = options.toArray() as? [[String: Any?]] {
118
+ for option in notificationOptions {
119
+ let notificationOption = try NotificationOption(from: option)
120
+
121
+ let content = UNMutableNotificationContent()
122
+ content.title = NSString.localizedUserNotificationString(forKey: notificationOption.title, arguments: nil)
123
+ content.body = NSString.localizedUserNotificationString(forKey: notificationOption.body, arguments: nil)
124
+ content.userInfo = [:]
125
+
126
+ if let extra = notificationOption.extra {
127
+ content.userInfo["cap_extra"] = extra
128
+ }
129
+
130
+ if let actionTypeId = notificationOption.actionTypeId {
131
+ content.categoryIdentifier = actionTypeId
132
+ }
133
+
134
+ if let threadIdentifier = notificationOption.threadIdentifier {
135
+ content.threadIdentifier = threadIdentifier
136
+ }
137
+
138
+ if let summaryArgument = notificationOption.summaryArgument {
139
+ content.summaryArgument = summaryArgument
140
+ }
141
+
142
+ if let sound = notificationOption.sound {
143
+ content.sound = UNNotificationSound(named: UNNotificationSoundName(sound))
144
+ }
145
+
146
+ var notificationDate = notificationOption.scheduleAt
147
+
148
+ if notificationDate < Date() {
149
+ notificationDate = Date()
150
+ }
151
+
152
+ let dateInfo = Calendar.current.dateComponents(in: TimeZone.current, from: notificationDate)
153
+ let dateInterval = DateInterval(start: Date(), end: dateInfo.date!)
154
+
155
+ let trigger = UNTimeIntervalNotificationTrigger(timeInterval: dateInterval.duration, repeats: false)
156
+ let request = UNNotificationRequest(identifier: "\(notificationOption.id)", content: content, trigger: trigger)
157
+
158
+ UNUserNotificationCenter.current().add(request)
159
+ }
160
+ }
161
+ } catch {
162
+ JSContext.current().exception = JSValue(newErrorFromMessage: "\(error)", in: JSContext.current())
163
+ }
164
+ }
165
+ }