续Complete Concurrency check enabled and how to resolve warnings
下面是fileImporter的实现...执行在.task()
中ContentView
中花费了太多的时间和内存,因为代码正在主线程上运行.问题是如何将它从主线程分离到后台线程,并在操作后安全地返回主线程.
在ContentView
上使用MainActor
来确保我们没有任何数据竞争(在Xcode中完成的构建设置下的并发判断),并在.task(id: selectedFile)
内调用importData()
.importData()
在extension ContentView
importData()
调用readMetaData
,然后循环导入每个item
@MainActor
struct ContentView: View {
@Environment(\.modelContext) private var modelContext
@State private var disable: Bool = false
@State private var value: Double = 0
@State var progress = ProgressItem()
@State private var importData: Bool = false
@State private var selectedFile: URL? = nil
var body: some View {
VStack {
Button {
disable = true
importData = true
} label: {
Text("Press")
}
.task(id: selectedFile) {
guard importData else {return}
if let selectedFile = selectedFile {
let data = try! String(contentsOf: selectedFile, encoding: .utf8)
await importData(data: data)
importData = false
}
}
.fileImporter(isPresented: $importData, allowedContentTypes: [UTType.plainText], allowsMultipleSelection: false) { result in
do {
guard let selectedFile: URL = try result.get().first else { return }
self.selectedFile = selectedFile
importData = true
//print(selectedFile)
progress.message = ""
progress.progress = 0.0
} catch {
print(error.localizedDescription)
}
}
Text("\(value)")
}
}
}
extension ContentView {
func fetchValue() async -> Double {
//long running task
try? await Task.sleep(nanoseconds: 60)
return 2.0
}
func importData(data: String) async {
let actor = DataHandler(modelContainer: modelContext.container)
// read metadata from file and identify number of items
await actor.readMetadata(data: data)
//loop through number of items from metadata and import each item
let items = await actor.getItems()
for item in items {
await actor.importItemsFrom(data: data)
}
print("... in importFile...")
}
}
@ModelActor
actor DataHandler {
private var metadata = [String]()
private var nItems: Int = 0
func insert<T: PersistentModel>(_ data: T) {
modelContext.insert(data)
try? modelContext.save()
}
func save() {
try? modelContext.save()
}
func getNumItems() -> Int {
return nItems
}
func getItems() -> [String] {
return metadata
}
func readMetadata(data: String) {
//reads metadata and saves metadata locally in metadata
//increment nItems
}
func importItemsFrom(data: String) {
// reads lines from file and persists to database using insert and save
}
}