I'm new to swift concurrency, I noticed that my code freezes UI when trying to initialize NSImage, so I decided to do it on the global thread like this:
func chooseImage() {
Task { @MainActor in
let openPanel = NSOpenPanel()
openPanel.prompt = "Select File"
openPanel.allowsMultipleSelection = false
openPanel.canChooseDirectories = false
openPanel.canCreateDirectories = false
openPanel.canChooseFiles = true
openPanel.allowedContentTypes = [.image]
openPanel.begin { (result) -> Void in
if result.rawValue == NSApplication.ModalResponse.OK.rawValue {
guard let url = openPanel.url else {return}
self.fileURL = url
loading = true
DispatchQueue.global(qos:.userInitiated).async
{
guard let chosenImage = NSImage(contentsOf: url) else { return }
DispatchQueue.main.async()
{
autoreleasepool {
self.nsImage = chosenImage
}
self.recongizeText()
loading = false
}
}
}
Does this mean that NSImage is not thread safe? does this warning really mean that there might be some kind of race condition, and if so what would be the best way to fix this?
As mentioned in the comments mixing GCD and Swift Concurrency is bad practice.
This is a translation to async/await
@MainActor
func chooseImage() async {
let openPanel = NSOpenPanel()
openPanel.prompt = "Select File"
openPanel.canChooseDirectories = false
openPanel.allowedContentTypes = [.image]
let result = await openPanel.begin()
guard result == .OK, let url = openPanel.url else { return }
self.fileURL = url
loading = true
let task = Task.detached {
try? Data(contentsOf: url)
}
guard let data = await task.value, let image = NSImage(data: data) else { return }
self.nsImage = image
self.recongizeText()
loading = false
}