From 04a8f97c5753330e3273180d484071f9aaee2f5c Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 3 Mar 2026 03:07:37 +0000 Subject: [PATCH] fix(swift): align async helper callsites across iOS and macOS --- apps/ios/Sources/Camera/CameraController.swift | 5 +++-- apps/ios/Sources/Location/LocationService.swift | 7 ++++--- .../Onboarding/GatewayOnboardingView.swift | 3 +++ .../ios/Sources/Screen/ScreenRecordService.swift | 1 + apps/ios/Sources/Voice/VoiceWakeManager.swift | 9 +-------- .../Sources/OpenClaw/CameraCaptureService.swift | 2 +- .../NodeMode/MacNodeLocationService.swift | 5 +++-- apps/macos/Sources/OpenClaw/NotifyOverlay.swift | 7 ++++--- apps/macos/Sources/OpenClaw/TalkOverlay.swift | 7 ++++--- apps/macos/Sources/OpenClaw/UsageCostData.swift | 16 ++++++++-------- .../VoiceWakeOverlayController+Window.swift | 5 +++-- 11 files changed, 35 insertions(+), 32 deletions(-) diff --git a/apps/ios/Sources/Camera/CameraController.swift b/apps/ios/Sources/Camera/CameraController.swift index af12acecfe8..115f36346dc 100644 --- a/apps/ios/Sources/Camera/CameraController.swift +++ b/apps/ios/Sources/Camera/CameraController.swift @@ -120,7 +120,8 @@ actor CameraController { Self.pickCamera(facing: preferFrontCamera ? .front : .back, deviceId: deviceId) }, cameraUnavailableError: CameraError.cameraUnavailable, - mapSetupError: Self.mapMovieSetupError) { output in + mapSetupError: Self.mapMovieSetupError, + operation: { output in var delegate: MovieFileDelegate? let recordedURL: URL = try await withCheckedThrowingContinuation { cont in let d = MovieFileDelegate(cont) @@ -131,7 +132,7 @@ actor CameraController { // Transcode .mov -> .mp4 for easier downstream handling. try await Self.exportToMP4(inputURL: recordedURL, outputURL: mp4URL) return try Data(contentsOf: mp4URL) - } + }) return ( format: format.rawValue, base64: data.base64EncodedString(), diff --git a/apps/ios/Sources/Location/LocationService.swift b/apps/ios/Sources/Location/LocationService.swift index f94ca15f3b6..f974e84cfd4 100644 --- a/apps/ios/Sources/Location/LocationService.swift +++ b/apps/ios/Sources/Location/LocationService.swift @@ -21,7 +21,7 @@ final class LocationService: NSObject, CLLocationManagerDelegate, LocationServic self.manager } - var locationRequestContinuation: CheckedContinuation? { + var locationRequestContinuation: CheckedContinuation? { get { self.locationContinuation } set { self.locationContinuation = newValue } } @@ -65,9 +65,10 @@ final class LocationService: NSObject, CLLocationManagerDelegate, LocationServic desiredAccuracy: desiredAccuracy, maxAgeMs: maxAgeMs, timeoutMs: timeoutMs, - request: { try await self.requestLocationOnce() }) { timeoutMs, operation in + request: { try await self.requestLocationOnce() }, + withTimeout: { timeoutMs, operation in try await self.withTimeout(timeoutMs: timeoutMs, operation: operation) - } + }) } private func awaitAuthorizationChange() async -> CLAuthorizationStatus { diff --git a/apps/ios/Sources/Onboarding/GatewayOnboardingView.swift b/apps/ios/Sources/Onboarding/GatewayOnboardingView.swift index bb0dea241d1..b8b6e267755 100644 --- a/apps/ios/Sources/Onboarding/GatewayOnboardingView.swift +++ b/apps/ios/Sources/Onboarding/GatewayOnboardingView.swift @@ -302,6 +302,7 @@ private struct ManualEntryStep: View { // (GatewaySetupCode) decode raw setup codes. } +@MainActor private func gatewayConnectionStatusLines( appModel: NodeAppModel, gatewayController: GatewayConnectionController) -> [String] @@ -309,6 +310,7 @@ private func gatewayConnectionStatusLines( ConnectionStatusBox.defaultLines(appModel: appModel, gatewayController: gatewayController) } +@MainActor private func resetGatewayConnectionState( appModel: NodeAppModel, connectStatusText: inout String?, @@ -319,6 +321,7 @@ private func resetGatewayConnectionState( connectingGatewayID = nil } +@MainActor @ViewBuilder private func gatewayConnectionStatusSection( appModel: NodeAppModel, diff --git a/apps/ios/Sources/Screen/ScreenRecordService.swift b/apps/ios/Sources/Screen/ScreenRecordService.swift index a987b2165fb..4bea2724dca 100644 --- a/apps/ios/Sources/Screen/ScreenRecordService.swift +++ b/apps/ios/Sources/Screen/ScreenRecordService.swift @@ -1,4 +1,5 @@ import AVFoundation +import OpenClawKit import ReplayKit final class ScreenRecordService: @unchecked Sendable { diff --git a/apps/ios/Sources/Voice/VoiceWakeManager.swift b/apps/ios/Sources/Voice/VoiceWakeManager.swift index cf0898e64d0..46174343bc8 100644 --- a/apps/ios/Sources/Voice/VoiceWakeManager.swift +++ b/apps/ios/Sources/Voice/VoiceWakeManager.swift @@ -441,13 +441,6 @@ final class VoiceWakeManager: NSObject { } } - private static func permissionMessage( - kind: String, - status: AVAudioSession.RecordPermission) -> String - { - self.deniedByDefaultPermissionMessage(kind: kind, isUndetermined: status == .undetermined) - } - private static func permissionMessage( kind: String, status: SFSpeechRecognizerAuthorizationStatus) -> String @@ -466,7 +459,7 @@ final class VoiceWakeManager: NSObject { } } - private static func deniedByDefaultPermissionMessage(kind: String, isUndetermined: Bool) -> String { + private nonisolated static func deniedByDefaultPermissionMessage(kind: String, isUndetermined: Bool) -> String { if isUndetermined { return "\(kind) permission not granted" } diff --git a/apps/macos/Sources/OpenClaw/CameraCaptureService.swift b/apps/macos/Sources/OpenClaw/CameraCaptureService.swift index 83a091bb558..29f532dce2e 100644 --- a/apps/macos/Sources/OpenClaw/CameraCaptureService.swift +++ b/apps/macos/Sources/OpenClaw/CameraCaptureService.swift @@ -164,7 +164,7 @@ actor CameraCaptureService { } private func ensureAccess(for mediaType: AVMediaType) async throws { - if !(await CameraAuthorization.isAuthorized(for: mediaType)) { + if await !(CameraAuthorization.isAuthorized(for: mediaType)) { throw CameraError.permissionDenied(kind: mediaType == .video ? "Camera" : "Microphone") } } diff --git a/apps/macos/Sources/OpenClaw/NodeMode/MacNodeLocationService.swift b/apps/macos/Sources/OpenClaw/NodeMode/MacNodeLocationService.swift index d9214bd77c8..92e8d0cfb1a 100644 --- a/apps/macos/Sources/OpenClaw/NodeMode/MacNodeLocationService.swift +++ b/apps/macos/Sources/OpenClaw/NodeMode/MacNodeLocationService.swift @@ -39,11 +39,12 @@ final class MacNodeLocationService: NSObject, CLLocationManagerDelegate, Locatio desiredAccuracy: desiredAccuracy, maxAgeMs: maxAgeMs, timeoutMs: timeoutMs, - request: { try await self.requestLocationOnce() }) { timeoutMs, operation in + request: { try await self.requestLocationOnce() }, + withTimeout: { timeoutMs, operation in try await self.withTimeout(timeoutMs: timeoutMs) { try await operation() } - } + }) } private func withTimeout( diff --git a/apps/macos/Sources/OpenClaw/NotifyOverlay.swift b/apps/macos/Sources/OpenClaw/NotifyOverlay.swift index 92f2d05c88e..d432f5a9a8e 100644 --- a/apps/macos/Sources/OpenClaw/NotifyOverlay.swift +++ b/apps/macos/Sources/OpenClaw/NotifyOverlay.swift @@ -64,9 +64,10 @@ final class NotifyOverlayController { OverlayPanelFactory.present( window: self.window, isVisible: &self.model.isVisible, - target: target) { window in - self.updateWindowFrame(animate: true) - window.orderFrontRegardless() + target: target) + { window in + self.updateWindowFrame(animate: true) + window.orderFrontRegardless() } } diff --git a/apps/macos/Sources/OpenClaw/TalkOverlay.swift b/apps/macos/Sources/OpenClaw/TalkOverlay.swift index 055829dd0ea..f72871d28ca 100644 --- a/apps/macos/Sources/OpenClaw/TalkOverlay.swift +++ b/apps/macos/Sources/OpenClaw/TalkOverlay.swift @@ -33,9 +33,10 @@ final class TalkOverlayController { OverlayPanelFactory.present( window: self.window, isVisible: &self.model.isVisible, - target: target) { window in - window.setFrame(target, display: true) - window.orderFrontRegardless() + target: target) + { window in + window.setFrame(target, display: true) + window.orderFrontRegardless() } } diff --git a/apps/macos/Sources/OpenClaw/UsageCostData.swift b/apps/macos/Sources/OpenClaw/UsageCostData.swift index 87cef68169b..3327a2a258f 100644 --- a/apps/macos/Sources/OpenClaw/UsageCostData.swift +++ b/apps/macos/Sources/OpenClaw/UsageCostData.swift @@ -77,14 +77,14 @@ struct GatewayCostUsageDay: Codable { init(from decoder: Decoder) throws { let c = try decoder.container(keyedBy: CodingKeys.self) self.date = try c.decode(String.self, forKey: .date) - self.totals = GatewayCostUsageTotals( - input: try c.decode(Int.self, forKey: .input), - output: try c.decode(Int.self, forKey: .output), - cacheRead: try c.decode(Int.self, forKey: .cacheRead), - cacheWrite: try c.decode(Int.self, forKey: .cacheWrite), - totalTokens: try c.decode(Int.self, forKey: .totalTokens), - totalCost: try c.decode(Double.self, forKey: .totalCost), - missingCostEntries: try c.decode(Int.self, forKey: .missingCostEntries)) + self.totals = try GatewayCostUsageTotals( + input: c.decode(Int.self, forKey: .input), + output: c.decode(Int.self, forKey: .output), + cacheRead: c.decode(Int.self, forKey: .cacheRead), + cacheWrite: c.decode(Int.self, forKey: .cacheWrite), + totalTokens: c.decode(Int.self, forKey: .totalTokens), + totalCost: c.decode(Double.self, forKey: .totalCost), + missingCostEntries: c.decode(Int.self, forKey: .missingCostEntries)) } func encode(to encoder: Encoder) throws { diff --git a/apps/macos/Sources/OpenClaw/VoiceWakeOverlayController+Window.swift b/apps/macos/Sources/OpenClaw/VoiceWakeOverlayController+Window.swift index dd19647b761..9575dde52bb 100644 --- a/apps/macos/Sources/OpenClaw/VoiceWakeOverlayController+Window.swift +++ b/apps/macos/Sources/OpenClaw/VoiceWakeOverlayController+Window.swift @@ -23,10 +23,11 @@ extension VoiceWakeOverlayController { "overlay present windowShown textLen=\(self.model.text.count, privacy: .public)") // Keep the status item in “listening” mode until we explicitly dismiss the overlay. AppStateStore.shared.triggerVoiceEars(ttl: nil) - }) { window in + }, + onAlreadyVisible: { window in self.updateWindowFrame(animate: true) window.orderFrontRegardless() - } + }) } private func ensureWindow() {