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,94 @@
1
+ import Foundation
2
+ import CoreLocation
3
+
4
+ @objc public class BGGeofenceLocationRequest: BGSingleLocationRequest {
5
+
6
+ @objc public var geofenceEvent: BGGeofenceEvent?
7
+
8
+ @objc public class func request(
9
+ event: BGGeofenceEvent,
10
+ maximumAge: Double,
11
+ timeout: Double,
12
+ desiredAccuracy: CLLocationAccuracy,
13
+ allowStale: Bool,
14
+ samples: Int,
15
+ label: String?,
16
+ persist: Bool,
17
+ success: @escaping (Any?) -> Void,
18
+ failure: @escaping (Int) -> Void
19
+ ) -> BGGeofenceLocationRequest {
20
+ let req = BGGeofenceLocationRequest()
21
+ req.geofenceEvent = event
22
+ req.maximumAge = maximumAge
23
+ req.timeout = timeout
24
+ req.desiredAccuracy = desiredAccuracy
25
+ req.allowStale = allowStale
26
+ req.samples = samples
27
+ req.label = label
28
+ req.persist = persist
29
+ req.success = success
30
+ req.failure = failure
31
+ req.type = "geofence"
32
+ return req
33
+ }
34
+
35
+ @objc public init(
36
+ event: BGGeofenceEvent,
37
+ maximumAge: Double,
38
+ timeout: Double,
39
+ desiredAccuracy: CLLocationAccuracy,
40
+ allowStale: Bool,
41
+ samples: Int,
42
+ label: String?,
43
+ persist: Bool,
44
+ success: @escaping (Any?) -> Void,
45
+ failure: @escaping (Int) -> Void
46
+ ) {
47
+ super.init()
48
+ self.geofenceEvent = event
49
+ self.maximumAge = maximumAge
50
+ self.timeout = timeout
51
+ self.desiredAccuracy = desiredAccuracy
52
+ self.allowStale = allowStale
53
+ self.samples = samples
54
+ self.label = label
55
+ self.persist = persist
56
+ self.success = success
57
+ self.failure = failure
58
+ self.type = "geofence"
59
+ }
60
+
61
+ @objc public override init() {
62
+ super.init()
63
+ self.type = "geofence"
64
+ }
65
+
66
+ @objc public func copy(with zone: NSZone? = nil) -> Any {
67
+ let copy = BGGeofenceLocationRequest()
68
+ copy.geofenceEvent = geofenceEvent
69
+ copy.maximumAge = maximumAge
70
+ copy.timeout = timeout
71
+ copy.desiredAccuracy = desiredAccuracy
72
+ copy.allowStale = allowStale
73
+ copy.samples = samples
74
+ copy.label = label
75
+ copy.persist = persist
76
+ copy.success = success
77
+ copy.failure = failure
78
+ return copy
79
+ }
80
+
81
+ @objc public func didComplete(withLocation location: CLLocation) {
82
+ success?(location)
83
+ }
84
+
85
+ public func setType(_ type: String) {
86
+ self.type = type
87
+ }
88
+
89
+ @objc public func toDictionary() -> [String: Any] {
90
+ var dict: [String: Any] = ["type": type]
91
+ if let event = geofenceEvent { dict["geofenceEvent"] = event.toDictionary() }
92
+ return dict
93
+ }
94
+ }
@@ -0,0 +1,315 @@
1
+ import Foundation
2
+ import CoreLocation
3
+
4
+ @objc public class BGGeofenceManager: NSObject, CLLocationManagerDelegate {
5
+
6
+ private static var _sharedInstance: BGGeofenceManager?
7
+ private static let instanceLock = NSLock()
8
+
9
+ @objc public class func sharedInstance() -> BGGeofenceManager {
10
+ instanceLock.lock()
11
+ defer { instanceLock.unlock() }
12
+ if _sharedInstance == nil { _sharedInstance = BGGeofenceManager() }
13
+ return _sharedInstance!
14
+ }
15
+
16
+ // MARK: - State
17
+
18
+ @objc public var locationManager: CLLocationManager?
19
+ @objc public var enabled: Bool = false
20
+ @objc public var isMoving: Bool = false
21
+ @objc public var count: Int = 0
22
+ @objc public var proximityRadius: CLLocationDistance = 1000
23
+ @objc public var evaluationInterval: TimeInterval = 5.0
24
+ @objc public var bufferInterval: TimeInterval = 0.5
25
+ @objc public var evaluated: Bool = false
26
+ @objc public var lastLocation: CLLocation?
27
+ @objc public var lastEvent: BGGeofenceEvent?
28
+ @objc public var lastAction: String?
29
+ @objc public var lastEvaluatedAt: Date?
30
+ @objc public var didScheduleColdStartReconcile: Bool = false
31
+ @objc public var isMonitoringSignificantLocationChanges: Bool = false
32
+ @objc public var entryStates: [String: String] = [:]
33
+ @objc public var monitoredGeofences: [String: BGGeofence] = [:]
34
+ @objc public var preventSuspendTask: Any?
35
+ @objc public var willEvaluateProximity: Bool = false
36
+
37
+ var bufferTimer: Timer?
38
+ var clLocationListener: AnyObject?
39
+ var eventQueue: DispatchQueue?
40
+
41
+ private let stateQueue = DispatchQueue(label: "BGGeofenceManager.state", attributes: .concurrent)
42
+ private var clMutationQueue = DispatchQueue(label: "BGGeofenceManager.cl")
43
+
44
+ @objc public override init() {
45
+ super.init()
46
+ eventQueue = DispatchQueue(label: "BGGeofenceManager.events")
47
+ registerConfigChangeHandlers()
48
+ }
49
+
50
+ @objc public func registerConfigChangeHandlers() {
51
+ }
52
+
53
+ // MARK: - Start/Stop
54
+
55
+ @objc public func start() {
56
+ guard !enabled else { return }
57
+ enabled = true
58
+ // NOTE: the CLLocationManager delegate is owned solely by BGCLRouter
59
+ // (see BGLocationManager.setupCoreLocation), which forwards region and
60
+ // location callbacks here. Do NOT assign mgr.delegate.
61
+ reconcileMonitoredCacheFromCoreLocation()
62
+ }
63
+
64
+ @objc public func stop() {
65
+ guard enabled else { return }
66
+ enabled = false
67
+ stopMonitoringGeofences()
68
+ }
69
+
70
+ // MARK: - CL mutation
71
+
72
+ @objc public func _mutateCL(_ block: @escaping (CLLocationManager) -> Void) {
73
+ clMutationQueue.async {
74
+ guard let mgr = self.locationManager else { return }
75
+ DispatchQueue.main.async { block(mgr) }
76
+ }
77
+ }
78
+
79
+ // MARK: - Geofence management
80
+
81
+ @objc public func create(_ geofence: BGGeofence, success: (() -> Void)?, failure: ((Error) -> Void)?) {
82
+ BGGeofenceDAO.sharedInstance().create(geofence)
83
+ startMonitoringGeofence(geofence)
84
+ count = BGGeofenceDAO.sharedInstance().count()
85
+ success?()
86
+ }
87
+
88
+ @objc public func destroy(_ identifier: String, success: (() -> Void)?, failure: ((Error) -> Void)?) {
89
+ if let geofence = BGGeofenceDAO.sharedInstance().find(identifier) {
90
+ BGGeofenceDAO.sharedInstance().destroy(identifier)
91
+ stopMonitoringGeofence(geofence)
92
+ }
93
+ count = BGGeofenceDAO.sharedInstance().count()
94
+ success?()
95
+ }
96
+
97
+ @objc public func isInfiniteMonitoring() -> Bool {
98
+ return true
99
+ }
100
+
101
+ @objc public func startMonitoringGeofence(_ geofence: BGGeofence) {
102
+ _mutateCL { mgr in
103
+ geofence.startMonitoring(withLocationManager: mgr, prefix: "BGGeofence:")
104
+ self.stateQueue.async(flags: .barrier) {
105
+ self.monitoredGeofences[geofence.identifier] = geofence
106
+ }
107
+ }
108
+ }
109
+
110
+ @objc public func stopMonitoringGeofence(_ geofence: BGGeofence) {
111
+ _mutateCL { mgr in
112
+ for region in mgr.monitoredRegions {
113
+ if region.identifier.hasSuffix(geofence.identifier) {
114
+ mgr.stopMonitoring(for: region)
115
+ }
116
+ }
117
+ self.stateQueue.async(flags: .barrier) {
118
+ self.monitoredGeofences.removeValue(forKey: geofence.identifier)
119
+ }
120
+ }
121
+ }
122
+
123
+ @objc public func stopMonitoringGeofenceByIdentifier(_ identifier: String) {
124
+ if let geofence = monitoredGeofences[identifier] {
125
+ stopMonitoringGeofence(geofence)
126
+ }
127
+ }
128
+
129
+ @objc public func stopMonitoringGeofences() {
130
+ _mutateCL { mgr in
131
+ for region in mgr.monitoredRegions {
132
+ if region.identifier.hasPrefix("BGGeofence:") {
133
+ mgr.stopMonitoring(for: region)
134
+ }
135
+ }
136
+ }
137
+ stateQueue.async(flags: .barrier) {
138
+ self.monitoredGeofences.removeAll()
139
+ }
140
+ }
141
+
142
+ @objc public func isMonitoringRegion(_ identifier: String) -> Bool {
143
+ return monitoredGeofences[identifier] != nil
144
+ }
145
+
146
+ @objc public func getMonitoredGeofenceByIdentifier(_ identifier: String) -> BGGeofence? {
147
+ var result: BGGeofence?
148
+ stateQueue.sync { result = monitoredGeofences[identifier] }
149
+ return result
150
+ }
151
+
152
+ @objc public func identifierFor(_ region: CLRegion) -> String {
153
+ let id = region.identifier
154
+ if id.hasPrefix("BGGeofence:") {
155
+ return String(id.dropFirst("BGGeofence:".count))
156
+ }
157
+ return id
158
+ }
159
+
160
+ @objc public func reconcileMonitoredCacheFromCoreLocation() {
161
+ guard let mgr = locationManager else { return }
162
+ var coreLocationIds = Set<String>()
163
+ for region in mgr.monitoredRegions {
164
+ if region.identifier.hasPrefix("BGGeofence:") {
165
+ coreLocationIds.insert(identifierFor(region))
166
+ }
167
+ }
168
+ let dao = BGGeofenceDAO.sharedInstance()
169
+ let all = dao.all()
170
+ for geofence in all {
171
+ if !coreLocationIds.contains(geofence.identifier) {
172
+ startMonitoringGeofence(geofence)
173
+ }
174
+ }
175
+ }
176
+
177
+ // MARK: - Evaluation
178
+
179
+ @objc public func setLocation(_ location: CLLocation, isMoving moving: Bool) {
180
+ lastLocation = location
181
+ isMoving = moving
182
+ if moving {
183
+ evaluateProximity(location)
184
+ }
185
+ }
186
+
187
+ @objc public func evaluateProximity(_ location: CLLocation) {
188
+ evaluateProximity(location, delay: false)
189
+ }
190
+
191
+ @objc public func evaluateProximity(_ location: CLLocation, delay: Bool) {
192
+ guard enabled else { return }
193
+ if delay {
194
+ startBufferTimer()
195
+ } else {
196
+ onEvaluate()
197
+ }
198
+ }
199
+
200
+ @objc public func startBufferTimer() {
201
+ bufferTimer?.invalidate()
202
+ DispatchQueue.main.async {
203
+ self.bufferTimer = Timer.scheduledTimer(withTimeInterval: self.bufferInterval, repeats: false) { [weak self] _ in
204
+ self?.onEvaluate()
205
+ }
206
+ }
207
+ }
208
+
209
+ @objc public func onEvaluate() {
210
+ guard let location = lastLocation else { return }
211
+ lastEvaluatedAt = Date()
212
+ evaluated = true
213
+
214
+ let dao = BGGeofenceDAO.sharedInstance()
215
+ let nearby = dao.allWithinRadius(proximityRadius, latitude: location.coordinate.latitude, longitude: location.coordinate.longitude, limit: 20)
216
+
217
+ _mutateCL { mgr in
218
+ var needsStartSLC = false
219
+ for geofence in nearby {
220
+ if !self.isMonitoringRegion(geofence.identifier) {
221
+ geofence.startMonitoring(withLocationManager: mgr, prefix: "BGGeofence:")
222
+ self.stateQueue.async(flags: .barrier) {
223
+ self.monitoredGeofences[geofence.identifier] = geofence
224
+ }
225
+ needsStartSLC = true
226
+ }
227
+ }
228
+ if needsStartSLC && !self.isMonitoringSignificantLocationChanges {
229
+ self.startMonitoringSignificantLocationChanges()
230
+ }
231
+ }
232
+ }
233
+
234
+ // MARK: - Significant location changes
235
+
236
+ @objc public func startMonitoringSignificantLocationChanges() {
237
+ guard !isMonitoringSignificantLocationChanges else { return }
238
+ _mutateCL { mgr in
239
+ mgr.startMonitoringSignificantLocationChanges()
240
+ self.isMonitoringSignificantLocationChanges = true
241
+ }
242
+ }
243
+
244
+ @objc public func stopMonitoringSignificantLocationChanges() {
245
+ guard isMonitoringSignificantLocationChanges else { return }
246
+ _mutateCL { mgr in
247
+ mgr.stopMonitoringSignificantLocationChanges()
248
+ self.isMonitoringSignificantLocationChanges = false
249
+ }
250
+ }
251
+
252
+ @objc public func onStopMonitoringSLC() {
253
+ stopMonitoringSignificantLocationChanges()
254
+ }
255
+
256
+ // MARK: - Event handling
257
+
258
+ @objc public func handleGeofenceEvent(_ geofence: BGGeofence, action: String) {
259
+ createEvent(geofence, geofence: geofence, action: action)
260
+ }
261
+
262
+ @objc public func createEvent(_ sender: BGGeofence, geofence: BGGeofence, action: String) {
263
+ let identifier = geofence.identifier
264
+ let event = BGGeofenceEvent(identifier: identifier, action: action, timestamp: Date(), geofence: geofence, location: lastLocation, extras: geofence.extras)
265
+ lastEvent = event
266
+ lastAction = action
267
+ geofence.fireEvent(action, location: lastLocation)
268
+ BGGeofenceDAO.sharedInstance().updateState(forIdentifier: identifier, entryState: action == "ENTER" ? "ENTER" : "EXIT", hits: geofence.hits + 1)
269
+ BGEventBus.sharedInstance().emit(BGEventNames.geofence, payload: event.toDictionary())
270
+ }
271
+
272
+ // MARK: - CLLocationManagerDelegate
273
+
274
+ @objc public func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
275
+ guard region.identifier.hasPrefix("BGGeofence:") else { return }
276
+ let id = identifierFor(region)
277
+ if let geofence = BGGeofenceDAO.sharedInstance().find(id) {
278
+ if geofence.notifyOnEntry {
279
+ handleGeofenceEvent(geofence, action: "ENTER")
280
+ }
281
+ if geofence.notifyOnDwell && geofence.loiteringDelay > 0 {
282
+ geofence.startLoitering()
283
+ }
284
+ }
285
+ }
286
+
287
+ @objc public func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
288
+ guard region.identifier.hasPrefix("BGGeofence:") else { return }
289
+ let id = identifierFor(region)
290
+ if let geofence = BGGeofenceDAO.sharedInstance().find(id) {
291
+ geofence.cancelLoitering()
292
+ if geofence.notifyOnExit {
293
+ handleGeofenceEvent(geofence, action: "EXIT")
294
+ }
295
+ }
296
+ }
297
+
298
+ @objc public func locationManager(_ manager: CLLocationManager, didStartMonitoringFor region: CLRegion) {
299
+ }
300
+
301
+ @objc public func locationManager(_ manager: CLLocationManager, monitoringDidFailFor region: CLRegion?, withError error: Error) {
302
+ BGNativeLogger.error("BGGeofenceManager", message: error.localizedDescription)
303
+ }
304
+
305
+ @objc public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
306
+ guard let location = locations.last else { return }
307
+ setLocation(location, isMoving: isMoving)
308
+ }
309
+
310
+ // MARK: - Info
311
+
312
+ @objc public func launchedInBackground() -> Bool {
313
+ return BGAppState.sharedInstance().didLaunchInBackground
314
+ }
315
+ }
@@ -0,0 +1,97 @@
1
+ import Foundation
2
+ import CoreLocation
3
+
4
+ @objc public final class BGGeofenceTransition: NSObject {
5
+
6
+ @objc public var location: CLLocation?
7
+ @objc public var triggerLocation: CLLocation?
8
+ @objc public var triggerLocationRequest: BGCurrentPositionRequest?
9
+ @objc public var loiteringRegion: CLCircularRegion?
10
+ @objc public var isLoitering: Bool = false
11
+ @objc public var didLoiter: Bool = false
12
+ @objc public var didComplete: Bool = false
13
+ @objc public var onComplete: ((NSError?) -> Void)?
14
+ @objc public var geofence: BGGeofence?
15
+ @objc public var region: CLRegion?
16
+ @objc public var timestamp: Date?
17
+ @objc public var action: String?
18
+
19
+ @objc public init(geofence: BGGeofence?,
20
+ region: CLRegion?,
21
+ action: String?,
22
+ onComplete: ((NSError?) -> Void)?) {
23
+ self.geofence = geofence
24
+ self.region = region
25
+ self.action = action
26
+ self.onComplete = onComplete
27
+ self.isLoitering = false
28
+ self.didLoiter = false
29
+ self.didComplete = false
30
+ self.timestamp = Date()
31
+ super.init()
32
+ requestTriggerLocation()
33
+ }
34
+
35
+ @objc public convenience init(geofence: BGGeofence?,
36
+ action: String?,
37
+ onComplete: ((NSError?) -> Void)?) {
38
+ self.init(geofence: geofence, region: nil, action: action, onComplete: onComplete)
39
+ }
40
+
41
+ @objc public convenience init(geofence: BGGeofence?,
42
+ action: String?,
43
+ triggerLocation: CLLocation?) {
44
+ self.init(geofence: geofence, action: action, onComplete: nil)
45
+ if let triggerLocation = triggerLocation {
46
+ self.triggerLocation = triggerLocation
47
+ }
48
+ }
49
+
50
+ @objc public func getLoiteringRegion(_ location: CLLocation) -> CLCircularRegion? {
51
+ if let existing = loiteringRegion {
52
+ return existing
53
+ }
54
+ guard let region = region as? CLCircularRegion else { return nil }
55
+ let center = CLLocation(latitude: region.center.latitude,
56
+ longitude: region.center.longitude)
57
+ let distance = location.distance(from: center)
58
+ let accuracy = location.horizontalAccuracy
59
+ let radius = distance + max(accuracy * 1.5, 10.0)
60
+ let loitering = CLCircularRegion(center: region.center,
61
+ radius: radius,
62
+ identifier: "loitering-region")
63
+ self.loiteringRegion = loitering
64
+ return loitering
65
+ }
66
+
67
+ @objc public func requestTriggerLocation() {
68
+ let label = String(format: "BGGeofenceTransition:%@:%@",
69
+ action ?? "", geofence?.identifier ?? "")
70
+ weak var weakSelf = self
71
+ let request = BGCurrentPositionRequest(
72
+ type: "geofence",
73
+ maximumAge: 30.0,
74
+ timeout: 10000,
75
+ desiredAccuracy: 1,
76
+ allowStale: true,
77
+ samples: 3,
78
+ label: label,
79
+ persist: true,
80
+ extras: nil,
81
+ success: { _ in },
82
+ failure: { (code: Int) in
83
+ guard let strongSelf = weakSelf else { return }
84
+ let error = NSError(domain: "BGGeofenceTransition", code: code)
85
+ strongSelf.onComplete?(error)
86
+ })
87
+ self.triggerLocationRequest = request
88
+ BGLocationRequestService.sharedInstance().requestLocation(request)
89
+ }
90
+
91
+ @objc public func cancelLoitering() {
92
+ guard let request = triggerLocationRequest else { return }
93
+ BGLocationRequestService.sharedInstance().cancelRequest(request.label ?? "")
94
+ BGLog.sharedInstance().notify("Geofence DWELL cancelled", debug: BGConfig.sharedInstance().logger.debug)
95
+ triggerLocationRequest = nil
96
+ }
97
+ }
@@ -0,0 +1,26 @@
1
+ import Foundation
2
+
3
+ @objc public final class BGGeofencesChangeEvent: NSObject {
4
+
5
+ @objc public private(set) var on: [Any]
6
+ @objc public private(set) var off: [Any]
7
+
8
+ @objc public init(on: [Any]?, off: [Any]?) {
9
+ self.on = on ?? []
10
+ self.off = off ?? []
11
+ super.init()
12
+ }
13
+
14
+ @objc public func toDictionary() -> [String: Any] {
15
+ var onDictionaries: [Any] = []
16
+ for geofence in on {
17
+ if let g = geofence as? BGGeofence {
18
+ onDictionaries.append(g.toDictionary())
19
+ }
20
+ }
21
+ return [
22
+ "on": onDictionaries,
23
+ "off": off
24
+ ]
25
+ }
26
+ }
@@ -0,0 +1,136 @@
1
+ import Foundation
2
+ import CoreLocation
3
+
4
+ @objc public class BGGeolocationConfig: BGConfigModuleBase {
5
+
6
+ @objc public var desiredAccuracy: CLLocationAccuracy = kCLLocationAccuracyBest
7
+ @objc public var distanceFilter: CLLocationDistance = 10.0
8
+ @objc public var stationaryRadius: CLLocationDistance = 25.0
9
+ @objc public var locationTimeout: Double = 60.0
10
+ @objc public var stopTimeout: Double = 5.0
11
+ @objc public var stopAfterElapsedMinutes: Double = 0
12
+ @objc public var activityType: CLActivityType = .automotiveNavigation
13
+ @objc public var pausesLocationUpdatesAutomatically: Bool = false
14
+ @objc public var showsBackgroundLocationIndicator: Bool = false
15
+ @objc public var useSignificantChangesOnly: Bool = false
16
+ // When true, the engine never powers GPS down to the stationary state — it
17
+ // keeps continuous location updates running for the whole tracking session.
18
+ // Combined with showsBackgroundLocationIndicator this is the "ride app"
19
+ // behavior: the app stays alive in the background (location indicator shown)
20
+ // and tracks continuously instead of relying on motion/region wakeups.
21
+ // Higher battery cost; far more reliable background tracking.
22
+ @objc public var disableStopDetection: Bool = false
23
+ @objc public var locationAuthorizationRequest: String = "Always"
24
+ @objc public var locationAuthorizationAlert: [String: String] = [:]
25
+ @objc public var disableLocationAuthorizationAlert: Bool = false
26
+ @objc public var geofenceProximityRadius: CLLocationDistance = 1000.0
27
+ @objc public var geofenceInitialTriggerEntry: Bool = true
28
+ @objc public var enableTimestampMeta: Bool = false
29
+ @objc public var disableElasticity: Bool = false
30
+ @objc public var elasticityMultiplier: Double = 1.0
31
+ @objc public var filter: BGLocationFilterConfig?
32
+
33
+ @objc public class func activityType(fromString s: String) -> CLActivityType {
34
+ switch s.lowercased() {
35
+ case "automotive_navigation", "automotivenavigation": return .automotiveNavigation
36
+ case "fitness": return .fitness
37
+ case "other_navigation", "othernavigation": return .otherNavigation
38
+ case "other": return .other
39
+ default: return .other
40
+ }
41
+ }
42
+
43
+ @objc public class func string(forActivityType type: CLActivityType) -> String {
44
+ switch type {
45
+ case .automotiveNavigation: return "automotive_navigation"
46
+ case .fitness: return "fitness"
47
+ case .otherNavigation: return "other_navigation"
48
+ case .other: return "other"
49
+ default: return "other"
50
+ }
51
+ }
52
+
53
+ @objc public class func decodeDesiredAccuracy(_ value: Any?) -> CLLocationAccuracy {
54
+ if let n = value as? NSNumber {
55
+ let d = n.doubleValue
56
+ switch d {
57
+ case -1: return kCLLocationAccuracyBestForNavigation
58
+ case -2: return kCLLocationAccuracyBest
59
+ case 10: return kCLLocationAccuracyNearestTenMeters
60
+ case 100: return kCLLocationAccuracyHundredMeters
61
+ case 1000: return kCLLocationAccuracyKilometer
62
+ case 3000: return kCLLocationAccuracyThreeKilometers
63
+ default: return d
64
+ }
65
+ }
66
+ return kCLLocationAccuracyBest
67
+ }
68
+
69
+ @objc public override func applyDefaults() {
70
+ desiredAccuracy = kCLLocationAccuracyBest
71
+ distanceFilter = 10.0
72
+ stationaryRadius = 25.0
73
+ locationTimeout = 60.0
74
+ stopTimeout = 5.0
75
+ stopAfterElapsedMinutes = 0
76
+ activityType = .automotiveNavigation
77
+ pausesLocationUpdatesAutomatically = false
78
+ showsBackgroundLocationIndicator = false
79
+ useSignificantChangesOnly = false
80
+ disableStopDetection = false
81
+ locationAuthorizationRequest = "Always"
82
+ disableLocationAuthorizationAlert = false
83
+ geofenceProximityRadius = 1000.0
84
+ geofenceInitialTriggerEntry = true
85
+ enableTimestampMeta = false
86
+ disableElasticity = false
87
+ elasticityMultiplier = 1.0
88
+ }
89
+
90
+ @objc public var requestsAlwaysAuthorization: Bool {
91
+ return locationAuthorizationRequest == "Always"
92
+ }
93
+
94
+ @objc public var usesHighAccuracyGPS: Bool {
95
+ return desiredAccuracy <= kCLLocationAccuracyNearestTenMeters
96
+ }
97
+
98
+ @objc public var hasValidGeofenceProximityRadius: Bool {
99
+ return geofenceProximityRadius > 0
100
+ }
101
+
102
+ @objc public func getLocationAuthorizationAlertStrings() -> [String: String] {
103
+ return locationAuthorizationAlert
104
+ }
105
+
106
+ @objc public override func propertySpecs() -> [BGPropertySpecImpl] {
107
+ return [
108
+ BGPropertySpec(name: "desiredAccuracy", type: "double"),
109
+ BGPropertySpec(name: "distanceFilter", type: "double"),
110
+ BGPropertySpec(name: "stationaryRadius", type: "double"),
111
+ BGPropertySpec(name: "locationTimeout", type: "double"),
112
+ BGPropertySpec(name: "stopTimeout", type: "double"),
113
+ BGPropertySpec(name: "stopAfterElapsedMinutes", type: "double"),
114
+ BGPropertySpec(name: "activityType", type: "string"),
115
+ BGPropertySpec(name: "pausesLocationUpdatesAutomatically", type: "bool"),
116
+ BGPropertySpec(name: "showsBackgroundLocationIndicator", type: "bool"),
117
+ BGPropertySpec(name: "useSignificantChangesOnly", type: "bool"),
118
+ BGPropertySpec(name: "disableStopDetection", type: "bool"),
119
+ BGPropertySpec(name: "locationAuthorizationRequest", type: "string"),
120
+ BGPropertySpec(name: "disableLocationAuthorizationAlert", type: "bool"),
121
+ BGPropertySpec(name: "geofenceProximityRadius", type: "double"),
122
+ BGPropertySpec(name: "geofenceInitialTriggerEntry", type: "bool"),
123
+ BGPropertySpec(name: "enableTimestampMeta", type: "bool"),
124
+ BGPropertySpec(name: "disableElasticity", type: "bool"),
125
+ BGPropertySpec(name: "elasticityMultiplier", type: "double")
126
+ ]
127
+ }
128
+
129
+ @objc public override func validateConfiguration() -> Bool {
130
+ return true
131
+ }
132
+
133
+ @objc public override var description: String {
134
+ return "<BGGeolocationConfig desiredAccuracy=\(desiredAccuracy) distanceFilter=\(distanceFilter) stationaryRadius=\(stationaryRadius)>"
135
+ }
136
+ }