fix(swift): align async helper callsites across iOS and macOS

This commit is contained in:
Peter Steinberger
2026-03-03 03:07:37 +00:00
parent 5cba9a6bab
commit 04a8f97c57
11 changed files with 35 additions and 32 deletions

View File

@@ -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(),

View File

@@ -21,7 +21,7 @@ final class LocationService: NSObject, CLLocationManagerDelegate, LocationServic
self.manager
}
var locationRequestContinuation: CheckedContinuation<CLLocation, Error>? {
var locationRequestContinuation: CheckedContinuation<CLLocation, Swift.Error>? {
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 {

View File

@@ -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,

View File

@@ -1,4 +1,5 @@
import AVFoundation
import OpenClawKit
import ReplayKit
final class ScreenRecordService: @unchecked Sendable {

View File

@@ -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"
}

View File

@@ -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")
}
}

View File

@@ -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<T: Sendable>(

View File

@@ -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()
}
}

View File

@@ -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()
}
}

View File

@@ -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 {

View File

@@ -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() {