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,143 @@
1
+ import Foundation
2
+ import UIKit
3
+ import CoreLocation
4
+
5
+ @objc public class BGBackgroundTaskManager: NSObject, CLLocationManagerDelegate {
6
+
7
+ private static var _sharedInstance: BGBackgroundTaskManager?
8
+ private static let lock = NSLock()
9
+
10
+ public var backgroundTasks: [UIBackgroundTaskIdentifier] = []
11
+ public var preventSuspendTasks: [UIBackgroundTaskIdentifier] = []
12
+ @objc public var locationManager: CLLocationManager?
13
+ @objc public var preventSuspendTimer: Timer?
14
+ @objc public var acquisitionBufferTimer: Timer?
15
+
16
+ @objc public class func sharedInstance() -> BGBackgroundTaskManager {
17
+ lock.lock()
18
+ defer { lock.unlock() }
19
+ if _sharedInstance == nil {
20
+ _sharedInstance = BGBackgroundTaskManager()
21
+ }
22
+ return _sharedInstance!
23
+ }
24
+
25
+ @objc public override init() {
26
+ super.init()
27
+ }
28
+
29
+ deinit {
30
+ NotificationCenter.default.removeObserver(self)
31
+ stopPreventSuspendTimer()
32
+ }
33
+
34
+ @objc public func acquireBackgroundTime() {
35
+ let task = UIApplication.shared.beginBackgroundTask(expirationHandler: { [weak self] in
36
+ self?.stopKeepAlive()
37
+ })
38
+ if task != .invalid {
39
+ backgroundTasks.append(task)
40
+ }
41
+ }
42
+
43
+ @objc public func createBackgroundTask() -> UIBackgroundTaskIdentifier {
44
+ return UIApplication.shared.beginBackgroundTask(expirationHandler: nil)
45
+ }
46
+
47
+ @objc public func stopBackgroundTask(_ task: UIBackgroundTaskIdentifier) {
48
+ UIApplication.shared.endBackgroundTask(task)
49
+ backgroundTasks.removeAll { $0 == task }
50
+ }
51
+
52
+ @objc public func startKeepAlive() {
53
+ acquireBackgroundTime()
54
+ }
55
+
56
+ @objc public func stopKeepAlive() {
57
+ for task in backgroundTasks {
58
+ UIApplication.shared.endBackgroundTask(task)
59
+ }
60
+ backgroundTasks.removeAll()
61
+ }
62
+
63
+ @objc public func startPreventSuspend(_ task: UIBackgroundTaskIdentifier) {
64
+ // Begin a REAL UIBackgroundTask so a backgrounded app gets an extended
65
+ // (~30s) execution window. Previously this just appended the passed
66
+ // identifier (always .invalid) and did nothing. The passed argument is
67
+ // ignored. Note: true stationary/kill-state longevity comes from SLC +
68
+ // region wake, not from background tasks — this only bridges short gaps
69
+ // (e.g. so a heartbeat tick can complete an HTTP flush).
70
+ DispatchQueue.main.async {
71
+ let id = UIApplication.shared.beginBackgroundTask(withName: "BGPreventSuspend") { [weak self] in
72
+ self?.stopAllPreventSuspend()
73
+ }
74
+ if id != .invalid {
75
+ self.preventSuspendTasks.append(id)
76
+ }
77
+ }
78
+ }
79
+
80
+ @objc public func renewPreventSuspend() {
81
+ // Relay pattern: take a fresh task BEFORE ending the previous ones so the
82
+ // execution window rolls forward without a gap. Driven from the heartbeat.
83
+ DispatchQueue.main.async {
84
+ let previous = self.preventSuspendTasks
85
+ let id = UIApplication.shared.beginBackgroundTask(withName: "BGPreventSuspend") { [weak self] in
86
+ self?.stopAllPreventSuspend()
87
+ }
88
+ if id != .invalid {
89
+ self.preventSuspendTasks.append(id)
90
+ }
91
+ for t in previous {
92
+ UIApplication.shared.endBackgroundTask(t)
93
+ self.preventSuspendTasks.removeAll { $0 == t }
94
+ }
95
+ }
96
+ }
97
+
98
+ @objc public func stopAllPreventSuspend() {
99
+ DispatchQueue.main.async {
100
+ for t in self.preventSuspendTasks {
101
+ UIApplication.shared.endBackgroundTask(t)
102
+ }
103
+ self.preventSuspendTasks.removeAll()
104
+ }
105
+ }
106
+
107
+ @objc public func stopPreventSuspend(_ task: UIBackgroundTaskIdentifier) {
108
+ // The argument is historically meaningless (.invalid); end every
109
+ // prevent-suspend task we actually hold.
110
+ stopAllPreventSuspend()
111
+ }
112
+
113
+ @objc public func startPreventSuspendTimer(_ interval: TimeInterval) {
114
+ stopPreventSuspendTimer()
115
+ preventSuspendTimer = Timer.scheduledTimer(timeInterval: interval,
116
+ target: self,
117
+ selector: #selector(onPreventSuspendTimer),
118
+ userInfo: nil,
119
+ repeats: true)
120
+ }
121
+
122
+ @objc public func stopPreventSuspendTimer() {
123
+ preventSuspendTimer?.invalidate()
124
+ preventSuspendTimer = nil
125
+ }
126
+
127
+ @objc public func onPreventSuspendTimer() {
128
+ pleaseStayAwake()
129
+ }
130
+
131
+ @objc public func pleaseStayAwake() {
132
+ }
133
+
134
+ @objc public func onResume(_ notification: Notification) {
135
+ }
136
+
137
+ @objc public func onSuspend(_ notification: Notification) {
138
+ }
139
+
140
+ @objc public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {}
141
+ @objc public func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {}
142
+ @objc public func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {}
143
+ }
@@ -0,0 +1,101 @@
1
+ import Foundation
2
+ import CoreLocation
3
+
4
+ /// Single delegate for the one shared `CLLocationManager`.
5
+ ///
6
+ /// A `CLLocationManager` has exactly ONE `delegate`. Previously
7
+ /// `BGTrackingService`, `BGGeofenceManager`, `BGLocationRequestService` and
8
+ /// `BGLocationAuthorization` each assigned `manager.delegate = self`, so whoever
9
+ /// ran last silently stole every callback from the others (e.g. starting
10
+ /// geofences right after tracking killed all `onLocation`/persist events, and
11
+ /// the auth callbacks never reached `BGLocationAuthorization`, so
12
+ /// `requestPermission` only resolved on its 20s timeout).
13
+ ///
14
+ /// Routing every callback through this one object lets all four services
15
+ /// participate again. Each downstream handler already self-filters (region
16
+ /// handlers guard by identifier prefix), so fan-out is safe.
17
+ @objc public class BGCLRouter: NSObject, CLLocationManagerDelegate {
18
+
19
+ private static var _sharedInstance: BGCLRouter?
20
+ private static let instanceLock = NSLock()
21
+
22
+ @objc public class func sharedInstance() -> BGCLRouter {
23
+ instanceLock.lock()
24
+ defer { instanceLock.unlock() }
25
+ if _sharedInstance == nil { _sharedInstance = BGCLRouter() }
26
+ return _sharedInstance!
27
+ }
28
+
29
+ private var tracking: BGTrackingService { BGTrackingService.sharedInstance() }
30
+ private var geofences: BGGeofenceManager { BGGeofenceManager.sharedInstance() }
31
+ private var requests: BGLocationRequestService { BGLocationRequestService.sharedInstance() }
32
+ private var authorization: BGLocationAuthorization { BGLocationAuthorization.sharedInstance() }
33
+
34
+ // MARK: - Location updates
35
+
36
+ @objc public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
37
+ // Always feed the request service: it owns getCurrentPosition sampling
38
+ // and watchPosition streams (no-ops when neither is active).
39
+ requests.locationManager(manager, didUpdateLocations: locations)
40
+
41
+ // Tracking owns persist + the `location` event; it also forwards each
42
+ // fix to BGGeofenceManager.setLocation internally. Only when tracking is
43
+ // disabled do we feed geofences directly (geofences-only mode).
44
+ if tracking.isEnabled {
45
+ tracking.locationManager(manager, didUpdateLocations: locations)
46
+ } else if geofences.enabled {
47
+ geofences.locationManager(manager, didUpdateLocations: locations)
48
+ }
49
+ }
50
+
51
+ @objc public func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
52
+ requests.locationManager(manager, didFailWithError: error)
53
+ if tracking.isEnabled {
54
+ tracking.locationManager(manager, didFailWithError: error)
55
+ }
56
+ }
57
+
58
+ // MARK: - Authorization
59
+
60
+ @available(iOS 14.0, *)
61
+ @objc public func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
62
+ // Primary path on iOS 14+. Resolves any in-flight requestPermission().
63
+ authorization.locationManagerDidChangeAuthorization(manager)
64
+ }
65
+
66
+ // Legacy path (< iOS 14). On iOS 14+ this is not called.
67
+ @objc public func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
68
+ authorization.locationManager(manager, didChangeAuthorization: status)
69
+ }
70
+
71
+ // MARK: - Region monitoring (handlers self-filter by identifier)
72
+
73
+ @objc public func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
74
+ geofences.locationManager(manager, didEnterRegion: region)
75
+ }
76
+
77
+ @objc public func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
78
+ // "BGStationary" -> tracking (start-detection); "BGGeofence:" -> geofences.
79
+ tracking.locationManager(manager, didExitRegion: region)
80
+ geofences.locationManager(manager, didExitRegion: region)
81
+ }
82
+
83
+ @objc public func locationManager(_ manager: CLLocationManager, didStartMonitoringFor region: CLRegion) {
84
+ geofences.locationManager(manager, didStartMonitoringFor: region)
85
+ }
86
+
87
+ @objc public func locationManager(_ manager: CLLocationManager, monitoringDidFailFor region: CLRegion?, withError error: Error) {
88
+ tracking.locationManager(manager, monitoringDidFailFor: region, withError: error)
89
+ geofences.locationManager(manager, monitoringDidFailFor: region, withError: error)
90
+ }
91
+
92
+ // MARK: - Pause / resume
93
+
94
+ @objc public func locationManagerDidPauseLocationUpdates(_ manager: CLLocationManager) {
95
+ tracking.locationManagerDidPauseLocationUpdates(manager)
96
+ }
97
+
98
+ @objc public func locationManagerDidResumeLocationUpdates(_ manager: CLLocationManager) {
99
+ tracking.locationManagerDidResumeLocationUpdates(manager)
100
+ }
101
+ }
@@ -0,0 +1,19 @@
1
+ import Foundation
2
+
3
+ @objc public final class BGCallback: NSObject {
4
+
5
+ @objc public var success: Any?
6
+ @objc public var failure: Any?
7
+ @objc public private(set) var options: Any?
8
+
9
+ @objc public init(success: Any?, failure: Any?) {
10
+ self.success = success
11
+ self.failure = failure
12
+ super.init()
13
+ }
14
+
15
+ @objc public convenience init(success: Any?, failure: Any?, options: Any?) {
16
+ self.init(success: success, failure: failure)
17
+ self.options = options
18
+ }
19
+ }
@@ -0,0 +1,440 @@
1
+ import Foundation
2
+
3
+ @objc public class BGConfig: NSObject, NSCoding {
4
+
5
+ private static var _sharedInstance: BGConfig?
6
+ private static let instanceLock = NSLock()
7
+ private static let storageKey = "BGLocationManager_config"
8
+
9
+ // MARK: - Singleton
10
+
11
+ @objc public class func sharedInstance() -> BGConfig {
12
+ instanceLock.lock()
13
+ defer { instanceLock.unlock() }
14
+ if _sharedInstance == nil {
15
+ _sharedInstance = BGConfig.decode() ?? BGConfig()
16
+ }
17
+ return _sharedInstance!
18
+ }
19
+
20
+ @objc public class func decode(config dict: [String: Any]) -> BGConfig {
21
+ let config = BGConfig()
22
+ config.updateWithDictionary(dict)
23
+ return config
24
+ }
25
+
26
+ @objc public class func uptime() -> TimeInterval {
27
+ return ProcessInfo.processInfo.systemUptime
28
+ }
29
+
30
+ @objc public class func userDefaults() -> UserDefaults {
31
+ return UserDefaults.standard
32
+ }
33
+
34
+ // MARK: - Sub-modules
35
+
36
+ @objc public lazy var geolocation: BGGeolocationConfig = BGGeolocationConfig()
37
+ @objc public lazy var http: BGHttpConfig = BGHttpConfig()
38
+ @objc public lazy var logger: BGLoggerConfig = BGLoggerConfig()
39
+ @objc public lazy var app: BGAppConfig = BGAppConfig()
40
+ @objc public lazy var authorization: BGAuthorizationConfig = BGAuthorizationConfig()
41
+ @objc public lazy var persistence: BGPersistenceConfig = BGPersistenceConfig()
42
+ @objc public lazy var activity: BGActivityConfig = {
43
+ let c = BGActivityConfig()
44
+ return c
45
+ }()
46
+
47
+ // MARK: - Top-level state
48
+
49
+ @objc public var enabled: Bool = false
50
+ @objc public var isMoving: Bool = false
51
+ @objc public var trackingMode: Int = 1
52
+ @objc public var didLaunchInBackground: Bool = false
53
+ @objc public var didRequestUpgradeLocationAuthorization: Bool = false
54
+ @objc public var lastLocationAuthorizationStatus: Int = 0
55
+ @objc public var iOSHasWarnedLocationServicesOff: Bool = false
56
+ @objc public var schedulerEnabled: Bool = false
57
+ @objc public var includeDeprecatedPropertiesInDictionary: Bool = false
58
+
59
+ private var listeners: [String: [[String: Any]]] = [:]
60
+ private var plugins: [String: AnyObject] = [:]
61
+ private let listenersLock = NSLock()
62
+ private let persistQueue = DispatchQueue(label: "BGConfig.persist")
63
+ private var pendingPersist: Bool = false
64
+
65
+ // MARK: - Init
66
+
67
+ @objc public override init() {
68
+ super.init()
69
+ commonInitSetup()
70
+ }
71
+
72
+ @objc public required init?(coder: NSCoder) {
73
+ super.init()
74
+ commonInitSetup()
75
+ if let data = coder.decodeObject(forKey: "geolocation") as? Data,
76
+ let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
77
+ geolocation.updateWithDictionary(dict)
78
+ }
79
+ if let data = coder.decodeObject(forKey: "http") as? Data,
80
+ let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
81
+ http.updateWithDictionary(dict)
82
+ }
83
+ if let data = coder.decodeObject(forKey: "logger") as? Data,
84
+ let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
85
+ logger.updateWithDictionary(dict)
86
+ }
87
+ if let data = coder.decodeObject(forKey: "app") as? Data,
88
+ let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
89
+ app.updateWithDictionary(dict)
90
+ }
91
+ enabled = coder.decodeBool(forKey: "enabled")
92
+ isMoving = coder.decodeBool(forKey: "isMoving")
93
+ trackingMode = coder.decodeInteger(forKey: "trackingMode")
94
+ schedulerEnabled = coder.decodeBool(forKey: "schedulerEnabled")
95
+ }
96
+
97
+ @objc public func encode(with coder: NSCoder) {
98
+ if let data = try? JSONSerialization.data(withJSONObject: geolocation.toDictionary()) { coder.encode(data, forKey: "geolocation") }
99
+ if let data = try? JSONSerialization.data(withJSONObject: http.toDictionary()) { coder.encode(data, forKey: "http") }
100
+ if let data = try? JSONSerialization.data(withJSONObject: logger.toDictionary()) { coder.encode(data, forKey: "logger") }
101
+ if let data = try? JSONSerialization.data(withJSONObject: app.toDictionary()) { coder.encode(data, forKey: "app") }
102
+ coder.encode(enabled, forKey: "enabled")
103
+ coder.encode(isMoving, forKey: "isMoving")
104
+ coder.encode(trackingMode, forKey: "trackingMode")
105
+ coder.encode(schedulerEnabled, forKey: "schedulerEnabled")
106
+ }
107
+
108
+ @objc public func commonInitSetup() {
109
+ applyDefaults()
110
+ }
111
+
112
+ @objc public func initPrivateForBoot() {
113
+ didLaunchInBackground = true
114
+ }
115
+
116
+ // MARK: - Defaults
117
+
118
+ @objc public func applyDefaults() {
119
+ applyModuleDefaults()
120
+ }
121
+
122
+ @objc public func applyModuleDefaults() {
123
+ geolocation.applyDefaults()
124
+ http.applyDefaults()
125
+ logger.applyDefaults()
126
+ app.applyDefaults()
127
+ authorization.applyDefaults()
128
+ persistence.applyDefaults()
129
+ }
130
+
131
+ // MARK: - Module access
132
+
133
+ @objc public func allModules() -> [BGConfigModuleBase] {
134
+ return [geolocation, http, logger, app, authorization, persistence]
135
+ }
136
+
137
+ @objc public func moduleMap() -> [String: BGConfigModuleBase] {
138
+ return [
139
+ "geolocation": geolocation,
140
+ "http": http,
141
+ "logger": logger,
142
+ "app": app,
143
+ "authorization": authorization,
144
+ "persistence": persistence
145
+ ]
146
+ }
147
+
148
+ @objc public func module(forKey key: String) -> BGConfigModuleBase? {
149
+ return moduleMap()[key]
150
+ }
151
+
152
+ @objc public func key(forModule module: BGConfigModuleBase) -> String? {
153
+ return moduleMap().first(where: { $0.value === module })?.key
154
+ }
155
+
156
+ // MARK: - Update
157
+
158
+ @objc public func updateWithDictionary(_ dict: [String: Any]) {
159
+ updateWithDictionaryUnsafe(dict)
160
+ persist()
161
+ }
162
+
163
+ @objc public func updateWithDictionaryUnsafe(_ dict: [String: Any]) {
164
+ for (key, value) in dict {
165
+ if let module = module(forKey: key), let subDict = value as? [String: Any] {
166
+ module.updateWithDictionary(subDict)
167
+ } else {
168
+ applyTopLevelValue(value, forKey: key)
169
+ }
170
+ }
171
+ flattenModulesFrom(dict)
172
+ }
173
+
174
+ @objc public func flattenedModules(from dict: [String: Any]) -> [String: Any] {
175
+ var flattened: [String: Any] = [:]
176
+ for module in allModules() {
177
+ let specs = module.propertySpecs()
178
+ for spec in specs {
179
+ if let value = dict[spec.name] {
180
+ flattened[spec.name] = value
181
+ }
182
+ }
183
+ }
184
+ return flattened
185
+ }
186
+
187
+ @objc public func flattenModulesFrom(_ dict: [String: Any]) {
188
+ for module in allModules() {
189
+ let specs = module.propertySpecs()
190
+ var moduleDict: [String: Any] = [:]
191
+ for spec in specs {
192
+ if let value = dict[spec.name] {
193
+ moduleDict[spec.name] = value
194
+ }
195
+ }
196
+ if !moduleDict.isEmpty {
197
+ module.updateWithDictionary(moduleDict)
198
+ }
199
+ }
200
+ }
201
+
202
+ private func applyTopLevelValue(_ value: Any, forKey key: String) {
203
+ switch key {
204
+ case "enabled": if let v = value as? Bool { enabled = v }
205
+ case "isMoving", "is_moving": if let v = value as? Bool { isMoving = v }
206
+ case "trackingMode": if let v = value as? Int { trackingMode = v }
207
+ case "schedulerEnabled": if let v = value as? Bool { schedulerEnabled = v }
208
+ default: break
209
+ }
210
+ }
211
+
212
+ // MARK: - Batch update
213
+
214
+ @objc public func batchUpdate(_ block: (BGConfig) -> Void) {
215
+ block(self)
216
+ persist()
217
+ }
218
+
219
+ // MARK: - Serialization
220
+
221
+ @objc public func toDictionary() -> [String: Any] {
222
+ return toDictionary(false)
223
+ }
224
+
225
+ @objc public func toDictionary(_ includeDeprecated: Bool) -> [String: Any] {
226
+ return toDictionaryUnsafe(includeDeprecated)
227
+ }
228
+
229
+ @objc public func toDictionaryUnsafe(_ includeDeprecated: Bool) -> [String: Any] {
230
+ var dict: [String: Any] = [:]
231
+ dict["enabled"] = enabled
232
+ dict["isMoving"] = isMoving
233
+ dict["trackingMode"] = trackingMode
234
+ dict["schedulerEnabled"] = schedulerEnabled
235
+
236
+ let moduleDict = moduleMap()
237
+ for (key, module) in moduleDict {
238
+ dict[key] = module.toDictionary()
239
+ }
240
+
241
+ for module in allModules() {
242
+ let moduleData = module.toDictionary()
243
+ dict.merge(moduleData) { $1 }
244
+ }
245
+
246
+ return dict
247
+ }
248
+
249
+ @objc public func toJson() -> String? {
250
+ let dict = toDictionary()
251
+ guard let data = try? JSONSerialization.data(withJSONObject: dict) else { return nil }
252
+ return String(data: data, encoding: .utf8)
253
+ }
254
+
255
+ @objc public func currentStateDictionary() -> [String: Any] {
256
+ return toDictionary()
257
+ }
258
+
259
+ // MARK: - Persistence
260
+
261
+ @objc public func persist() {
262
+ persistQueue.async { self.persistUnsafe() }
263
+ }
264
+
265
+ @objc public func forcePersistNow() {
266
+ persistUnsafe()
267
+ }
268
+
269
+ @objc public func persistUnsafe() {
270
+ let data = try? NSKeyedArchiver.archivedData(withRootObject: self, requiringSecureCoding: false)
271
+ UserDefaults.standard.set(data, forKey: BGConfig.storageKey)
272
+ UserDefaults.standard.synchronize()
273
+ }
274
+
275
+ @objc public func _flushDefaults() {
276
+ UserDefaults.standard.synchronize()
277
+ }
278
+
279
+ @objc public class func decode() -> BGConfig? {
280
+ guard let data = UserDefaults.standard.data(forKey: storageKey) else { return nil }
281
+ return try? NSKeyedUnarchiver.unarchivedObject(ofClass: BGConfig.self, from: data)
282
+ }
283
+
284
+ // MARK: - Listeners
285
+
286
+ @objc public func addListener(_ property: String, callback: @escaping ([String: Any]) -> Void) -> Int {
287
+ listenersLock.lock()
288
+ defer { listenersLock.unlock() }
289
+ let token = Int.random(in: 1...Int.max)
290
+ var entry: [String: Any] = ["callback": callback, "token": token]
291
+ if listeners[property] == nil { listeners[property] = [] }
292
+ listeners[property]!.append(entry)
293
+ return token
294
+ }
295
+
296
+ @objc public func onChange(_ block: @escaping ([String: Any]) -> Void) {
297
+ _ = addListener("*", callback: block)
298
+ }
299
+
300
+ @objc public func removeListener(_ property: String, forProperty prop: String) {
301
+ }
302
+
303
+ @objc public func removeListener(_ property: String, token: Int) {
304
+ listenersLock.lock()
305
+ defer { listenersLock.unlock() }
306
+ listeners[property]?.removeAll { ($0["token"] as? Int) == token }
307
+ }
308
+
309
+ @objc public func removeAllListeners(forProperty property: String) {
310
+ listenersLock.lock()
311
+ defer { listenersLock.unlock() }
312
+ listeners.removeValue(forKey: property)
313
+ }
314
+
315
+ @objc public func removeAllListeners() {
316
+ listenersLock.lock()
317
+ defer { listenersLock.unlock() }
318
+ listeners.removeAll()
319
+ }
320
+
321
+ @objc public func emitChangeEvent(_ event: String, value: Any?) {
322
+ listenersLock.lock()
323
+ let list = (listeners[event] ?? []) + (listeners["*"] ?? [])
324
+ listenersLock.unlock()
325
+ let payload: [String: Any] = ["event": event, "value": value ?? NSNull()]
326
+ for entry in list {
327
+ if let cb = entry["callback"] as? ([String: Any]) -> Void {
328
+ cb(payload)
329
+ }
330
+ }
331
+ }
332
+
333
+ @objc public func emitModuleEvents(forPrefixes prefixes: [String]) {
334
+ for prefix in prefixes {
335
+ emitChangeEvent(prefix, value: nil)
336
+ }
337
+ }
338
+
339
+ @objc public func moduleEventPrefixes(forChangedKeypaths keypaths: [String]) -> [String] {
340
+ return keypaths
341
+ }
342
+
343
+ // MARK: - KVO
344
+
345
+ @objc public func setupKVO() {
346
+ }
347
+
348
+ @objc public func setupModuleObservation() {
349
+ }
350
+
351
+ @objc public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
352
+ }
353
+
354
+ // MARK: - Plugins
355
+
356
+ @objc public func registerPlugin(_ plugin: AnyObject) {
357
+ plugins[NSStringFromClass(type(of: plugin))] = plugin
358
+ }
359
+
360
+ @objc public func hasPlugin(forEvent event: String) -> Bool {
361
+ return false
362
+ }
363
+
364
+ // MARK: - Convenience computed properties
365
+
366
+ @objc public func hasSchedule() -> Bool {
367
+ return app.hasSchedule
368
+ }
369
+
370
+ @objc public func hasTriggerActivities() -> Bool {
371
+ return false
372
+ }
373
+
374
+ @objc public func hasValidUrl() -> Bool {
375
+ return http.hasValidUrl
376
+ }
377
+
378
+ @objc public func isLocationTrackingMode() -> Bool {
379
+ return trackingMode == 1
380
+ }
381
+
382
+ @objc public func isFirstBoot() -> Bool {
383
+ return !UserDefaults.standard.bool(forKey: "BGLocationManager_booted")
384
+ }
385
+
386
+ @objc public func didDeviceReboot() -> Bool {
387
+ let stored = UserDefaults.standard.double(forKey: "BGLocationManager_uptime")
388
+ let current = ProcessInfo.processInfo.systemUptime
389
+ return stored > current
390
+ }
391
+
392
+ @objc public func shouldPersist(_ location: BGLocation) -> Bool {
393
+ return persistence.persistMode > 0
394
+ }
395
+
396
+ @objc public func isValue(_ value1: Any?, equalTo value2: Any?) -> Bool {
397
+ guard let v1 = value1, let v2 = value2 else { return value1 == nil && value2 == nil }
398
+ if let n1 = v1 as? NSObject, let n2 = v2 as? NSObject { return n1.isEqual(n2) }
399
+ return false
400
+ }
401
+
402
+ // MARK: - State
403
+
404
+ @objc public func reset() {
405
+ resetUnsafe()
406
+ persist()
407
+ }
408
+
409
+ @objc public func resetUnsafe() {
410
+ enabled = false
411
+ isMoving = false
412
+ applyModuleDefaults()
413
+ }
414
+
415
+ @objc public func resetConfig(_ dict: [String: Any]) {
416
+ resetUnsafe()
417
+ updateWithDictionaryUnsafe(dict)
418
+ persist()
419
+ }
420
+
421
+ @objc public func lockForInvalidLicense() {
422
+ enabled = false
423
+ }
424
+
425
+ @objc public func validateLicense() {
426
+ BGLicenseManager.sharedManager().validateLicense()
427
+ }
428
+
429
+ @objc public func migrateFromLegacyObject(_ legacy: AnyObject) {
430
+ }
431
+
432
+ @objc public func getLocationAuthorizationAlertStrings() -> [String: Any] {
433
+ return [
434
+ "title": "Background Location Access",
435
+ "message": "This app requires location access to function properly.",
436
+ "cancel": "Cancel",
437
+ "settings": "Settings"
438
+ ]
439
+ }
440
+ }