I'm having trouble making async functions run in background threads (to prevent blocking the main thread).
Below is a method that takes about 5 seconds to run.
From what I've learned, it seemed like making the function async
and marking it with await
on function call would be enough. But it doesn't work as intended and still freezes up the UI.
EDIT Since it's stated that Swift 5.5 concurrency can replace DispatchQueue, I am trying to find a way to do this with only Async/Await.
EDIT_2 I did try removing the @MainActor wrapper, but it still seem to run on the main thread.
NumberManager.swift
@MainActor class NumberManager: ObservableObject {
@Published var numbers: [Double]?
func generateNumbers() async {
var numbers = [Double]()
numbers = (1...10_000_000).map { _ in Double.random(in: -10...10) }
self.numbers = numbers
// takes about 5 seconds to run...
} }
ContentView
struct ContentView: View {
@StateObject private var numberManager = NumberManager()
var body: some View{
TabView{
VStack{
DetailView(text: isNumbersValid ? "First number is: \(numberManager.numbers![0])" : nil)
.onAppear() {
Task {
// Runs in the main thread, freezing up the UI until it completes.
await numberManager.generateNumbers()
}
}
}
.tabItem {
Label("One", systemImage: "list.dash")
}
Text("Hello")
.tabItem {
Label("Two", systemImage: "square.and.pencil")
}
}
}
var isNumbersValid: Bool{
numberManager.numbers != nil && numberManager.numbers?.count != 0
} }
What I've tried...
I've tried a few things, but the only way that made it run in the background was changing the function as below. But I know that using Task.detached should be avoided unless it's absolutely necessary, and I didn't think this is the correct use-case.
func generateNumbers() async {
Task.detached {
var numbers = [Double]()
numbers = (1...10_000_000).map { _ in Double.random(in: -10...10) }
await MainActor.run { [numbers] in
self.numbers = numbers
}
}