如何在SwiftUI 6中异步扫描文件夹?Swift 6编译错误求助
Swift 6 文件夹异步扫描编译错误修复方案
问题根源
Swift 6 启用了严格并发检查(Strict Concurrency Checking),原代码存在以下不符合新并发规则的问题:
FileManager.enumerator(at:)返回的FileEnumerator并非Sendable类型,在@MainActor隔离的异步方法中使用会触发并发安全警告/错误。- 同步目录枚举逻辑阻塞
MainActor,违背异步编程的非阻塞原则。 Task.isCancelled静态属性在 Swift 6 中推荐改为通过当前任务实例检查状态,避免歧义。
修复后的完整代码
import SwiftUI @MainActor final class FileScannerViewModel: ObservableObject { @Published var scannedCount: Int = 0 @Published var isScanning: Bool = false private var scanTask: Task<Void, Error>? func startScan() { guard !isScanning else { return } scannedCount = 0 isScanning = true // 创建后台任务脱离MainActor,避免阻塞主线程 scanTask = Task.detached { [weak self] in guard let self = self else { return } let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! do { try await self.scanDirectory(at: documentsURL) if !Task.isCancelled { await MainActor.run { self.isScanning = false } } } catch is CancellationError { await MainActor.run { self.isScanning = false } } catch { print("扫描出错:\(error)") await MainActor.run { self.isScanning = false } } } } func cancelScan() { scanTask?.cancel() scanTask = nil isScanning = false } // 标记为非隔离方法,在后台执行扫描逻辑 nonisolated private func scanDirectory(at url: URL) async throws { let fileManager = FileManager.default guard let enumerator = fileManager.enumerator(at: url, includingPropertiesForKeys: nil) else { return } for case let fileURL as URL in enumerator { // 检查当前任务是否被取消,触发取消时抛出错误 try Task.checkCancellation() // 回到MainActor更新UI计数 await MainActor.run { self.scannedCount += 1 } // 让出线程资源,避免持续阻塞 await Task.yield() } } } struct ContentView: View { @StateObject private var viewModel = FileScannerViewModel() var body: some View { VStack(spacing: 20) { Text("已扫描文件数:\(viewModel.scannedCount)") if viewModel.isScanning { ProgressView() .progressViewStyle(.circular) } HStack { Button("开始扫描") { viewModel.startScan() } .disabled(viewModel.isScanning) Button("取消") { viewModel.cancelScan() } .disabled(!viewModel.isScanning) } } .padding() } }
关键修复点说明
- 后台任务执行扫描:用
Task.detached创建脱离MainActor的任务,避免同步枚举阻塞主线程,同时解决FileEnumerator的并发安全问题。 - 非隔离扫描方法:给
scanDirectory添加nonisolated标记,明确该方法不在MainActor隔离环境中运行,符合Swift 6并发规则。 - 规范取消处理:使用
try Task.checkCancellation()替代静态Task.isCancelled,取消时抛出CancellationError,便于统一捕获处理。 - 完善错误与状态重置:捕获取消错误和扫描异常,确保无论何种情况都能正确重置UI状态。
内容的提问来源于stack exchange,提问作者GT4485
相关产品推荐
相关产品推荐

