react-native-bg-geolocation 0.2.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 (142) hide show
  1. package/BgGeolocation.podspec +39 -0
  2. package/LICENSE +20 -0
  3. package/README.md +366 -0
  4. package/android/build.gradle +69 -0
  5. package/android/src/main/AndroidManifest.xml +53 -0
  6. package/android/src/main/java/com/bggeolocation/BgGeolocationActivityRecognitionReceiver.kt +116 -0
  7. package/android/src/main/java/com/bggeolocation/BgGeolocationBootReceiver.kt +44 -0
  8. package/android/src/main/java/com/bggeolocation/BgGeolocationForegroundService.kt +373 -0
  9. package/android/src/main/java/com/bggeolocation/BgGeolocationGeofenceReceiver.kt +55 -0
  10. package/android/src/main/java/com/bggeolocation/BgGeolocationHeadlessTask.kt +138 -0
  11. package/android/src/main/java/com/bggeolocation/BgGeolocationModule.kt +1030 -0
  12. package/android/src/main/java/com/bggeolocation/BgGeolocationMotionStateMachine.kt +159 -0
  13. package/android/src/main/java/com/bggeolocation/BgGeolocationPackage.kt +31 -0
  14. package/android/src/main/res/drawable/bg_geo_notification.xml +9 -0
  15. package/ios/BgGeolocation.h +14 -0
  16. package/ios/BgGeolocation.mm +709 -0
  17. package/ios/engine/AtomicBoolean.swift +48 -0
  18. package/ios/engine/BGActivityChangeEvent.swift +20 -0
  19. package/ios/engine/BGActivityConfig.swift +71 -0
  20. package/ios/engine/BGAppConfig.swift +92 -0
  21. package/ios/engine/BGAppState.swift +147 -0
  22. package/ios/engine/BGAuthorization.swift +85 -0
  23. package/ios/engine/BGAuthorizationAlertPresenter.swift +39 -0
  24. package/ios/engine/BGAuthorizationConfig.swift +50 -0
  25. package/ios/engine/BGAuthorizationEvent.swift +40 -0
  26. package/ios/engine/BGBackgroundTaskManager.swift +143 -0
  27. package/ios/engine/BGCLRouter.swift +101 -0
  28. package/ios/engine/BGCallback.swift +19 -0
  29. package/ios/engine/BGConfig.swift +440 -0
  30. package/ios/engine/BGConfigModuleBase.swift +180 -0
  31. package/ios/engine/BGConfigOLD.swift +582 -0
  32. package/ios/engine/BGConnectivityChangeEvent.swift +15 -0
  33. package/ios/engine/BGCrashDetector.swift +122 -0
  34. package/ios/engine/BGCurrentPositionRequest.swift +87 -0
  35. package/ios/engine/BGDataStore.swift +75 -0
  36. package/ios/engine/BGDatabase.swift +677 -0
  37. package/ios/engine/BGDatabasePool.swift +220 -0
  38. package/ios/engine/BGDatabaseQueue.swift +215 -0
  39. package/ios/engine/BGDateUtils.swift +26 -0
  40. package/ios/engine/BGDeviceInfo.swift +54 -0
  41. package/ios/engine/BGDeviceManager.swift +65 -0
  42. package/ios/engine/BGEnabledChangeEvent.swift +11 -0
  43. package/ios/engine/BGEnv.swift +17 -0
  44. package/ios/engine/BGEventBus.swift +83 -0
  45. package/ios/engine/BGEventManager.swift +169 -0
  46. package/ios/engine/BGEventNames.swift +51 -0
  47. package/ios/engine/BGGeofence.swift +233 -0
  48. package/ios/engine/BGGeofenceDAO.swift +152 -0
  49. package/ios/engine/BGGeofenceEvent.swift +42 -0
  50. package/ios/engine/BGGeofenceLocationRequest.swift +94 -0
  51. package/ios/engine/BGGeofenceManager.swift +315 -0
  52. package/ios/engine/BGGeofenceTransition.swift +97 -0
  53. package/ios/engine/BGGeofencesChangeEvent.swift +26 -0
  54. package/ios/engine/BGGeolocationConfig.swift +136 -0
  55. package/ios/engine/BGHeartbeatEvent.swift +31 -0
  56. package/ios/engine/BGHeartbeatService.swift +51 -0
  57. package/ios/engine/BGHttpConfig.swift +105 -0
  58. package/ios/engine/BGHttpErrorCodes.swift +63 -0
  59. package/ios/engine/BGHttpEvent.swift +34 -0
  60. package/ios/engine/BGHttpRequest.swift +126 -0
  61. package/ios/engine/BGHttpResponse.swift +93 -0
  62. package/ios/engine/BGHttpService.swift +428 -0
  63. package/ios/engine/BGKalmanFilter.swift +105 -0
  64. package/ios/engine/BGLMActionNames.swift +55 -0
  65. package/ios/engine/BGLicenseManager.swift +26 -0
  66. package/ios/engine/BGLiveActivityManager.swift +327 -0
  67. package/ios/engine/BGLocation.swift +311 -0
  68. package/ios/engine/BGLocationAuthorization.swift +427 -0
  69. package/ios/engine/BGLocationDAO.swift +252 -0
  70. package/ios/engine/BGLocationErrors.swift +28 -0
  71. package/ios/engine/BGLocationEvent.swift +43 -0
  72. package/ios/engine/BGLocationFilter.swift +82 -0
  73. package/ios/engine/BGLocationFilterConfig.swift +57 -0
  74. package/ios/engine/BGLocationHelper.swift +54 -0
  75. package/ios/engine/BGLocationManager.swift +662 -0
  76. package/ios/engine/BGLocationMetricsEngine.swift +116 -0
  77. package/ios/engine/BGLocationRequestService.swift +459 -0
  78. package/ios/engine/BGLocationSatisfier.swift +14 -0
  79. package/ios/engine/BGLocationStreamEvent.swift +27 -0
  80. package/ios/engine/BGLog.swift +337 -0
  81. package/ios/engine/BGLogLevel.swift +26 -0
  82. package/ios/engine/BGLoggerConfig.swift +60 -0
  83. package/ios/engine/BGMotionActivity.swift +31 -0
  84. package/ios/engine/BGMotionActivityClassifier.swift +108 -0
  85. package/ios/engine/BGMotionActivityManagerAdapter.swift +40 -0
  86. package/ios/engine/BGMotionActivitySource.swift +46 -0
  87. package/ios/engine/BGMotionDetector.swift +377 -0
  88. package/ios/engine/BGMotionPermissionManager.swift +50 -0
  89. package/ios/engine/BGNativeLogger.swift +48 -0
  90. package/ios/engine/BGNotificaitons.swift +37 -0
  91. package/ios/engine/BGOdometer.swift +66 -0
  92. package/ios/engine/BGPersistenceConfig.swift +29 -0
  93. package/ios/engine/BGPolygonStreamRequest.swift +48 -0
  94. package/ios/engine/BGPowerSaveChangeEvent.swift +12 -0
  95. package/ios/engine/BGPropertySpec.swift +29 -0
  96. package/ios/engine/BGProviderChangeEvent.swift +31 -0
  97. package/ios/engine/BGQueue.swift +50 -0
  98. package/ios/engine/BGRPC.swift +194 -0
  99. package/ios/engine/BGReachability.swift +58 -0
  100. package/ios/engine/BGResultSet.swift +157 -0
  101. package/ios/engine/BGSchedule.swift +228 -0
  102. package/ios/engine/BGScheduleEvent.swift +13 -0
  103. package/ios/engine/BGScheduler.swift +116 -0
  104. package/ios/engine/BGSingleLocationRequest.swift +49 -0
  105. package/ios/engine/BGStreamLocationRequest.swift +42 -0
  106. package/ios/engine/BGTemplate.swift +54 -0
  107. package/ios/engine/BGTimerService.swift +46 -0
  108. package/ios/engine/BGTrackingAudioManager.swift +286 -0
  109. package/ios/engine/BGTrackingService.swift +879 -0
  110. package/ios/engine/BGWatchPositionRequest.swift +63 -0
  111. package/ios/engine/DatabaseQueue.swift +47 -0
  112. package/ios/engine/LogQuery.swift +10 -0
  113. package/ios/engine/SQLQuery.swift +65 -0
  114. package/ios/engine/TransistorAuthorizationToken.swift +182 -0
  115. package/ios/liveactivity/BGLiveTrackingAttributes.swift +52 -0
  116. package/ios/locationpush/BGLocationPushDeliverer.swift +260 -0
  117. package/ios/locationpush/BGLocationPushService.swift +161 -0
  118. package/ios/locationpush/BGLocationPushShared.swift +98 -0
  119. package/ios/locationpush/BGLocationPushSocketClient.swift +198 -0
  120. package/lib/module/NativeBgGeolocation.js +5 -0
  121. package/lib/module/NativeBgGeolocation.js.map +1 -0
  122. package/lib/module/events.js +20 -0
  123. package/lib/module/events.js.map +1 -0
  124. package/lib/module/index.js +706 -0
  125. package/lib/module/index.js.map +1 -0
  126. package/lib/module/package.json +1 -0
  127. package/lib/module/types.js +2 -0
  128. package/lib/module/types.js.map +1 -0
  129. package/lib/typescript/package.json +1 -0
  130. package/lib/typescript/src/NativeBgGeolocation.d.ts +57 -0
  131. package/lib/typescript/src/NativeBgGeolocation.d.ts.map +1 -0
  132. package/lib/typescript/src/events.d.ts +18 -0
  133. package/lib/typescript/src/events.d.ts.map +1 -0
  134. package/lib/typescript/src/index.d.ts +238 -0
  135. package/lib/typescript/src/index.d.ts.map +1 -0
  136. package/lib/typescript/src/types.d.ts +229 -0
  137. package/lib/typescript/src/types.d.ts.map +1 -0
  138. package/package.json +141 -0
  139. package/src/NativeBgGeolocation.ts +236 -0
  140. package/src/events.ts +17 -0
  141. package/src/index.tsx +935 -0
  142. package/src/types.ts +254 -0
@@ -0,0 +1,662 @@
1
+ import Foundation
2
+ import CoreLocation
3
+ import UIKit
4
+
5
+ @objc public class BGLocationManager: NSObject {
6
+
7
+ private static var _sharedInstance: BGLocationManager?
8
+ private static let instanceLock = NSLock()
9
+
10
+ @objc public class func sharedInstance() -> BGLocationManager {
11
+ instanceLock.lock()
12
+ defer { instanceLock.unlock() }
13
+ if _sharedInstance == nil { _sharedInstance = BGLocationManager() }
14
+ return _sharedInstance!
15
+ }
16
+
17
+ // MARK: - State
18
+
19
+ @objc public var viewController: UIViewController?
20
+ @objc public var beforeInsertBlock: ((BGLocation) -> BGLocation?)?
21
+
22
+ private var locationManager: CLLocationManager?
23
+ private var configChangeBufferTimer: Timer?
24
+ private var isReady: Bool = false
25
+ private let setupQueue = DispatchQueue(label: "BGLocationManager.setup")
26
+
27
+ @objc public override init() {
28
+ super.init()
29
+ setupCoreLocation()
30
+ }
31
+
32
+ private func setupCoreLocation() {
33
+ let configure = {
34
+ let mgr = CLLocationManager()
35
+ self.locationManager = mgr
36
+ BGLocationAuthorization.configureShared(withLocationManager: mgr)
37
+ BGLocationRequestService.configureShared(withLocationManager: mgr)
38
+ BGTrackingService.sharedInstance().locationManager = mgr
39
+ BGGeofenceManager.sharedInstance().locationManager = mgr
40
+ // BGScheduler does not hold a locationManager reference
41
+
42
+ // Configure the shared manager the instant it exists —
43
+ // allowsBackgroundLocationUpdates DEFAULTS TO false, and iOS refuses
44
+ // background delivery / pauses GPS without it. Doing this here (not
45
+ // only in start()) means the kill-state auto-resume path and any
46
+ // direct SLC/region arming get a correctly-configured manager.
47
+ BGTrackingService.sharedInstance().configureLocationManager(mgr)
48
+
49
+ // A CLLocationManager has exactly one delegate. Route every callback
50
+ // through BGCLRouter, which fans out to the services above. This MUST
51
+ // be the only place `mgr.delegate` is assigned.
52
+ mgr.delegate = BGCLRouter.sharedInstance()
53
+
54
+ // Kill-state / cold-boot auto-resume. On an iOS background relaunch
55
+ // (significant-change or region exit after system termination) JS may never
56
+ // call ready(), so the engine must re-arm monitoring itself or the OS
57
+ // wake is wasted and nothing is delivered. Only resume when tracking
58
+ // was persisted enabled (or startOnBoot fired) — i.e. the user had
59
+ // tracking on and didn't stop it. ready() is idempotent (guarded by
60
+ // isReady), so a later JS ready() is a no-op for start.
61
+ let cfg = BGConfig.sharedInstance()
62
+ let launchedInBackground = BGAppState.sharedInstance().didLaunchInBackground
63
+ NSLog("[BGGEO] setupCoreLocation auto-resume check: enabled=\(cfg.enabled) startOnBoot=\(cfg.app.startOnBoot) launchedInBackground=\(launchedInBackground)")
64
+ if cfg.enabled || (cfg.app.startOnBoot && launchedInBackground) {
65
+ NSLog("[BGGEO] auto-resuming engine natively (kill-state path)")
66
+ self.ready()
67
+ }
68
+ }
69
+
70
+ if Thread.isMainThread {
71
+ configure()
72
+ } else {
73
+ DispatchQueue.main.sync(execute: configure)
74
+ }
75
+ }
76
+
77
+ // MARK: - Lifecycle
78
+
79
+ @objc public func ready() {
80
+ guard !isReady else { return }
81
+ isReady = true
82
+ let config = BGConfig.sharedInstance()
83
+ // Mark the app as having booted at least once so isFirstBoot() returns false on next launch.
84
+ UserDefaults.standard.set(true, forKey: "BGLocationManager_booted")
85
+ BGLog.sharedInstance().configure()
86
+ BGHttpService.sharedInstance().startMonitoring()
87
+ BGLocationDAO.sharedInstance()
88
+
89
+ let appState = BGAppState.sharedInstance()
90
+ config.didLaunchInBackground = appState.didLaunchInBackground
91
+ if config.app.startOnBoot && config.didLaunchInBackground {
92
+ doStart(true)
93
+ }
94
+
95
+ if config.enabled {
96
+ doStart(config.isMoving)
97
+ }
98
+
99
+ // Drain records left by an interrupted background request immediately.
100
+ // This does not depend on React Native listeners or a later reachability
101
+ // transition, so a location-triggered cold launch can deliver natively.
102
+ BGHttpService.sharedInstance().resumePendingAutoSync()
103
+
104
+ // Clear the background-launch flag UNCONDITIONALLY once it has been read
105
+ // into config — otherwise (when ready() runs via the cfg.enabled branch
106
+ // instead of startOnBoot) the flag leaks into the NEXT normal foreground
107
+ // launch, corrupting launch classification.
108
+ UserDefaults.standard.removeObject(forKey: "BGLocationManager_didLaunchInBackground")
109
+
110
+ // Mirror the HTTP config into the App Group so the Location Push Service
111
+ // Extension can POST locations to the same server while the app is killed.
112
+ syncConfigToAppGroup()
113
+
114
+ BGAppState.sharedInstance().clientReady = true
115
+ }
116
+
117
+ /// Writes the current HTTP config (url, headers, params, auth token, etc.)
118
+ /// into the App Group shared UserDefaults. The CLLocationPushServiceExtension
119
+ /// runs in a separate process and reads this to know where/how to upload the
120
+ /// location it captures after an APNs location push. Safe no-op if the App
121
+ /// Group entitlement is not configured.
122
+ @objc public func syncConfigToAppGroup() {
123
+ guard let defaults = BGLocationPushShared.sharedDefaults() else {
124
+ NSLog("[BGGEO] App Group not configured — Location Push config not synced")
125
+ return
126
+ }
127
+ let config = BGConfig.sharedInstance()
128
+ let http = config.http
129
+
130
+ defaults.set(http.url, forKey: BGLocationPushShared.keyUrl)
131
+ defaults.set(http.method, forKey: BGLocationPushShared.keyMethod)
132
+ defaults.set(http.headers, forKey: BGLocationPushShared.keyHeaders)
133
+ defaults.set(http.params, forKey: BGLocationPushShared.keyParams)
134
+ defaults.set(http.rootProperty, forKey: BGLocationPushShared.keyRootProperty)
135
+
136
+ let auth = config.authorization
137
+ if let token = auth.accessToken, !token.isEmpty {
138
+ defaults.set(token, forKey: BGLocationPushShared.keyAccessToken)
139
+ } else {
140
+ defaults.removeObject(forKey: BGLocationPushShared.keyAccessToken)
141
+ }
142
+
143
+ NSLog("[BGGEO] Synced HTTP config to App Group (url=\(http.url)) for Location Push Extension")
144
+ }
145
+
146
+ /// Host-app-supplied delivery config for the Location Push Service Extension.
147
+ /// Lets the app hand the extension a preferred socket channel (and any extra
148
+ /// REST overrides). Keys: socketUrl, socketPath, socketEvent, socketAuthToken,
149
+ /// socketTimeout, url, accessToken, extras. Persisted to the App Group.
150
+ @objc public func setLocationPushConfig(_ config: [String: Any]) {
151
+ guard let defaults = BGLocationPushShared.sharedDefaults() else {
152
+ NSLog("[BGGEO] App Group not configured — Location Push config not stored")
153
+ return
154
+ }
155
+
156
+ // Partial update: only touch keys PRESENT in the dict. An explicit
157
+ // NSNull clears; an absent key is left unchanged (so a later
158
+ // fcmToken-only call doesn't wipe the socket config).
159
+ func apply(_ defaultsKey: String, _ srcKey: String) {
160
+ guard let value = config[srcKey] else { return }
161
+ if value is NSNull { defaults.removeObject(forKey: defaultsKey) }
162
+ else { defaults.set(value, forKey: defaultsKey) }
163
+ }
164
+
165
+ apply(BGLocationPushShared.keySocketUrl, "socketUrl")
166
+ apply(BGLocationPushShared.keySocketPath, "socketPath")
167
+ apply(BGLocationPushShared.keySocketEvent, "socketEvent")
168
+ apply(BGLocationPushShared.keySocketAuthToken, "socketAuthToken")
169
+ apply(BGLocationPushShared.keySocketTimeout, "socketTimeout")
170
+ apply(BGLocationPushShared.keyFallbackUrl, "fallbackUrl")
171
+ apply(BGLocationPushShared.keyFcmToken, "fcmToken")
172
+
173
+ // Optional REST overrides (otherwise the values synced from http config win).
174
+ if let url = config["url"] { defaults.set(url, forKey: BGLocationPushShared.keyUrl) }
175
+ if let token = config["accessToken"] { defaults.set(token, forKey: BGLocationPushShared.keyAccessToken) }
176
+ if let extras = config["extras"] as? [String: Any] {
177
+ defaults.set(extras, forKey: BGLocationPushShared.keyExtras)
178
+ }
179
+ // Custom HTTP headers for the REST fallback (e.g. {"Device-Type": "ios"}).
180
+ if let headers = config["headers"] as? [String: String] {
181
+ defaults.set(headers, forKey: BGLocationPushShared.keyHeaders)
182
+ }
183
+ // Host-defined payload shape (applies to socket emit + REST fallback).
184
+ // Pass NSNull / omit to keep the built-in default payload.
185
+ if let template = config["payloadTemplate"] as? [String: Any] {
186
+ defaults.set(template, forKey: BGLocationPushShared.keyPayloadTemplate)
187
+ } else if config["payloadTemplate"] is NSNull {
188
+ defaults.removeObject(forKey: BGLocationPushShared.keyPayloadTemplate)
189
+ }
190
+
191
+ NSLog("[BGGEO] Stored Location Push delivery config (socketUrl=\(config["socketUrl"] ?? "nil"))")
192
+ }
193
+
194
+ @objc public func start() {
195
+ let config = BGConfig.sharedInstance()
196
+ config.enabled = true
197
+ // Start must be durable before returning to JS. If the process is
198
+ // terminated immediately after the user taps Start, an async archive
199
+ // can be lost and the next Core Location launch cannot auto-resume.
200
+ config.forcePersistNow()
201
+ syncConfigToAppGroup()
202
+ doStart(config.isMoving)
203
+ }
204
+
205
+ @objc public func doStart(_ isMoving: Bool) {
206
+ let config = BGConfig.sharedInstance()
207
+ config.validateLicense()
208
+
209
+ let tracking = BGTrackingService.sharedInstance()
210
+ tracking.beforeInsertBlock = beforeInsertBlock
211
+ tracking.start(isMoving)
212
+ BGLiveActivityManager.shared.startIfNeeded(isMoving: isMoving)
213
+ BGTrackingAudioManager.shared.startIfNeeded()
214
+
215
+ if config.app.preventSuspend {
216
+ BGBackgroundTaskManager.sharedInstance().startPreventSuspend(.invalid)
217
+ }
218
+
219
+ if config.hasSchedule() {
220
+ startSchedule()
221
+ }
222
+
223
+ startGeofences()
224
+ BGEventBus.sharedInstance().emit(BGEventNames.enabledChange, payload: ["enabled": true])
225
+ }
226
+
227
+ @objc public func stop() {
228
+ let config = BGConfig.sharedInstance()
229
+ config.enabled = false
230
+ config.isMoving = false
231
+ config.forcePersistNow()
232
+
233
+ BGTrackingService.sharedInstance().stop()
234
+ BGLiveActivityManager.shared.end()
235
+ BGTrackingAudioManager.shared.stop()
236
+ BGBackgroundTaskManager.sharedInstance().stopPreventSuspend(.invalid)
237
+ BGGeofenceManager.sharedInstance().stop()
238
+ BGScheduler.sharedInstance().stop()
239
+ BGEventBus.sharedInstance().emit(BGEventNames.enabledChange, payload: ["enabled": false])
240
+ }
241
+
242
+ @objc public var enabled: Bool {
243
+ return BGConfig.sharedInstance().enabled
244
+ }
245
+
246
+ // MARK: - Pace
247
+
248
+ @objc public func changePace(_ isMoving: Bool) {
249
+ BGTrackingService.sharedInstance().changePace(isMoving)
250
+ }
251
+
252
+ @objc public func setPace(_ isMoving: Bool) {
253
+ changePace(isMoving)
254
+ }
255
+
256
+ // MARK: - Current position
257
+
258
+ @objc public func getCurrentPosition(_ request: BGCurrentPositionRequest) {
259
+ BGTrackingService.sharedInstance().getCurrentPosition(request)
260
+ }
261
+
262
+ // MARK: - Watch position
263
+
264
+ @objc public func watchPosition(_ request: BGWatchPositionRequest) {
265
+ // Bridge the watch request onto a stream and drive its success block with
266
+ // a BGLocation on every emitted fix (previously the passed request was
267
+ // dropped and a blank stream with no callback was started instead).
268
+ let stream = BGStreamLocationRequest()
269
+ stream.interval = request.interval
270
+ stream.desiredAccuracy = request.desiredAccuracy
271
+ stream.persist = request.persist
272
+ stream.extras = request.extras
273
+ stream.success = { location in
274
+ guard let cl = location as? CLLocation else { return }
275
+ let tsLocation = BGLocation(location: cl, type: "watch", extras: request.extras as? [String: Any])
276
+ if request.persist {
277
+ _ = BGLocationDAO.sharedInstance().create(tsLocation, error: nil)
278
+ }
279
+ request.success?(tsLocation)
280
+ }
281
+ _ = BGLocationRequestService.sharedInstance().startStream(stream)
282
+ }
283
+
284
+ @objc public func stopWatchPosition(_ watchId: Int) {
285
+ // The bridge supports a single active watch, so stop all streams rather
286
+ // than relying on a stream id the JS layer never receives.
287
+ BGLocationRequestService.sharedInstance().stopAllStreams()
288
+ }
289
+
290
+ // MARK: - Geofences
291
+
292
+ @objc public func addGeofence(_ geofence: BGGeofence, success: (() -> Void)?, failure: ((Error) -> Void)?) {
293
+ BGGeofenceManager.sharedInstance().create(geofence, success: success, failure: failure)
294
+ }
295
+
296
+ @objc public func addGeofences(_ geofences: [BGGeofence], success: (() -> Void)?, failure: ((Error) -> Void)?) {
297
+ for geofence in geofences {
298
+ BGGeofenceManager.sharedInstance().create(geofence, success: nil, failure: nil)
299
+ }
300
+ success?()
301
+ }
302
+
303
+ @objc public func removeGeofence(_ identifier: String, success: (() -> Void)?, failure: ((Error) -> Void)?) {
304
+ BGGeofenceManager.sharedInstance().destroy(identifier, success: success, failure: failure)
305
+ }
306
+
307
+ @objc public func removeGeofences(_ identifiers: [String], success: (() -> Void)?, failure: ((Error) -> Void)?) {
308
+ for id in identifiers {
309
+ BGGeofenceManager.sharedInstance().destroy(id, success: nil, failure: nil)
310
+ }
311
+ success?()
312
+ }
313
+
314
+ @objc public func removeGeofences() {
315
+ BGGeofenceDAO.sharedInstance().destroyAll()
316
+ BGGeofenceManager.sharedInstance().stopMonitoringGeofences()
317
+ }
318
+
319
+ @objc public func getGeofence(_ identifier: String, success: ((BGGeofence) -> Void)?, failure: ((Error) -> Void)?) {
320
+ if let geofence = BGGeofenceDAO.sharedInstance().find(identifier) {
321
+ success?(geofence)
322
+ } else {
323
+ failure?(NSError(domain: "BGLocationManager", code: 404, userInfo: [NSLocalizedDescriptionKey: "Geofence not found: \(identifier)"]))
324
+ }
325
+ }
326
+
327
+ @objc public func getGeofences(_ success: (([BGGeofence]) -> Void)?, failure: ((Error) -> Void)?) {
328
+ success?(BGGeofenceDAO.sharedInstance().all())
329
+ }
330
+
331
+ @objc public func getGeofences() -> [BGGeofence] {
332
+ return BGGeofenceDAO.sharedInstance().all()
333
+ }
334
+
335
+ @objc public func geofenceExists(_ identifier: String, callback: ((Bool) -> Void)?) {
336
+ callback?(BGGeofenceDAO.sharedInstance().exists(identifier))
337
+ }
338
+
339
+ @objc public func startGeofences() {
340
+ BGGeofenceManager.sharedInstance().start()
341
+ }
342
+
343
+ // MARK: - Locations
344
+
345
+ @objc public func getLocations(_ success: (([[String: Any]]) -> Void)?, failure: ((Error) -> Void)?) {
346
+ let records = BGLocationDAO.sharedInstance().all()
347
+ success?(records)
348
+ }
349
+
350
+ @objc public func getCount() -> Int {
351
+ return BGLocationDAO.sharedInstance().getCount()
352
+ }
353
+
354
+ @objc public func insertLocation(_ location: BGLocation, success: ((BGLocation) -> Void)?, failure: ((Error) -> Void)?) {
355
+ if BGLocationDAO.sharedInstance().create(location, error: nil) {
356
+ success?(location)
357
+ } else {
358
+ failure?(NSError(domain: "BGLocationManager", code: -1, userInfo: [NSLocalizedDescriptionKey: "Failed to insert location"]))
359
+ }
360
+ }
361
+
362
+ @objc public func persistLocation(_ location: BGLocation) {
363
+ _ = BGLocationDAO.sharedInstance().create(location, error: nil)
364
+ }
365
+
366
+ @objc public func destroyLocation(_ uuid: String) -> Bool {
367
+ return BGLocationDAO.sharedInstance().destroy(uuid)
368
+ }
369
+
370
+ @objc public func destroyLocation(_ uuid: String, success: (() -> Void)?, failure: ((Error) -> Void)?) {
371
+ if BGLocationDAO.sharedInstance().destroy(uuid) {
372
+ success?()
373
+ } else {
374
+ failure?(NSError(domain: "BGLocationManager", code: -1, userInfo: [NSLocalizedDescriptionKey: "Failed to destroy location"]))
375
+ }
376
+ }
377
+
378
+ @objc public func destroyLocations(_ failure: ((Error) -> Void)?) {
379
+ BGLocationDAO.sharedInstance().clear()
380
+ }
381
+
382
+ @objc public func destroyLocations() {
383
+ BGLocationDAO.sharedInstance().clear()
384
+ }
385
+
386
+ @objc public func clearDatabase() {
387
+ destroyLocations()
388
+ }
389
+
390
+ @objc public func sync(_ success: (([[String: Any]]) -> Void)?, failure: ((Error) -> Void)?) {
391
+ BGHttpService.sharedInstance().flush({ response in
392
+ let records = BGLocationDAO.sharedInstance().all()
393
+ success?(records)
394
+ }, failure: failure)
395
+ }
396
+
397
+ @objc public func getStationaryLocation() -> CLLocation? {
398
+ return BGTrackingService.sharedInstance().stationaryLocation
399
+ }
400
+
401
+ // MARK: - Odometer
402
+
403
+ @objc public func getOdometer() -> Double {
404
+ return BGOdometer.sharedInstance().getOdometer()
405
+ }
406
+
407
+ @objc public func setOdometer(_ value: Double, request: BGCurrentPositionRequest?) {
408
+ BGTrackingService.sharedInstance().setOdometer(value, request: request)
409
+ }
410
+
411
+ // MARK: - Schedule
412
+
413
+ @objc public func startSchedule() {
414
+ guard let mgr = locationManager else { return }
415
+ _ = BGScheduler.sharedInstance().start(withSchedule: BGConfig.sharedInstance().app.schedule)
416
+ }
417
+
418
+ @objc public func stopSchedule() {
419
+ BGScheduler.sharedInstance().stop()
420
+ }
421
+
422
+ // MARK: - Log
423
+
424
+ @objc public func getLog(_ query: LogQuery?, success: (([String: Any]) -> Void)?, failure: ((Error) -> Void)?) {
425
+ let q = query ?? LogQuery()
426
+ let entries = BGLog.sharedInstance().getLog(q)
427
+ success?(["log": entries])
428
+ }
429
+
430
+ @objc public func getLog(_ query: LogQuery?, failure: ((Error) -> Void)?) {
431
+ getLog(query, success: nil, failure: failure)
432
+ }
433
+
434
+ @objc public func emailLog(_ to: String, query: LogQuery?, success: (() -> Void)?, failure: ((Error) -> Void)?) {
435
+ let q = query ?? LogQuery()
436
+ BGLog.sharedInstance().emailLog(to, query: q, success: success, failure: failure)
437
+ }
438
+
439
+ @objc public func emailLog(_ to: String, success: (() -> Void)?, failure: ((Error) -> Void)?) {
440
+ emailLog(to, query: nil, success: success, failure: failure)
441
+ }
442
+
443
+ @objc public func uploadLog(_ url: String, query: LogQuery?, success: (([String: Any]) -> Void)?, failure: ((Error) -> Void)?) {
444
+ let q = query ?? LogQuery()
445
+ BGLog.sharedInstance().uploadLog(url, query: q, success: success, failure: failure)
446
+ }
447
+
448
+ @objc public func destroyLog() {
449
+ BGLog.sharedInstance().destroy()
450
+ }
451
+
452
+ @objc public func setLogLevel(_ level: Int) {
453
+ BGLog.sharedInstance().setLogLevel(level)
454
+ }
455
+
456
+ @objc public func log(_ level: String, message: String) {
457
+ BGLog.sharedInstance().notify(message, debug: level == "debug")
458
+ }
459
+
460
+ @objc public func error(_ level: String, message: String) {
461
+ BGLog.sharedInstance().alert(level, message: message)
462
+ }
463
+
464
+ @objc public func playSound(_ soundId: SystemSoundID) {
465
+ BGLog.sharedInstance().playSound(soundId)
466
+ }
467
+
468
+ // MARK: - Provider state
469
+
470
+ @objc public func getProviderState() -> [String: Any] {
471
+ let status = CLLocationManager.authorizationStatus()
472
+ return [
473
+ "status": status.rawValue,
474
+ "enabled": CLLocationManager.locationServicesEnabled(),
475
+ "gps": CLLocationManager.locationServicesEnabled(),
476
+ "network": false,
477
+ "accuracyAuthorization": 1
478
+ ]
479
+ }
480
+
481
+ @objc public func getState() -> [String: Any] {
482
+ var state = BGConfig.sharedInstance().currentStateDictionary()
483
+ state["liveActivity"] = BGLiveActivityManager.shared.stateDictionary()
484
+ state["trackingAudio"] = BGTrackingAudioManager.shared.stateDictionary()
485
+ return state
486
+ }
487
+
488
+ // MARK: - Hardware availability
489
+
490
+ @objc public func isLocationServicesEnabled() -> Bool {
491
+ return CLLocationManager.locationServicesEnabled()
492
+ }
493
+
494
+ @objc public func isPowerSaveMode() -> Bool {
495
+ return ProcessInfo.processInfo.isLowPowerModeEnabled
496
+ }
497
+
498
+ @objc public func isAccelerometerAvailable() -> Bool {
499
+ return BGMotionDetector.sharedInstance().isAccelerometerAvailable()
500
+ }
501
+
502
+ @objc public func isGyroAvailable() -> Bool {
503
+ return BGMotionDetector.sharedInstance().isGyroAvailable()
504
+ }
505
+
506
+ @objc public func isDeviceMotionAvailable() -> Bool {
507
+ return BGMotionDetector.sharedInstance().isDeviceMotionAvailable()
508
+ }
509
+
510
+ @objc public func isMagnetometerAvailable() -> Bool {
511
+ return BGMotionDetector.sharedInstance().isMagnetometerAvailable()
512
+ }
513
+
514
+ @objc public func isMotionHardwareAvailable() -> Bool {
515
+ return BGMotionDetector.motionHardwareAvailable()
516
+ }
517
+
518
+ @objc public func isRotationAvailable() -> Bool {
519
+ return BGMotionDetector.sharedInstance().isGyroAvailable()
520
+ }
521
+
522
+ // MARK: - Permissions
523
+
524
+ @objc public func requestPermission(_ success: (() -> Void)?, failure: ((Error) -> Void)?) {
525
+ BGLocationAuthorization.sharedInstance().updateDesiredPolicyFromConfig()
526
+ BGLocationAuthorization.sharedInstance().requestAuthorization { status, error in
527
+ if let error = error {
528
+ failure?(error)
529
+ } else {
530
+ success?()
531
+ }
532
+ }
533
+ }
534
+
535
+ @objc public func requestTemporaryFullAccuracy(_ purposeKey: String, success: (() -> Void)?, failure: ((Error) -> Void)?) {
536
+ BGLocationAuthorization.sharedInstance().requestTemporaryFullAccuracy(purposeKey, success: success, failure: failure)
537
+ }
538
+
539
+ // MARK: - Background task
540
+
541
+ @objc public func createBackgroundTask() -> UIBackgroundTaskIdentifier {
542
+ return BGBackgroundTaskManager.sharedInstance().createBackgroundTask()
543
+ }
544
+
545
+ @objc public func stopBackgroundTask(_ taskId: UIBackgroundTaskIdentifier) {
546
+ BGBackgroundTaskManager.sharedInstance().stopBackgroundTask(taskId)
547
+ }
548
+
549
+ // MARK: - Listeners
550
+
551
+ @objc public func onLocation(_ success: ((BGLocation) -> Void)?, failure: ((Error) -> Void)?) {
552
+ BGEventManager.sharedInstance().addLocationListener(success: { location in
553
+ if let loc = location as? BGLocation { success?(loc) }
554
+ }, failure: { payload in
555
+ if let err = payload as? Error { failure?(err) }
556
+ })
557
+ }
558
+
559
+ @objc public func onMotionChange(_ success: ((BGLocation) -> Void)?, failure: ((Error) -> Void)?) {
560
+ _ = BGEventManager.sharedInstance().addListener(BGEventNames.motionChangeComplete, callback: { payload in
561
+ if let loc = payload as? BGLocation { success?(loc) }
562
+ })
563
+ }
564
+
565
+ @objc public func onActivityChange(_ success: ((BGMotionActivity) -> Void)?) {
566
+ _ = BGEventBus.sharedInstance().on(BGEventNames.activityChange) { payload in
567
+ if let activity = payload as? BGMotionActivity { success?(activity) }
568
+ }
569
+ }
570
+
571
+ @objc public func onGeofence(_ success: ((BGGeofenceEvent) -> Void)?) {
572
+ _ = BGEventBus.sharedInstance().on(BGEventNames.geofence) { payload in
573
+ if let event = payload as? BGGeofenceEvent { success?(event) }
574
+ }
575
+ }
576
+
577
+ @objc public func onGeofencesChange(_ success: ((BGGeofencesChangeEvent) -> Void)?) {
578
+ }
579
+
580
+ @objc public func onHttp(_ success: (([String: Any]) -> Void)?) {
581
+ _ = BGEventBus.sharedInstance().on(BGEventNames.http) { payload in
582
+ if let dict = payload as? [String: Any] { success?(dict) }
583
+ }
584
+ }
585
+
586
+ @objc public func onHeartbeat(_ success: ((BGHeartbeatEvent) -> Void)?) {
587
+ _ = BGEventBus.sharedInstance().on(BGEventNames.heartbeat) { payload in
588
+ if let event = payload as? BGHeartbeatEvent { success?(event) }
589
+ }
590
+ }
591
+
592
+ @objc public func onSchedule(_ success: ((BGScheduleEvent) -> Void)?) {
593
+ _ = BGEventBus.sharedInstance().on(BGEventNames.schedule) { payload in
594
+ if let event = payload as? BGScheduleEvent { success?(event) }
595
+ }
596
+ }
597
+
598
+ @objc public func onEnabledChange(_ success: ((Bool) -> Void)?) {
599
+ _ = BGEventBus.sharedInstance().on(BGEventNames.enabledChange) { payload in
600
+ if let dict = payload as? [String: Any], let enabled = dict["enabled"] as? Bool {
601
+ success?(enabled)
602
+ }
603
+ }
604
+ }
605
+
606
+ @objc public func onConnectivityChange(_ success: ((Bool) -> Void)?) {
607
+ _ = BGEventBus.sharedInstance().on(BGEventNames.connectivityChange) { payload in
608
+ if let dict = payload as? [String: Any], let connected = dict["connected"] as? Bool {
609
+ success?(connected)
610
+ }
611
+ }
612
+ }
613
+
614
+ @objc public func onPowerSaveChange(_ success: ((Bool) -> Void)?) {
615
+ _ = BGEventBus.sharedInstance().on(BGEventNames.powerSaveChange) { payload in
616
+ if let dict = payload as? [String: Any], let enabled = dict["isPowerSaveMode"] as? Bool {
617
+ success?(enabled)
618
+ }
619
+ }
620
+ }
621
+
622
+ @objc public func onAuthorization(_ success: ((BGAuthorizationEvent) -> Void)?) {
623
+ _ = BGEventBus.sharedInstance().on(BGEventNames.authorization) { payload in
624
+ if let event = payload as? BGAuthorizationEvent { success?(event) }
625
+ }
626
+ }
627
+
628
+ @objc public func onProviderChange(_ success: ((BGProviderChangeEvent) -> Void)?) {
629
+ _ = BGEventBus.sharedInstance().on(BGEventNames.providerChange) { payload in
630
+ if let event = payload as? BGProviderChangeEvent { success?(event) }
631
+ }
632
+ }
633
+
634
+ // MARK: - Remove listeners
635
+
636
+ @objc public func removeListener(_ event: String, callback: AnyObject) {
637
+ }
638
+
639
+ @objc public func removeListener(_ event: String, token: Int) {
640
+ BGEventBus.sharedInstance().off(event, token: String(token))
641
+ }
642
+
643
+ @objc public func removeListeners(_ event: String) {
644
+ BGEventBus.sharedInstance().offAll(event)
645
+ }
646
+
647
+ @objc public func removeListenersForEvent(_ event: String) {
648
+ removeListeners(event)
649
+ }
650
+
651
+ @objc public func removeListeners() {
652
+ BGEventBus.sharedInstance().offAll()
653
+ }
654
+
655
+ // MARK: - View controller
656
+
657
+ public func setViewController(_ vc: UIViewController) {
658
+ viewController = vc
659
+ }
660
+ }
661
+
662
+ import AudioToolbox