From 862989ce762aa2895504d726c43ccbb51dc8e476 Mon Sep 17 00:00:00 2001 From: Mattt Zmuda Date: Wed, 16 Sep 2026 04:49:38 -0700 Subject: [PATCH] Fix task cancellation and captures for Xcode 27 --- App/Controllers/ServerController.swift | 2 +- App/Services/Capture.swift | 18 ++++++++++++------ CLI/StdioProxy.swift | 4 ++-- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/App/Controllers/ServerController.swift b/App/Controllers/ServerController.swift index 9f33527a..aba496d7 100644 --- a/App/Controllers/ServerController.swift +++ b/App/Controllers/ServerController.swift @@ -322,7 +322,7 @@ final class ServerController: ObservableObject { } init() { - Task { + Task { [self] in // Initialize bindings from AppStorage before the server starts. await networkManager.updateServiceBindings(self.currentServiceBindings) await networkManager.updateDisabledTools(self.disabledTools, generation: 0) diff --git a/App/Services/Capture.swift b/App/Services/Capture.swift index 55b2456b..eeef1439 100644 --- a/App/Services/Capture.swift +++ b/App/Services/Capture.swift @@ -279,8 +279,14 @@ final class CaptureService: NSObject, Service { captureSession.startRunning() Task { @MainActor in - if delay > 0 { - try await Task.sleep(for: .seconds(delay)) + do { + if delay > 0 { try await Task.sleep(for: .seconds(delay)) } + } catch { + timeoutTask.cancel() + captureSession.stopRunning() + self.currentPhotoDelegate = nil + await resumeOnce(.failure(error), nil) + return } let settings = AVCapturePhotoSettings() @@ -291,7 +297,7 @@ final class CaptureService: NSObject, Service { let delegate = PhotoCaptureDelegate( format: format, quality: quality, - completion: { [weak self] result in + completion: { [weak self = self] result in Task { @MainActor in timeoutTask.cancel() captureSession.stopRunning() @@ -384,10 +390,10 @@ final class CaptureService: NSObject, Service { return try await withCheckedThrowingContinuation { continuation in Task { - try await Task.sleep(for: .seconds(duration + 0.5)) - recorder.stop() - + defer { recorder.stop() } do { + try await Task.sleep(for: .seconds(duration + 0.5)) + recorder.stop() let audioData = try Data(contentsOf: tempURL) try FileManager.default.removeItem(at: tempURL) let audioValue = Value.data(mimeType: format.mimeType, audioData) diff --git a/CLI/StdioProxy.swift b/CLI/StdioProxy.swift index a2c5b20b..4da36553 100644 --- a/CLI/StdioProxy.swift +++ b/CLI/StdioProxy.swift @@ -46,7 +46,7 @@ actor StdioProxy { connection.start(queue: .main) - connection.stateUpdateHandler = { state in + connection.stateUpdateHandler = { [weak self] state in Task { [weak self] in await self?.handleConnectionState(state, continuation: nil, connectionState: nil) } @@ -55,7 +55,7 @@ actor StdioProxy { try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in let connectionState = ConnectionState() - connection.stateUpdateHandler = { state in + connection.stateUpdateHandler = { [weak self] state in Task { [weak self] in await self?.handleConnectionState( state,