fix(ios): bound location permission wait

This commit is contained in:
Vincent Koc
2026-07-06 04:14:49 +02:00
parent fdc9aa82d7
commit 75c8753a2c
2 changed files with 30 additions and 7 deletions

View File

@@ -89,6 +89,7 @@ final class LocationService: NSObject, CLLocationManagerDelegate, LocationServic
Task { @MainActor in
let clock = ContinuousClock()
let noPromptDeadline = clock.now.advanced(by: .milliseconds(1500))
var activeUndeterminedDeadline: ContinuousClock.Instant?
var observedPrompt = UIApplication.shared.applicationState != .active
// A slow system prompt must not trigger the no-callback fallback. Once iOS makes
// the app inactive, wait until the user dismisses the prompt and the app returns.
@@ -97,15 +98,27 @@ final class LocationService: NSObject, CLLocationManagerDelegate, LocationServic
let applicationIsActive = UIApplication.shared.applicationState == .active
if !applicationIsActive {
observedPrompt = true
activeUndeterminedDeadline = nil
continue
}
guard observedPrompt || clock.now >= noPromptDeadline else { continue }
let status = self.manager.authorizationStatus
guard Self.shouldCompleteAuthorizationWait(
if Self.shouldCompleteAuthorizationWait(
status: status,
requiresDeterminedStatus: requiresDeterminedStatus)
else { continue }
self.finishAuthorizationWait(waitID: waitID, status: status)
{
self.finishAuthorizationWait(waitID: waitID, status: status)
continue
}
if observedPrompt, activeUndeterminedDeadline == nil {
activeUndeterminedDeadline = clock.now.advanced(by: .milliseconds(1500))
}
let fallbackDeadline = activeUndeterminedDeadline ?? noPromptDeadline
guard clock.now >= fallbackDeadline else { continue }
self.finishAuthorizationWait(
waitID: waitID,
status: status,
allowUndeterminedFallback: true)
}
}
}
@@ -113,16 +126,22 @@ final class LocationService: NSObject, CLLocationManagerDelegate, LocationServic
nonisolated static func shouldCompleteAuthorizationWait(
status: CLAuthorizationStatus,
requiresDeterminedStatus: Bool) -> Bool
requiresDeterminedStatus: Bool,
allowUndeterminedFallback: Bool = false) -> Bool
{
!requiresDeterminedStatus || status != .notDetermined
allowUndeterminedFallback || !requiresDeterminedStatus || status != .notDetermined
}
private func finishAuthorizationWait(waitID: UUID, status: CLAuthorizationStatus) {
private func finishAuthorizationWait(
waitID: UUID,
status: CLAuthorizationStatus,
allowUndeterminedFallback: Bool = false)
{
guard self.authWaitID == waitID, let cont = self.authContinuation else { return }
guard Self.shouldCompleteAuthorizationWait(
status: status,
requiresDeterminedStatus: self.authWaitRequiresDeterminedStatus)
requiresDeterminedStatus: self.authWaitRequiresDeterminedStatus,
allowUndeterminedFallback: allowUndeterminedFallback)
else { return }
self.authWaitID = nil
self.authWaitRequiresDeterminedStatus = false

View File

@@ -101,6 +101,10 @@ import Testing
#expect(LocationService.shouldCompleteAuthorizationWait(
status: .notDetermined,
requiresDeterminedStatus: false))
#expect(LocationService.shouldCompleteAuthorizationWait(
status: .notDetermined,
requiresDeterminedStatus: true,
allowUndeterminedFallback: true))
}
@MainActor @Test func `off mode stops significant location monitoring`() async {