mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-18 15:01:36 +00:00
* fix(ios): return promptly from noncooperative timeouts * fix(apple): harden async timeout cancellation Co-authored-by: NianJiuZst <180004567+NianJiuZst@users.noreply.github.com> * fix(apple): preserve legacy location requests * style(apple): format location continuation code --------- Co-authored-by: NianJiuZst <180004567+NianJiuZst@users.noreply.github.com> Co-authored-by: Peter Steinberger <steipete@gmail.com>
87 lines
3.2 KiB
Swift
87 lines
3.2 KiB
Swift
import CoreLocation
|
|
import Foundation
|
|
|
|
@MainActor
|
|
public protocol LocationServiceCommon: AnyObject, CLLocationManagerDelegate {
|
|
var locationManager: CLLocationManager { get }
|
|
var locationRequestContinuation: CheckedContinuation<CLLocation, Error>? { get set }
|
|
}
|
|
|
|
@MainActor
|
|
public protocol ConcurrentLocationServiceCommon: LocationServiceCommon, Sendable {
|
|
var locationRequestContinuations: [UUID: CheckedContinuation<CLLocation, Error>] { get set }
|
|
}
|
|
|
|
extension LocationServiceCommon {
|
|
public func configureLocationManager() {
|
|
self.locationManager.delegate = self
|
|
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
|
|
}
|
|
|
|
public func authorizationStatus() -> CLAuthorizationStatus {
|
|
self.locationManager.authorizationStatus
|
|
}
|
|
|
|
public func accuracyAuthorization() -> CLAccuracyAuthorization {
|
|
LocationServiceSupport.accuracyAuthorization(manager: self.locationManager)
|
|
}
|
|
|
|
public func requestLocationOnce() async throws -> CLLocation {
|
|
try await LocationServiceSupport.requestLocation(manager: self.locationManager) { continuation in
|
|
self.locationRequestContinuation = continuation
|
|
}
|
|
}
|
|
}
|
|
|
|
extension ConcurrentLocationServiceCommon {
|
|
public func requestLocationOnce() async throws -> CLLocation {
|
|
// CLLocationManager coalesces requestLocation calls into one pending fix, so every
|
|
// active waiter shares the next delegate result; cancel the platform request only last.
|
|
let requestID = UUID()
|
|
return try await withTaskCancellationHandler {
|
|
try Task.checkCancellation()
|
|
return try await LocationServiceSupport.requestLocation(manager: self.locationManager) { continuation in
|
|
self.locationRequestContinuations[requestID] = continuation
|
|
}
|
|
} onCancel: {
|
|
Task { @MainActor [weak self] in
|
|
guard let self,
|
|
let continuation = self.locationRequestContinuations.removeValue(forKey: requestID)
|
|
else {
|
|
return
|
|
}
|
|
if self.locationRequestContinuations.isEmpty,
|
|
self.locationRequestContinuation == nil
|
|
{
|
|
self.locationManager.stopUpdatingLocation()
|
|
}
|
|
continuation.resume(throwing: CancellationError())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public enum LocationServiceSupport {
|
|
public static func accuracyAuthorization(manager: CLLocationManager) -> CLAccuracyAuthorization {
|
|
if #available(iOS 14.0, macOS 11.0, *) {
|
|
return manager.accuracyAuthorization
|
|
}
|
|
return .fullAccuracy
|
|
}
|
|
|
|
@MainActor
|
|
public static func requestLocation(
|
|
manager: CLLocationManager,
|
|
setContinuation: @escaping (CheckedContinuation<CLLocation, Error>) -> Void) async throws -> CLLocation
|
|
{
|
|
try await withCheckedThrowingContinuation { continuation in
|
|
guard !Task.isCancelled else {
|
|
continuation.resume(throwing: CancellationError())
|
|
return
|
|
}
|
|
setContinuation(continuation)
|
|
manager.requestLocation()
|
|
}
|
|
}
|
|
}
|