@capgo/background-geolocation 7.0.8 → 7.0.10

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.
@@ -2,6 +2,7 @@ import Capacitor
2
2
  import Foundation
3
3
  import UIKit
4
4
  import CoreLocation
5
+ import AVFoundation
5
6
 
6
7
  // Avoids a bewildering type warning.
7
8
  let null = Optional<Double>.none as Any
@@ -33,63 +34,42 @@ func formatLocation(_ location: CLLocation) -> PluginCallResultData {
33
34
  ]
34
35
  }
35
36
 
36
- class Watcher {
37
- let callbackId: String
38
- let locationManager: CLLocationManager = CLLocationManager()
39
- private let created = Date()
40
- private let allowStale: Bool
41
- private var isUpdatingLocation: Bool = false
42
- init(_ id: String, stale: Bool) {
43
- callbackId = id
44
- allowStale = stale
45
- }
46
- func start() {
47
- // Avoid unnecessary calls to startUpdatingLocation, which can
48
- // result in extraneous invocations of didFailWithError.
49
- if !isUpdatingLocation {
50
- locationManager.startUpdatingLocation()
51
- isUpdatingLocation = true
52
- }
53
- }
54
- func stop() {
55
- if isUpdatingLocation {
56
- locationManager.stopUpdatingLocation()
57
- isUpdatingLocation = false
58
- }
59
- }
60
- func isLocationValid(_ location: CLLocation) -> Bool {
61
- return (
62
- allowStale ||
63
- location.timestamp >= created
64
- )
65
- }
66
- }
67
-
68
37
  @objc(BackgroundGeolocation)
69
38
  public class BackgroundGeolocation: CAPPlugin, CLLocationManagerDelegate {
70
- private var watchers = [Watcher]()
39
+ private var locationManager: CLLocationManager?
40
+ private var created: Date?
41
+ private var allowStale: Bool = false
42
+ private var isUpdatingLocation: Bool = false
43
+ private var activeCallbackId: String?
44
+ private var audioPlayer: AVAudioPlayer?
71
45
 
72
46
  @objc override public func load() {
73
47
  UIDevice.current.isBatteryMonitoringEnabled = true
74
48
  }
75
49
 
76
- @objc func addWatcher(_ call: CAPPluginCall) {
50
+ @objc func start(_ call: CAPPluginCall) {
77
51
  call.keepAlive = true
78
52
 
79
53
  // CLLocationManager requires main thread
80
54
  DispatchQueue.main.async {
55
+ // Check if already started
56
+ if self.locationManager != nil {
57
+ return call.reject("Location tracking already started", "ALREADY_STARTED")
58
+ }
59
+ // Create fresh location manager and initialize date
60
+ self.locationManager = CLLocationManager()
61
+ self.locationManager!.delegate = self
62
+ self.created = Date()
63
+
81
64
  let background = call.getString("backgroundMessage") != nil
82
- let watcher = Watcher(
83
- call.callbackId,
84
- stale: call.getBool("stale") ?? false
85
- )
86
- let manager = watcher.locationManager
87
- manager.delegate = self
65
+ self.allowStale = call.getBool("stale") ?? false
66
+ self.activeCallbackId = call.callbackId
67
+
88
68
  let externalPower = [
89
69
  .full,
90
70
  .charging
91
71
  ].contains(UIDevice.current.batteryState)
92
- manager.desiredAccuracy = (
72
+ self.locationManager!.desiredAccuracy = (
93
73
  externalPower
94
74
  ? kCLLocationAccuracyBestForNavigation
95
75
  : kCLLocationAccuracyBest
@@ -100,11 +80,11 @@ public class BackgroundGeolocation: CAPPlugin, CLLocationManagerDelegate {
100
80
  if distanceFilter == nil || distanceFilter == 0 {
101
81
  distanceFilter = kCLDistanceFilterNone
102
82
  }
103
- manager.distanceFilter = distanceFilter!
104
- manager.allowsBackgroundLocationUpdates = background
105
- manager.showsBackgroundLocationIndicator = background
106
- manager.pausesLocationUpdatesAutomatically = false
107
- self.watchers.append(watcher)
83
+ self.locationManager!.distanceFilter = distanceFilter!
84
+ self.locationManager!.allowsBackgroundLocationUpdates = background
85
+ self.locationManager!.showsBackgroundLocationIndicator = background
86
+ self.locationManager!.pausesLocationUpdatesAutomatically = false
87
+
108
88
  if call.getBool("requestPermissions") != false {
109
89
  let status = CLLocationManager.authorizationStatus()
110
90
  if [
@@ -114,35 +94,35 @@ public class BackgroundGeolocation: CAPPlugin, CLLocationManagerDelegate {
114
94
  ].contains(status) {
115
95
  return (
116
96
  background
117
- ? manager.requestAlwaysAuthorization()
118
- : manager.requestWhenInUseAuthorization()
97
+ ? self.locationManager!.requestAlwaysAuthorization()
98
+ : self.locationManager!.requestWhenInUseAuthorization()
119
99
  )
120
100
  }
121
101
  if background && status == .authorizedWhenInUse {
122
102
  // Attempt to escalate.
123
- manager.requestAlwaysAuthorization()
103
+ self.locationManager!.requestAlwaysAuthorization()
124
104
  }
125
105
  }
126
- return watcher.start()
106
+ return self.startUpdatingLocation()
127
107
  }
128
108
  }
129
109
 
130
- @objc func removeWatcher(_ call: CAPPluginCall) {
110
+ @objc func stop(_ call: CAPPluginCall) {
131
111
  // CLLocationManager requires main thread
132
112
  DispatchQueue.main.async {
133
- if let callbackId = call.getString("id") {
134
- if let index = self.watchers.firstIndex(
135
- where: { $0.callbackId == callbackId }
136
- ) {
137
- self.watchers[index].locationManager.stopUpdatingLocation()
138
- self.watchers.remove(at: index)
139
- }
113
+ self.stopUpdatingLocation()
114
+
115
+ self.locationManager?.delegate = nil
116
+ self.locationManager = nil
117
+ self.created = nil
118
+
119
+ if let callbackId = self.activeCallbackId {
140
120
  if let savedCall = self.bridge?.savedCall(withID: callbackId) {
141
121
  self.bridge?.releaseCall(savedCall)
142
122
  }
143
- return call.resolve()
123
+ self.activeCallbackId = nil
144
124
  }
145
- return call.reject("No callback ID")
125
+ return call.resolve()
146
126
  }
147
127
  }
148
128
 
@@ -169,46 +149,91 @@ public class BackgroundGeolocation: CAPPlugin, CLLocationManagerDelegate {
169
149
  }
170
150
  }
171
151
 
152
+ private func startUpdatingLocation() {
153
+ // Avoid unnecessary calls to startUpdatingLocation, which can
154
+ // result in extraneous invocations of didFailWithError.
155
+ if !isUpdatingLocation, let manager = locationManager {
156
+ manager.startUpdatingLocation()
157
+ isUpdatingLocation = true
158
+ }
159
+ }
160
+
161
+ private func stopUpdatingLocation() {
162
+ if isUpdatingLocation, let manager = locationManager {
163
+ manager.stopUpdatingLocation()
164
+ isUpdatingLocation = false
165
+ }
166
+ }
167
+
168
+ private func isLocationValid(_ location: CLLocation) -> Bool {
169
+ guard let created = created else { return allowStale }
170
+ return (
171
+ allowStale ||
172
+ location.timestamp >= created
173
+ )
174
+ }
175
+
176
+ @objc func playSound(_ call: CAPPluginCall) {
177
+ // Use a background queue for audio loading to avoid blocking the main thread
178
+ DispatchQueue.global(qos: .background).async { [weak self] in
179
+ guard let self = self else { return }
180
+
181
+ let assetPath = "public/" + (call.getString("soundFile") ?? "")
182
+ let assetPathSplit = assetPath.components(separatedBy: ".")
183
+ guard let url = Bundle.main.url(forResource: assetPathSplit[0], withExtension: assetPathSplit[1]) else {
184
+ call.reject("Sound file not found: \(assetPath)")
185
+ return
186
+ }
187
+
188
+ do {
189
+ self.audioPlayer?.stop()
190
+ self.audioPlayer = nil
191
+ self.audioPlayer = try AVAudioPlayer(contentsOf: url)
192
+ self.audioPlayer?.play()
193
+ call.resolve()
194
+ } catch {
195
+ call.reject("Could not play the sound file: \(error.localizedDescription)")
196
+ }
197
+ }
198
+ }
199
+
172
200
  public func locationManager(
173
201
  _ manager: CLLocationManager,
174
202
  didFailWithError error: Error
175
203
  ) {
176
- if let watcher = self.watchers.first(
177
- where: { $0.locationManager == manager }
178
- ) {
179
- if let call = self.bridge?.savedCall(withID: watcher.callbackId) {
180
- if let clErr = error as? CLError {
181
- if clErr.code == .locationUnknown {
182
- // This error is sometimes sent by the manager if
183
- // it cannot get a fix immediately.
184
- return
185
- } else if clErr.code == .denied {
186
- watcher.stop()
187
- return call.reject(
188
- "Permission denied.",
189
- "NOT_AUTHORIZED"
190
- )
191
- }
192
- }
193
- return call.reject(error.localizedDescription, nil, error)
204
+ guard let callbackId = activeCallbackId,
205
+ let call = self.bridge?.savedCall(withID: callbackId) else {
206
+ return
207
+ }
208
+
209
+ if let clErr = error as? CLError {
210
+ if clErr.code == .locationUnknown {
211
+ // This error is sometimes sent by the manager if
212
+ // it cannot get a fix immediately.
213
+ return
214
+ } else if clErr.code == .denied {
215
+ stopUpdatingLocation()
216
+ return call.reject(
217
+ "Permission denied.",
218
+ "NOT_AUTHORIZED"
219
+ )
194
220
  }
195
221
  }
222
+ return call.reject(error.localizedDescription, nil, error)
196
223
  }
197
224
 
198
225
  public func locationManager(
199
226
  _ manager: CLLocationManager,
200
227
  didUpdateLocations locations: [CLLocation]
201
228
  ) {
202
- if let location = locations.last {
203
- if let watcher = self.watchers.first(
204
- where: { $0.locationManager == manager }
205
- ) {
206
- if watcher.isLocationValid(location) {
207
- if let call = self.bridge?.savedCall(withID: watcher.callbackId) {
208
- return call.resolve(formatLocation(location))
209
- }
210
- }
211
- }
229
+ guard let location = locations.last,
230
+ let callbackId = activeCallbackId,
231
+ let call = self.bridge?.savedCall(withID: callbackId) else {
232
+ return
233
+ }
234
+
235
+ if isLocationValid(location) {
236
+ return call.resolve(formatLocation(location))
212
237
  }
213
238
  }
214
239
 
@@ -220,11 +245,7 @@ public class BackgroundGeolocation: CAPPlugin, CLLocationManagerDelegate {
220
245
  // it is on iOS 14 when the permissions dialog is presented, we ignore
221
246
  // it.
222
247
  if status != .notDetermined {
223
- if let watcher = self.watchers.first(
224
- where: { $0.locationManager == manager }
225
- ) {
226
- return watcher.start()
227
- }
248
+ startUpdatingLocation()
228
249
  }
229
250
  }
230
251
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/background-geolocation",
3
- "version": "7.0.8",
3
+ "version": "7.0.10",
4
4
  "description": "Receive geolocation updates even while the app is in the background.",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",