fix(macos): reduce permission polling allocation churn (#116092)

* fix(macos): reuse location manager for permission polling

* chore: remove release-owned changelog note

* test(macos): avoid volatile location status comparison
This commit is contained in:
Vincent Koc
2026-07-30 06:10:25 +08:00
committed by GitHub
parent cff7cbfdcb
commit ee7b23dfe6
3 changed files with 22 additions and 9 deletions
@@ -174,7 +174,7 @@ enum PermissionManager {
}
return false
}
let status = CLLocationManager().authorizationStatus
let status = await self.locationAuthorizationStatus()
switch status {
case .authorizedAlways, .authorizedWhenInUse, .authorized:
return true
@@ -198,6 +198,10 @@ enum PermissionManager {
return mic && speech
}
static func locationAuthorizationStatus() async -> CLAuthorizationStatus {
await LocationPermissionRequester.shared.authorizationStatus
}
static func ensureVoiceWakePermissions(interactive: Bool) async -> Bool {
let results = await self.ensure([.microphone, .speechRecognition], interactive: interactive)
return results[.microphone] == true && results[.speechRecognition] == true
@@ -241,7 +245,7 @@ enum PermissionManager {
? .granted : .notGranted
case .location:
let status = CLLocationManager().authorizationStatus
let status = await self.locationAuthorizationStatus()
results[cap] = CLLocationManager.locationServicesEnabled()
&& self.isLocationAuthorized(status: status, requireAlways: false) ? .granted : .notGranted
}
@@ -303,6 +307,11 @@ final class LocationPermissionRequester: NSObject, CLLocationManagerDelegate {
self.manager.delegate = self
}
var authorizationStatus: CLAuthorizationStatus {
// Core Location retains per-manager framework state, so status polling must reuse this process-lifetime owner.
self.manager.authorizationStatus
}
func request(always: Bool) async -> CLAuthorizationStatus {
let current = self.manager.authorizationStatus
if PermissionManager.isLocationAuthorized(status: current, requireAlways: always) {
@@ -155,7 +155,7 @@ private struct LocationAccessSettings: View {
return false
}
let status = CLLocationManager().authorizationStatus
let status = await PermissionManager.locationAuthorizationStatus()
let requireAlways = mode == .always
if PermissionManager.isLocationAuthorized(status: status, requireAlways: requireAlways) {
return true
@@ -24,15 +24,19 @@ struct PermissionManagerTests {
#expect(ensured.keys.count == caps.count)
}
@Test func `location status matches authorization always`() async {
let status = CLLocationManager().authorizationStatus
@Test func `location status matches canonical authorization`() async {
let status = await PermissionManager.locationAuthorizationStatus()
let results = await PermissionManager.grantedStatus([.location])
#expect(results[.location] == (status == .authorizedAlways))
let expected = CLLocationManager.locationServicesEnabled()
&& PermissionManager.isLocationAuthorized(status: status, requireAlways: false)
#expect(results[.location] == expected)
}
@Test func `ensure location non interactive matches authorization always`() async {
let status = CLLocationManager().authorizationStatus
@Test func `ensure location non interactive matches canonical authorization`() async {
let status = await PermissionManager.locationAuthorizationStatus()
let ensured = await PermissionManager.ensure([.location], interactive: false)
#expect(ensured[.location] == (status == .authorizedAlways))
let expected = CLLocationManager.locationServicesEnabled()
&& PermissionManager.isLocationAuthorized(status: status, requireAlways: false)
#expect(ensured[.location] == expected)
}
}