如何将VNDocumentCameraViewControllerDelegate适配Swift 6并发模型
Swift 6下适配VNDocumentCameraViewControllerDelegate的并发问题解决方案
针对你遇到的Swift 6并发模型适配问题,以下是可行的解决方案:
核心修改思路
避开类级别@MainActor导致的协议一致性冲突,同时通过显式切换主Actor上下文处理UI操作,再通过Sendable合规性解决单例的并发安全问题。
修改后的完整代码
fileprivate class DocumentScanDelegate: NSObject, VNDocumentCameraViewControllerDelegate, @unchecked Sendable { static let shared = DocumentScanDelegate() var compressionQuality: CGFloat = 1 var onScanSuccess: (UIImage) -> Void = { _ in } func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFinishWith scan: VNDocumentCameraScan) { MainActor.run { controller.dismiss(animated: true) guard scan.pageCount >= 1 else { return } let lastPage = scan.imageOfPage(at: scan.pageCount - 1) let compressed = lastPage.compressed(quality: self.compressionQuality) self.onScanSuccess(compressed) } } func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFailWithError error: Error) { MainActor.run { controller.dismiss(animated: true) } } func documentCameraViewControllerDidCancel(_ controller: VNDocumentCameraViewController) { MainActor.run { controller.dismiss(animated: true) } } }
关键修改说明
添加
@unchecked Sendable合规性
由于类继承自NSObject(未原生遵循Sendable),但我们可以保证类内可变状态(compressionQuality、onScanSuccess)的访问仅在主Actor上下文执行,因此用@unchecked Sendable标记类,解决单例shared的并发安全错误。用
MainActor.run包裹UI操作VNDocumentCameraViewControllerDelegate的回调方法可能在后台线程触发,而dismiss是UIKit的主Actor隔离方法,必须在主线程执行。通过MainActor.run将所有UI相关逻辑切换到主Actor上下文,既解决了跨Actor调用的错误,又避免了类级别@MainActor与协议非隔离要求的冲突。规避
VNDocumentCameraScan的Sendable问题
虽然VNDocumentCameraScan未遵循Sendable,但我们仅在回调方法的局部作用域内读取它的属性,且所有访问都被包裹在MainActor.run中,不会产生并发访问风险,Swift 6会认可这种用法。
内容的提问来源于stack exchange,提问作者OMGPOP
相关产品推荐
相关产品推荐

