@capacitor/geolocation 7.1.4 → 7.1.5-dev.2

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.
@@ -13,6 +13,6 @@ Pod::Spec.new do |s|
13
13
  s.source_files = 'ios/Sources/**/*.{swift,h,m,c,cc,mm,cpp}'
14
14
  s.ios.deployment_target = '14.0'
15
15
  s.dependency 'Capacitor'
16
- s.dependency 'IONGeolocationLib', spec='~> 1.0'
16
+ #s.dependency 'IONGeolocationLib', spec='1.0.1'
17
17
  s.swift_version = '5.1'
18
18
  end
package/Package.swift CHANGED
@@ -10,20 +10,16 @@ let package = Package(
10
10
  targets: ["GeolocationPlugin"])
11
11
  ],
12
12
  dependencies: [
13
- .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "7.0.0")
13
+ .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "7.0.0"),
14
+ .package(url: "https://github.com/ionic-team/ion-ios-geolocation.git", from: "1.0.2")
14
15
  ],
15
16
  targets: [
16
- .binaryTarget(
17
- name: "IONGeolocationLib",
18
- url: "https://github.com/ionic-team/ion-ios-geolocation/releases/download/1.0.0/IONGeolocationLib.zip",
19
- checksum: "b117d3681a947f5d367e79abdb3bfc9abf7ab070ea5279d7da634ddd2d54ffdb" // sha-256
20
- ),
21
17
  .target(
22
18
  name: "GeolocationPlugin",
23
19
  dependencies: [
24
20
  .product(name: "Capacitor", package: "capacitor-swift-pm"),
25
21
  .product(name: "Cordova", package: "capacitor-swift-pm"),
26
- "IONGeolocationLib"
22
+ .product(name: "IONGeolocationLib", package: "ion-ios-geolocation")
27
23
  ],
28
24
  path: "ios/Sources/GeolocationPlugin"),
29
25
  .testTarget(
@@ -1,5 +1,5 @@
1
1
  import Capacitor
2
- import IONGeolocationLib
2
+ //import IONGeolocationLib
3
3
 
4
4
  private enum GeolocationCallbackType {
5
5
  case requestPermissions
@@ -10,6 +10,7 @@ enum GeolocationError: Error {
10
10
  case permissionRestricted
11
11
  case positionUnavailable
12
12
  case inputArgumentsIssue(target: GeolocationMethod)
13
+ case timeout
13
14
 
14
15
  func toCodeMessagePair() -> (String, String) {
15
16
  ("OS-PLUG-GLOC-\(String(format: "%04d", code))", description)
@@ -29,6 +30,7 @@ private extension GeolocationError {
29
30
  case .watchPosition: 5
30
31
  case .clearWatch: 6
31
32
  }
33
+ case .timeout: 10
32
34
  }
33
35
  }
34
36
 
@@ -39,6 +41,7 @@ private extension GeolocationError {
39
41
  case .locationServicesDisabled: "Location services are not enabled."
40
42
  case .permissionRestricted: "Application's use of location services was restricted."
41
43
  case .inputArgumentsIssue(let target): "The '\(target.rawValue)' input parameters aren't valid."
44
+ case .timeout: "Could not obtain location in time. Try with a higher timeout."
42
45
  }
43
46
  }
44
47
  }
@@ -1,5 +1,6 @@
1
1
  import Capacitor
2
- import IONGeolocationLib
2
+
3
+ // import IONGeolocationLib
3
4
  import UIKit
4
5
 
5
6
  import Combine
@@ -19,14 +20,16 @@ public class GeolocationPlugin: CAPPlugin, CAPBridgedPlugin {
19
20
  private var locationService: (any IONGLOCService)?
20
21
  private var cancellables = Set<AnyCancellable>()
21
22
  private var locationCancellable: AnyCancellable?
23
+ private var timeoutCancellable: AnyCancellable?
22
24
  private var callbackManager: GeolocationCallbackManager?
23
25
  private var statusInitialized = false
24
26
  private var locationInitialized: Bool = false
27
+ private var timeout : Int?
25
28
 
26
29
  override public func load() {
27
30
  self.locationService = IONGLOCManagerWrapper()
28
31
  self.callbackManager = .init(capacitorBridge: bridge)
29
-
32
+
30
33
  NotificationCenter.default.addObserver(
31
34
  self,
32
35
  selector: #selector(appDidBecomeActive),
@@ -40,8 +43,10 @@ public class GeolocationPlugin: CAPPlugin, CAPBridgedPlugin {
40
43
  print("App became active. Restarting location monitoring for watch callbacks.")
41
44
  locationCancellable?.cancel()
42
45
  locationCancellable = nil
46
+ timeoutCancellable?.cancel()
47
+ timeoutCancellable = nil
43
48
  locationInitialized = false
44
-
49
+
45
50
  locationService?.stopMonitoringLocation()
46
51
  locationService?.startMonitoringLocation()
47
52
  bindLocationPublisher()
@@ -55,6 +60,7 @@ public class GeolocationPlugin: CAPPlugin, CAPBridgedPlugin {
55
60
  @objc func getCurrentPosition(_ call: CAPPluginCall) {
56
61
  shouldSetupBindings()
57
62
  let enableHighAccuracy = call.getBool(Constants.Arguments.enableHighAccuracy, false)
63
+ self.timeout = call.getInt("timeout")
58
64
  handleLocationRequest(enableHighAccuracy, call: call)
59
65
  }
60
66
 
@@ -62,6 +68,7 @@ public class GeolocationPlugin: CAPPlugin, CAPBridgedPlugin {
62
68
  shouldSetupBindings()
63
69
  let enableHighAccuracy = call.getBool(Constants.Arguments.enableHighAccuracy, false)
64
70
  let watchUUID = call.callbackId
71
+ self.timeout = call.getInt("timeout")
65
72
  handleLocationRequest(enableHighAccuracy, watchUUID: watchUUID, call: call)
66
73
  }
67
74
 
@@ -77,6 +84,8 @@ public class GeolocationPlugin: CAPPlugin, CAPBridgedPlugin {
77
84
  locationService?.stopMonitoringLocation()
78
85
  locationCancellable?.cancel()
79
86
  locationCancellable = nil
87
+ timeoutCancellable?.cancel()
88
+ timeoutCancellable = nil
80
89
  locationInitialized = false
81
90
  }
82
91
 
@@ -145,7 +154,7 @@ private extension GeolocationPlugin {
145
154
  locationCancellable = locationService?.currentLocationPublisher
146
155
  .catch { [weak self] error -> AnyPublisher<IONGLOCPositionModel, Never> in
147
156
  print("An error was found while retrieving the location: \(error)")
148
-
157
+
149
158
  if case IONGLOCLocationError.locationUnavailable = error {
150
159
  print("Location unavailable (likely due to backgrounding). Keeping watch callbacks alive.")
151
160
  self?.callbackManager?.sendError(.positionUnavailable)
@@ -160,6 +169,15 @@ private extension GeolocationPlugin {
160
169
  .sink(receiveValue: { [weak self] position in
161
170
  self?.callbackManager?.sendSuccess(with: position)
162
171
  })
172
+
173
+ timeoutCancellable = locationService?.locationTimeoutPublisher
174
+ .sink(receiveValue: { [weak self] error in
175
+ if case .timeout = error {
176
+ self?.callbackManager?.sendError(.timeout)
177
+ } else {
178
+ self?.callbackManager?.sendError(.positionUnavailable)
179
+ }
180
+ })
163
181
  }
164
182
 
165
183
  func requestLocationAuthorisation(type requestType: IONGLOCAuthorisationRequestType) {
@@ -200,14 +218,15 @@ private extension GeolocationPlugin {
200
218
  callbackManager?.sendRequestPermissionsSuccess(Constants.AuthorisationStatus.Status.granted)
201
219
  }
202
220
  if shouldRequestCurrentPosition {
203
- locationService?.requestSingleLocation()
221
+ locationService?.requestSingleLocation(timeout: self.timeout)
204
222
  }
205
223
  if shouldRequestLocationMonitoring {
206
- locationService?.startMonitoringLocation()
224
+ locationService?.startMonitoringLocation(timeout: self.timeout)
207
225
  }
208
226
  }
209
227
 
210
228
  func handleLocationRequest(_ enableHighAccuracy: Bool, watchUUID: String? = nil, call: CAPPluginCall) {
229
+ bindLocationPublisher()
211
230
  let configurationModel = IONGLOCConfigurationModel(enableHighAccuracy: enableHighAccuracy)
212
231
  locationService?.updateConfiguration(configurationModel)
213
232
 
@@ -1,5 +1,5 @@
1
1
  import Capacitor
2
- import IONGeolocationLib
2
+ //import IONGeolocationLib
3
3
 
4
4
  extension IONGLOCPositionModel {
5
5
  func toJSObject() -> JSObject {
@@ -0,0 +1,26 @@
1
+ import CoreLocation
2
+
3
+ public enum IONGLOCAuthorisation {
4
+ case notDetermined
5
+ case restricted
6
+ case denied
7
+ case authorisedAlways
8
+ case authorisedWhenInUse
9
+
10
+ init(from status: CLAuthorizationStatus) {
11
+ self = switch status {
12
+ case .notDetermined: .notDetermined
13
+ case .restricted: .restricted
14
+ case .denied: .denied
15
+ case .authorizedAlways: .authorisedAlways
16
+ case .authorizedWhenInUse: .authorisedWhenInUse
17
+ @unknown default: .notDetermined
18
+ }
19
+ }
20
+ }
21
+
22
+ extension CLLocationManager {
23
+ var currentAuthorisationValue: IONGLOCAuthorisation {
24
+ .init(from: authorizationStatus)
25
+ }
26
+ }
@@ -0,0 +1,16 @@
1
+ import CoreLocation
2
+
3
+ public enum IONGLOCAuthorisationRequestType {
4
+ case whenInUse
5
+ case always
6
+
7
+ func requestAuthorization(using locationManager: CLLocationManager) {
8
+ let requestAuthorisation = switch self {
9
+ case .whenInUse:
10
+ locationManager.requestWhenInUseAuthorization
11
+ case .always:
12
+ locationManager.requestAlwaysAuthorization
13
+ }
14
+ requestAuthorisation()
15
+ }
16
+ }
@@ -0,0 +1,44 @@
1
+ import CoreLocation
2
+
3
+ public struct IONGLOCPositionModel: Equatable {
4
+ private(set) public var altitude: Double
5
+ private(set) public var course: Double
6
+ private(set) public var horizontalAccuracy: Double
7
+ private(set) public var latitude: Double
8
+ private(set) public var longitude: Double
9
+ private(set) public var speed: Double
10
+ private(set) public var timestamp: Double
11
+ private(set) public var verticalAccuracy: Double
12
+
13
+ private init(altitude: Double, course: Double, horizontalAccuracy: Double, latitude: Double, longitude: Double, speed: Double, timestamp: Double, verticalAccuracy: Double) {
14
+ self.altitude = altitude
15
+ self.course = course
16
+ self.horizontalAccuracy = horizontalAccuracy
17
+ self.latitude = latitude
18
+ self.longitude = longitude
19
+ self.speed = speed
20
+ self.timestamp = timestamp
21
+ self.verticalAccuracy = verticalAccuracy
22
+ }
23
+ }
24
+
25
+ public extension IONGLOCPositionModel {
26
+ static func create(from location: CLLocation) -> IONGLOCPositionModel {
27
+ .init(
28
+ altitude: location.altitude,
29
+ course: location.course,
30
+ horizontalAccuracy: location.horizontalAccuracy,
31
+ latitude: location.coordinate.latitude,
32
+ longitude: location.coordinate.longitude,
33
+ speed: location.speed,
34
+ timestamp: location.timestamp.millisecondsSinceUnixEpoch,
35
+ verticalAccuracy: location.verticalAccuracy
36
+ )
37
+ }
38
+ }
39
+
40
+ private extension Date {
41
+ var millisecondsSinceUnixEpoch: Double {
42
+ timeIntervalSince1970 * 1000
43
+ }
44
+ }
@@ -0,0 +1,45 @@
1
+ import Combine
2
+
3
+ public protocol IONGLOCServicesChecker {
4
+ func areLocationServicesEnabled() -> Bool
5
+ }
6
+
7
+ public protocol IONGLOCAuthorisationHandler {
8
+ var authorisationStatus: IONGLOCAuthorisation { get }
9
+ var authorisationStatusPublisher: Published<IONGLOCAuthorisation>.Publisher { get }
10
+
11
+ func requestAuthorisation(withType authorisationType: IONGLOCAuthorisationRequestType)
12
+ }
13
+
14
+ public enum IONGLOCLocationError: Error {
15
+ case locationUnavailable
16
+ case timeout
17
+ case other(_ error: Error)
18
+ }
19
+
20
+ public protocol IONGLOCLocationHandler {
21
+ var currentLocation: IONGLOCPositionModel? { get }
22
+ var currentLocationPublisher: AnyPublisher<IONGLOCPositionModel, IONGLOCLocationError> { get }
23
+ var locationTimeoutPublisher: AnyPublisher<IONGLOCLocationError, Never> { get }
24
+ func updateConfiguration(_ configuration: IONGLOCConfigurationModel)
25
+ }
26
+
27
+ public protocol IONGLOCSingleLocationHandler: IONGLOCLocationHandler {
28
+ func requestSingleLocation(timeout: Int?)
29
+ }
30
+
31
+ public protocol IONGLOCMonitorLocationHandler: IONGLOCLocationHandler {
32
+ func startMonitoringLocation(timeout: Int?)
33
+ func startMonitoringLocation()
34
+ func stopMonitoringLocation()
35
+ }
36
+
37
+ public struct IONGLOCConfigurationModel {
38
+ private(set) var enableHighAccuracy: Bool
39
+ private(set) var minimumUpdateDistanceInMeters: Double?
40
+
41
+ public init(enableHighAccuracy: Bool, minimumUpdateDistanceInMeters: Double? = nil) {
42
+ self.enableHighAccuracy = enableHighAccuracy
43
+ self.minimumUpdateDistanceInMeters = minimumUpdateDistanceInMeters
44
+ }
45
+ }
@@ -0,0 +1,133 @@
1
+ import Combine
2
+ import CoreLocation
3
+
4
+ public typealias IONGLOCService = IONGLOCServicesChecker & IONGLOCAuthorisationHandler & IONGLOCSingleLocationHandler & IONGLOCMonitorLocationHandler
5
+
6
+ public struct IONGLOCServicesValidator: IONGLOCServicesChecker {
7
+ public init() {}
8
+
9
+ public func areLocationServicesEnabled() -> Bool {
10
+ CLLocationManager.locationServicesEnabled()
11
+ }
12
+ }
13
+
14
+ public class IONGLOCManagerWrapper: NSObject, IONGLOCService {
15
+ @Published public var authorisationStatus: IONGLOCAuthorisation
16
+ public var authorisationStatusPublisher: Published<IONGLOCAuthorisation>.Publisher { $authorisationStatus }
17
+
18
+ @Published public var currentLocation: IONGLOCPositionModel?
19
+ private var timeoutCancellable: AnyCancellable?
20
+ public var currentLocationPublisher: AnyPublisher<IONGLOCPositionModel, IONGLOCLocationError> {
21
+ Publishers.Merge($currentLocation, currentLocationForceSubject)
22
+ .dropFirst() // ignore the first value as it's the one set on the constructor.
23
+ .tryMap { location in
24
+ guard let location else { throw IONGLOCLocationError.locationUnavailable }
25
+ return location
26
+ }
27
+ .mapError { $0 as? IONGLOCLocationError ?? .other($0) }
28
+ .eraseToAnyPublisher()
29
+ }
30
+
31
+ public var locationTimeoutPublisher: AnyPublisher<IONGLOCLocationError, Never> {
32
+ locationTimeoutSubject.eraseToAnyPublisher()
33
+ }
34
+
35
+ private let currentLocationForceSubject = PassthroughSubject<IONGLOCPositionModel?, Never>()
36
+ private let locationTimeoutSubject = PassthroughSubject<IONGLOCLocationError, Never>()
37
+
38
+ private let locationManager: CLLocationManager
39
+ private let servicesChecker: IONGLOCServicesChecker
40
+
41
+ private var isMonitoringLocation = false
42
+
43
+ public init(locationManager: CLLocationManager = .init(), servicesChecker: IONGLOCServicesChecker = IONGLOCServicesValidator()) {
44
+ self.locationManager = locationManager
45
+ self.servicesChecker = servicesChecker
46
+ self.authorisationStatus = locationManager.currentAuthorisationValue
47
+
48
+ super.init()
49
+ locationManager.delegate = self
50
+ }
51
+
52
+ public func requestAuthorisation(withType authorisationType: IONGLOCAuthorisationRequestType) {
53
+ authorisationType.requestAuthorization(using: locationManager)
54
+ }
55
+
56
+ public func startMonitoringLocation(timeout: Int? = nil) {
57
+ let timeoutValue = timeout ?? 5000
58
+ isMonitoringLocation = true
59
+ self.startTimer(timeout: timeoutValue)
60
+ locationManager.startUpdatingLocation()
61
+ }
62
+
63
+ public func startMonitoringLocation() {
64
+ isMonitoringLocation = true
65
+ locationManager.startUpdatingLocation()
66
+ }
67
+
68
+ public func stopMonitoringLocation() {
69
+ isMonitoringLocation = false
70
+ locationManager.stopUpdatingLocation()
71
+ }
72
+
73
+ public func requestSingleLocation(timeout: Int? = nil) {
74
+ // Fallback to default timeout (5000) when the parameter is nil,
75
+ // since optional defaults in Swift don't apply when nil is explicitly passed.
76
+ let timeoutValue = timeout ?? 5000
77
+ // If monitoring is active meaning the location service is already running
78
+ // and calling .requestLocation() will not trigger a new location update,
79
+ // we can just return the current location.
80
+ if isMonitoringLocation, let location = currentLocation {
81
+ currentLocationForceSubject.send(location)
82
+ return
83
+ }
84
+ self.startTimer(timeout: timeoutValue)
85
+ self.locationManager.requestLocation()
86
+ }
87
+
88
+ private func startTimer(timeout: Int) {
89
+ timeoutCancellable?.cancel()
90
+ timeoutCancellable = nil
91
+ timeoutCancellable = Just(())
92
+ .delay(for: .milliseconds(timeout), scheduler: DispatchQueue.main)
93
+ .sink { [weak self] _ in
94
+ guard let self = self else { return }
95
+ self.locationTimeoutSubject.send(.timeout)
96
+ self.timeoutCancellable?.cancel()
97
+ self.timeoutCancellable = nil
98
+ }
99
+ }
100
+
101
+ public func updateConfiguration(_ configuration: IONGLOCConfigurationModel) {
102
+ locationManager.desiredAccuracy = configuration.enableHighAccuracy ? kCLLocationAccuracyBest : kCLLocationAccuracyThreeKilometers
103
+ configuration.minimumUpdateDistanceInMeters.map {
104
+ locationManager.distanceFilter = $0
105
+ }
106
+ }
107
+
108
+ public func areLocationServicesEnabled() -> Bool {
109
+ servicesChecker.areLocationServicesEnabled()
110
+ }
111
+ }
112
+
113
+ extension IONGLOCManagerWrapper: CLLocationManagerDelegate {
114
+ public func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
115
+ authorisationStatus = manager.currentAuthorisationValue
116
+ }
117
+
118
+ public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
119
+ timeoutCancellable?.cancel()
120
+ timeoutCancellable = nil
121
+ guard let latestLocation = locations.last else {
122
+ currentLocation = nil
123
+ return
124
+ }
125
+ currentLocation = IONGLOCPositionModel.create(from: latestLocation)
126
+ }
127
+
128
+ public func locationManager(_ manager: CLLocationManager, didFailWithError error: any Error) {
129
+ timeoutCancellable?.cancel()
130
+ timeoutCancellable = nil
131
+ currentLocation = nil
132
+ }
133
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capacitor/geolocation",
3
- "version": "7.1.4",
3
+ "version": "7.1.5-dev.2",
4
4
  "description": "The Geolocation API provides simple methods for getting and tracking the current position of the device using GPS, along with altitude, heading, and speed information if available.",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",
@@ -57,6 +57,7 @@
57
57
  "@ionic/prettier-config": "^4.0.0",
58
58
  "@ionic/swiftlint-config": "^2.0.0",
59
59
  "@semantic-release/changelog": "^6.0.3",
60
+ "@semantic-release/exec": "^7.1.0",
60
61
  "@semantic-release/git": "^10.0.1",
61
62
  "@semantic-release/github": "^11.0.1",
62
63
  "@semantic-release/npm": "^12.0.1",