@@ -24,44 +24,114 @@ import Foundation
2424import CoreLocation
2525
2626public typealias AuthotizationContinuation = CheckedContinuation < CLAuthorizationStatus , Never >
27+ public typealias AccuracyAuthorizationContinuation = CheckedContinuation < CLAccuracyAuthorization ? , Never >
2728public typealias LocationOnceContinuation = CheckedContinuation < LocationUpdateEvent ? , Error >
29+ public typealias LocationEnabledStream = AsyncStream < LocationEnabledEvent >
2830public typealias LocationStream = AsyncStream < LocationUpdateEvent >
2931public typealias RegionMonitoringStream = AsyncStream < RegionMonitoringEvent >
3032public typealias VisitMonitoringStream = AsyncStream < VisitMonitoringEvent >
3133public typealias HeadingMonitorStream = AsyncStream < HeadingMonitorEvent >
34+ public typealias AuthorizationStream = AsyncStream < AuthorizationEvent >
35+ public typealias AccuracyAuthorizationStream = AsyncStream < AccuracyAuthorizationEvent >
36+ @available ( watchOS, unavailable)
3237public typealias BeaconsRangingStream = AsyncStream < BeaconRangeEvent >
3338
3439public final class AsyncLocationManager {
3540 private var locationManager : CLLocationManager
3641 private var proxyDelegate : AsyncDelegateProxyInterface
3742 private var locationDelegate : CLLocationManagerDelegate
3843
39- public convenience init ( desiredAccuracy: LocationAccuracy = . bestAccuracy) {
44+ public convenience init ( desiredAccuracy: LocationAccuracy = . bestAccuracy, allowsBackgroundLocationUpdates : Bool = false ) {
4045 self . init ( locationManager: CLLocationManager ( ) , desiredAccuracy: desiredAccuracy)
4146 }
4247
43- public init ( locationManager: CLLocationManager , desiredAccuracy: LocationAccuracy = . bestAccuracy) {
48+ public init ( locationManager: CLLocationManager , desiredAccuracy: LocationAccuracy = . bestAccuracy, allowsBackgroundLocationUpdates : Bool = false ) {
4449 self . locationManager = locationManager
4550 proxyDelegate = AsyncDelegateProxy ( )
4651 locationDelegate = LocationDelegate ( delegateProxy: proxyDelegate)
4752 self . locationManager. delegate = locationDelegate
4853 self . locationManager. desiredAccuracy = desiredAccuracy. convertingAccuracy
54+ self . locationManager. allowsBackgroundLocationUpdates = allowsBackgroundLocationUpdates
4955 }
5056
5157
58+ public func getLocationEnabled( ) async -> Bool {
59+ // Though undocumented, `locationServicesEnabled()` must not be called from the main thread. Otherwise,
60+ // we get a runtime warning "This method can cause UI unresponsiveness if invoked on the main thread"
61+ // Therefore, we use `Task.detached` to ensure we're off the main thread.
62+ // Also, we force `try` as we expect no exceptions to be thrown from `locationServicesEnabled()`
63+ try ! await Task . detached { CLLocationManager . locationServicesEnabled ( ) } . value
64+ }
65+
66+ @available ( watchOS 6 . 0 , * )
5267 public func getAuthorizationStatus( ) -> CLAuthorizationStatus {
53- if #available( iOS 14 , * ) {
68+ if #available( iOS 14 , watchOS 7 , * ) {
5469 return locationManager. authorizationStatus
5570 } else {
5671 return CLLocationManager . authorizationStatus ( )
5772 }
5873 }
59-
74+
75+ public func startMonitoringLocationEnabled( ) async -> LocationEnabledStream {
76+ let performer = LocationEnabledMonitoringPerformer ( )
77+ return LocationEnabledStream { stream in
78+ performer. linkContinuation ( stream)
79+ proxyDelegate. addPerformer ( performer)
80+ stream. onTermination = { @Sendable _ in
81+ self . stopMonitoringLocationEnabled ( )
82+ }
83+ }
84+ }
85+
86+ public func stopMonitoringLocationEnabled( ) {
87+ proxyDelegate. cancel ( for: LocationEnabledMonitoringPerformer . self)
88+ }
89+
90+ public func startMonitoringAuthorization( ) async -> AuthorizationStream {
91+ let performer = AuthorizationMonitoringPerformer ( )
92+ return AuthorizationStream { stream in
93+ performer. linkContinuation ( stream)
94+ proxyDelegate. addPerformer ( performer)
95+ stream. onTermination = { @Sendable _ in
96+ self . stopMonitoringAuthorization ( )
97+ }
98+ }
99+ }
100+
101+ public func stopMonitoringAuthorization( ) {
102+ proxyDelegate. cancel ( for: AuthorizationMonitoringPerformer . self)
103+ }
104+
105+ public func startMonitoringAccuracyAuthorization( ) async -> AccuracyAuthorizationStream {
106+ let performer = AccuracyAuthorizationMonitoringPerformer ( )
107+ return AccuracyAuthorizationStream { stream in
108+ performer. linkContinuation ( stream)
109+ proxyDelegate. addPerformer ( performer)
110+ stream. onTermination = { @Sendable _ in
111+ self . stopMonitoringAccuracyAuthorization ( )
112+ }
113+ }
114+ }
115+
116+ public func stopMonitoringAccuracyAuthorization( ) {
117+ proxyDelegate. cancel ( for: AccuracyAuthorizationMonitoringPerformer . self)
118+ }
119+
120+ @available ( iOS 14 , watchOS 7 , * )
121+ public func getAccuracyAuthorization( ) -> CLAccuracyAuthorization {
122+ locationManager. accuracyAuthorization
123+ }
124+
60125 public func updateAccuracy( with newAccuracy: LocationAccuracy ) {
61126 locationManager. desiredAccuracy = newAccuracy. convertingAccuracy
62127 }
63-
128+
129+ public func updateAllowsBackgroundLocationUpdates( with newAllows: Bool ) {
130+ locationManager. allowsBackgroundLocationUpdates = newAllows
131+ }
132+
64133 @available ( * , deprecated, message: " Use new function requestPermission(with:) " )
134+ @available ( watchOS 7 . 0 , * )
65135 public func requestAuthorizationWhenInUse( ) async -> CLAuthorizationStatus {
66136 let authorizationPerformer = RequestAuthorizationPerformer ( )
67137 return await withTaskCancellationHandler ( operation: {
@@ -82,24 +152,37 @@ public final class AsyncLocationManager {
82152
83153#if !APPCLIP
84154 @available ( * , deprecated, message: " Use new function requestPermission(with:) " )
155+ @available ( watchOS 7 . 0 , * )
156+ @available ( iOS 14 , * )
85157 public func requestAuthorizationAlways( ) async -> CLAuthorizationStatus {
86158 let authorizationPerformer = RequestAuthorizationPerformer ( )
87159 return await withTaskCancellationHandler ( operation: {
88160 await withCheckedContinuation { continuation in
161+ #if os(macOS)
89162 if #available( iOS 14 , * ) , locationManager. authorizationStatus != . notDetermined {
90163 continuation. resume ( with: . success( locationManager. authorizationStatus) )
91164 } else {
92165 authorizationPerformer. linkContinuation ( continuation)
93166 proxyDelegate. addPerformer ( authorizationPerformer)
94167 locationManager. requestAlwaysAuthorization ( )
95168 }
169+ #else
170+ if #available( iOS 14 , * ) , locationManager. authorizationStatus != . notDetermined && locationManager. authorizationStatus != . authorizedWhenInUse {
171+ continuation. resume ( with: . success( locationManager. authorizationStatus) )
172+ } else {
173+ authorizationPerformer. linkContinuation ( continuation)
174+ proxyDelegate. addPerformer ( authorizationPerformer)
175+ locationManager. requestAlwaysAuthorization ( )
176+ }
177+ #endif
96178 }
97179 } , onCancel: {
98180 proxyDelegate. cancel ( for: authorizationPerformer. uniqueIdentifier)
99181 } )
100182 }
101183#endif
102184
185+ @available ( watchOS 7 . 0 , * )
103186 public func requestPermission( with permissionType: LocationPermission ) async -> CLAuthorizationStatus {
104187 switch permissionType {
105188 case . always:
@@ -112,7 +195,12 @@ public final class AsyncLocationManager {
112195 return await locationPermissionWhenInUse ( )
113196 }
114197 }
115-
198+
199+ @available ( iOS 14 , watchOS 7 , * )
200+ public func requestTemporaryFullAccuracyAuthorization( purposeKey: String ) async -> CLAccuracyAuthorization ? {
201+ await locationPermissionTemporaryFullAccuracy ( purposeKey: purposeKey)
202+ }
203+
116204 public func startUpdatingLocation( ) async -> LocationStream {
117205 let monitoringPerformer = MonitoringUpdateLocationPerformer ( )
118206 return LocationStream { streamContinuation in
@@ -143,6 +231,7 @@ public final class AsyncLocationManager {
143231 } )
144232 }
145233
234+ @available ( watchOS, unavailable)
146235 public func startMonitoring( for region: CLRegion ) async -> RegionMonitoringStream {
147236 let performer = RegionMonitoringPerformer ( region: region)
148237 return RegionMonitoringStream { streamContinuation in
@@ -154,6 +243,7 @@ public final class AsyncLocationManager {
154243 }
155244 }
156245
246+ @available ( watchOS, unavailable)
157247 public func stopMonitoring( for region: CLRegion ) {
158248 proxyDelegate. cancel ( for: RegionMonitoringPerformer . self) { regionMonitoring in
159249 guard let regionPerformer = regionMonitoring as? RegionMonitoringPerformer else { return false }
@@ -162,6 +252,7 @@ public final class AsyncLocationManager {
162252 locationManager. stopMonitoring ( for: region)
163253 }
164254
255+ @available ( watchOS, unavailable)
165256 public func startMonitoringVisit( ) async -> VisitMonitoringStream {
166257 let performer = VisitMonitoringPerformer ( )
167258 return VisitMonitoringStream { stream in
@@ -174,6 +265,7 @@ public final class AsyncLocationManager {
174265 }
175266 }
176267
268+ @available ( watchOS, unavailable)
177269 public func stopMonitoringVisit( ) {
178270 proxyDelegate. cancel ( for: VisitMonitoringPerformer . self)
179271 locationManager. stopMonitoringVisits ( )
@@ -199,6 +291,7 @@ public final class AsyncLocationManager {
199291 }
200292#endif
201293
294+ @available ( watchOS, unavailable)
202295 public func startRangingBeacons( satisfying: CLBeaconIdentityConstraint ) async -> BeaconsRangingStream {
203296 let performer = BeaconsRangePerformer ( satisfying: satisfying)
204297 return BeaconsRangingStream { stream in
@@ -211,6 +304,7 @@ public final class AsyncLocationManager {
211304 }
212305 }
213306
307+ @available ( watchOS, unavailable)
214308 public func stopRangingBeacons( satisfying: CLBeaconIdentityConstraint ) {
215309 proxyDelegate. cancel ( for: BeaconsRangePerformer . self) { beaconsMonitoring in
216310 guard let beaconsPerformer = beaconsMonitoring as? BeaconsRangePerformer else { return false }
@@ -243,13 +337,45 @@ extension AsyncLocationManager {
243337 let authorizationPerformer = RequestAuthorizationPerformer ( )
244338 return await withTaskCancellationHandler ( operation: {
245339 await withCheckedContinuation { continuation in
246- if #available( iOS 14 , * ) , locationManager. authorizationStatus != . notDetermined {
340+ #if os(macOS)
341+ if #available( iOS 14 , watchOS 7 , * ) , locationManager. authorizationStatus != . notDetermined {
342+ continuation. resume ( with: . success( locationManager. authorizationStatus) )
343+ } else {
344+ authorizationPerformer. linkContinuation ( continuation)
345+ proxyDelegate. addPerformer ( authorizationPerformer)
346+ locationManager. requestAlwaysAuthorization ( )
347+ }
348+ #else
349+ if #available( iOS 14 , watchOS 7 , * ) , locationManager. authorizationStatus != . notDetermined && locationManager. authorizationStatus != . authorizedWhenInUse {
247350 continuation. resume ( with: . success( locationManager. authorizationStatus) )
248351 } else {
249352 authorizationPerformer. linkContinuation ( continuation)
250353 proxyDelegate. addPerformer ( authorizationPerformer)
251354 locationManager. requestAlwaysAuthorization ( )
252355 }
356+ #endif
357+ }
358+ } , onCancel: {
359+ proxyDelegate. cancel ( for: authorizationPerformer. uniqueIdentifier)
360+ } )
361+ }
362+
363+ @available ( iOS 14 , watchOS 7 , * )
364+ private func locationPermissionTemporaryFullAccuracy( purposeKey: String ) async -> CLAccuracyAuthorization ? {
365+ let authorizationPerformer = RequestAccuracyAuthorizationPerformer ( )
366+ return await withTaskCancellationHandler ( operation: {
367+ await withCheckedContinuation { continuation in
368+ if locationManager. authorizationStatus != . notDetermined && locationManager. accuracyAuthorization == . fullAccuracy {
369+ continuation. resume ( with: . success( locationManager. accuracyAuthorization) )
370+ } else if locationManager. authorizationStatus == . notDetermined {
371+ continuation. resume ( with: . success( nil ) )
372+ } else if !CLLocationManager. locationServicesEnabled ( ) {
373+ continuation. resume ( with: . success( nil ) )
374+ } else {
375+ authorizationPerformer. linkContinuation ( continuation)
376+ proxyDelegate. addPerformer ( authorizationPerformer)
377+ locationManager. requestTemporaryFullAccuracyAuthorization ( withPurposeKey: purposeKey)
378+ }
253379 }
254380 } , onCancel: {
255381 proxyDelegate. cancel ( for: authorizationPerformer. uniqueIdentifier)
0 commit comments