From f740f1c775b3cf2657afe324566aede56055b38d Mon Sep 17 00:00:00 2001 From: Andrew Benson Date: Sat, 9 Aug 2025 21:00:28 -0500 Subject: [PATCH 1/5] PhotoOutput now has public 'avCapturePhotoOutput', as you can't reasonably configure your settings during capture without information like `isShutterSoundSuppressionSupported`. --- Capturer/Basic/Outputs/PhotoOutput.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Capturer/Basic/Outputs/PhotoOutput.swift b/Capturer/Basic/Outputs/PhotoOutput.swift index a673edb..0ad470a 100644 --- a/Capturer/Basic/Outputs/PhotoOutput.swift +++ b/Capturer/Basic/Outputs/PhotoOutput.swift @@ -32,6 +32,7 @@ public final class PhotoOutput: _StatefulObjectBase, OutputNodeType, @unchecked } private let _output = AVCapturePhotoOutput() + public var avCapturePhotoOutput: AVCapturePhotoOutput { _output } public init(quality: AVCapturePhotoOutput.QualityPrioritization = .balanced) { super.init() From cf91c169299f33d3ee3a669f1074c3328cab11e3 Mon Sep 17 00:00:00 2001 From: Andrew Benson Date: Tue, 26 Aug 2025 18:53:48 -0500 Subject: [PATCH 2/5] Added `enableMultitaskingCameraAccessIfSupported` configuration flag --- Capturer/Basic/CaptureBody.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Capturer/Basic/CaptureBody.swift b/Capturer/Basic/CaptureBody.swift index fd1a12c..100a658 100644 --- a/Capturer/Basic/CaptureBody.swift +++ b/Capturer/Basic/CaptureBody.swift @@ -14,6 +14,8 @@ public final class CaptureBodyWrapper: @unchecked Sendable { public var sessionPreset: AVCaptureSession.Preset = .photo + public var enableMultitaskingCameraAccessIfSupported: Bool = false + public init() { } @@ -63,6 +65,10 @@ public final class CaptureBodyWrapper: @unchecked Sendable { self.session.performConfiguration { $0.sessionPreset = configuration.sessionPreset $0.automaticallyConfiguresCaptureDeviceForWideColor = true + if configuration.enableMultitaskingCameraAccessIfSupported, + $0.isMultitaskingCameraAccessSupported { + $0.isMultitaskingCameraAccessEnabled = true + } } } From 61e09c05cd83e2897323a4b4cd8af6fc20fa4ae7 Mon Sep 17 00:00:00 2001 From: Andrew Benson Date: Sat, 28 Mar 2026 17:41:29 -0400 Subject: [PATCH 3/5] Fix Swift 6 strict concurrency errors - PixelBufferView: Use @MainActor Task with explicit capture to avoid sending parameter data race warning on pixelBuffer handler - PhotoTools: Mark completion handler @Sendable to satisfy strict concurrency checking for cross-isolation capture --- Capturer/Basic/PhotoTools.swift | 2 +- Capturer/Basic/Views/PixelBufferView.swift | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Capturer/Basic/PhotoTools.swift b/Capturer/Basic/PhotoTools.swift index 7161393..aaf44e6 100644 --- a/Capturer/Basic/PhotoTools.swift +++ b/Capturer/Basic/PhotoTools.swift @@ -4,7 +4,7 @@ import UIKit enum PhotoTools { @MainActor - public static func save(image: UIImage, completion: @escaping (Result) -> Void) { + public static func save(image: UIImage, completion: @escaping @Sendable (Result) -> Void) { PHPhotoLibrary.shared().performChanges { diff --git a/Capturer/Basic/Views/PixelBufferView.swift b/Capturer/Basic/Views/PixelBufferView.swift index 79d31a4..2565399 100644 --- a/Capturer/Basic/Views/PixelBufferView.swift +++ b/Capturer/Basic/Views/PixelBufferView.swift @@ -36,8 +36,8 @@ public final class PixelBufferView: UIView, PixelBufferDisplaying { subscription = await output .pixelBufferBus .addHandler { [unowned self] pixelBuffer in - Task { - await self.input(pixelBuffer: pixelBuffer) + Task { @MainActor [unowned self] in + self.input(pixelBuffer: pixelBuffer) } } } From 2ba9e69acec630821392bfe5d24bce28d4cb9970 Mon Sep 17 00:00:00 2001 From: Andrew Benson Date: Sat, 28 Mar 2026 20:45:00 -0400 Subject: [PATCH 4/5] Mark CoreImageFilter as @unchecked Sendable CoreImageFilter is constructed once and passed to an actor-isolated context. Adding @unchecked Sendable satisfies the generic Sendable constraint on AnyCVPixelBufferOutput without requiring CIFilter to be Sendable. --- Capturer/Extended/Filters/CoreImageFilter.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Capturer/Extended/Filters/CoreImageFilter.swift b/Capturer/Extended/Filters/CoreImageFilter.swift index 3c8ea2c..1d49e3e 100644 --- a/Capturer/Extended/Filters/CoreImageFilter.swift +++ b/Capturer/Extended/Filters/CoreImageFilter.swift @@ -2,7 +2,7 @@ import Foundation import CoreImage -open class CoreImageFilter: CVPixelBufferModifying { +open class CoreImageFilter: CVPixelBufferModifying, @unchecked Sendable { private lazy var ciContext = MTLCreateSystemDefaultDevice() .map { From ddedf7798523addac23d40d8a41cdd31ef6d6801 Mon Sep 17 00:00:00 2001 From: Andrew Benson Date: Mon, 6 Apr 2026 20:28:37 -0500 Subject: [PATCH 5/5] Fix Swift 6 sending closure data race in PixelBufferView Use weak self captures instead of unowned self in Task and addHandler closures to satisfy strict concurrency sending requirements. --- Capturer/Basic/Views/PixelBufferView.swift | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Capturer/Basic/Views/PixelBufferView.swift b/Capturer/Basic/Views/PixelBufferView.swift index 2565399..d5bd4ec 100644 --- a/Capturer/Basic/Views/PixelBufferView.swift +++ b/Capturer/Basic/Views/PixelBufferView.swift @@ -32,14 +32,15 @@ public final class PixelBufferView: UIView, PixelBufferDisplaying { subscription?.cancel() - Task { - subscription = await output + Task { [weak self] in + let subscription = await output .pixelBufferBus - .addHandler { [unowned self] pixelBuffer in - Task { @MainActor [unowned self] in - self.input(pixelBuffer: pixelBuffer) + .addHandler { [weak self] pixelBuffer in + Task { @MainActor in + self?.input(pixelBuffer: pixelBuffer) } } + self?.subscription = subscription } }