开启严格并发与Swift 6后,PhotosPickerItem转Data报错求助
解决PhotosPickerItem在严格并发模式下的数据竞争警告
问题原因
开启complete严格并发模式并升级到Swift 6后,PhotosPickerItem属于主Actor隔离类型(绑定的@State属性默认归主Actor管理)。在.task(id: selectedPhoto)的非隔离闭包中直接访问selectedPhoto?.loadTransferable(type: Data.self),会触发跨Actor访问隔离值的数据竞争风险警告。
解决方案
确保访问PhotosPickerItem的代码在主Actor上下文中执行,两种简洁实现方式:
方式1:给task闭包标记@MainActor
直接为.task的闭包添加@MainActor属性,强制闭内代码在主Actor上运行:
.task(id: selectedPhoto) { @MainActor in if let data = try? await selectedPhoto?.loadTransferable(type: Data.self) { selectedPhotoData = data } }
方式2:用MainActor.run包裹访问逻辑
若不想给整个闭包标记主Actor,可将访问selectedPhoto的代码包裹在MainActor.run中:
.task(id: selectedPhoto) { await MainActor.run { if let data = try? await selectedPhoto?.loadTransferable(type: Data.self) { selectedPhotoData = data } } }
修正后的完整代码(含拼写错误修复)
View部分
struct AddModel: View { @Environment(ViewModel.self) private var viewModel @Environment(\.dismiss) private var dismiss @State private var showPhotosPicker: Bool = false @State private var selectedPhoto: PhotosPickerItem? @State private var selectedPhotoData: Data? var body: some View { Form { photoSection } .toolbar { toolbarItems } } } // MARK: - View Components extension AddModel { // 修正:原代码误写为AddShoeView @ViewBuilder private var photoSection: some View { Section { VStack(spacing: 12) { ZStack { if let selectedPhotoData, let uiImage = UIImage(data: selectedPhotoData) { Image(uiImage: uiImage) .resizable() .scaledToFill() .frame(width: 150, height: 150) .clipShape(RoundedRectangle(cornerRadius: 12)) } else { Image(systemName: "square.fill") .resizable() .foregroundStyle(.secondary) .frame(width: 150, height: 150) .clipShape(RoundedRectangle(cornerRadius: 12)) } } Text("Add Photo") .font(.callout) .fontWeight(.semibold) .foregroundStyle(.white) .padding(.horizontal, 12) .padding(.vertical, 8) .background(Color(uiColor: .secondarySystemBackground)) .cornerRadius(20) } .frame(maxWidth: .infinity) .photosPicker(isPresented: $showPhotosPicker, selection: $selectedPhoto, photoLibrary: .shared()) .onTapGesture { showPhotosPicker.toggle() } // 应用解决方案:标记闭包为@MainActor .task(id: selectedPhoto) { @MainActor in if let data = try? await selectedPhoto?.loadTransferable(type: Data.self) { selectedPhotoData = data } } } .listRowBackground(Color.clear) .listRowInsets(EdgeInsets()) } @ToolbarContentBuilder private var toolbarItems: some ToolbarContent { ToolbarItem(placement: .confirmationAction) { Button { viewModel.addModel(image: selectedPhotoData) // 修正:原代码误写为viewiewModel dismiss() } label: { Text("Save") } } } }
ViewModel部分
@Observable final class ViewModel { // 修正:原代码多了多余冒号 @ObservationIgnored private var modelContext: ModelContext private(set) var models: [Model] = [] init(modelContext: ModelContext) { self.modelContext = modelContext fetchModels() } func addModel(image: Data?) { let model = Model(image: image) modelContext.insert(model) // 修正:原代码误写为insert(shoe) save() } private func fetchModels() { do { let descriptor = FetchDescriptor<Model>() self.models = try modelContext.fetch(descriptor) } catch { print("Fetching models failed, \(error.localizedDescription)") } } private func save() { do { try modelContext.save() } catch { print("Saving context failed, \(error.localizedDescription)") } fetchModels() } }
内容的提问来源于stack exchange,提问作者Robert Basamac
相关产品推荐
相关产品推荐

