I have a SwiftUI application where I am animating a Text view and have two buttons to trigger a heavy load computation in the background. I am using both DispatchQueue and Task to perform this heavy computation, and I’m interested in understanding the differences and best practices for using these two methods in SwiftUI.
import SwiftUI
struct ContentView: View {
@State private var scale: CGFloat = 1.0
var body: some View {
VStack {
Text("Animating Text")
.font(.largeTitle)
.scaleEffect(scale)
.animation(
Animation.easeInOut(duration: 1.5)
.repeatForever(autoreverses: true)
)
.onAppear {
self.scale = 1.5
}
Button("Run With DispatchQueue") {
DispatchQueue.global(qos: .background).async {
performHeavyLoad(duration: 60 * 5)
}
}
Button("Run With Task") {
Task(priority: .background) {
// Example: High-load computation on the main thread for 5 minutes
performHeavyLoad(duration: 60 * 5)
}
}
}
}
// Function to perform heavy computation on the calling thread
nonisolated
func performHeavyLoad(duration: TimeInterval) {
let start = Date()
let end = start.addingTimeInterval(duration)
while Date() < end {
for _ in 0..<1_000_000 {
_ = sqrt(12345.6789)
}
}
}
}
In this example, Button("Run With Task") will cause UI unresponsive, but Button("Run With DispatchQueue") will not.
2
Answers
Run With Task
executes stuff on the main thread, so it’s the cause of unresponsive behavior.Run With DispatchQueue
on other hands, executes on the background thread as you declared.Simply print out the thread to check that:
It’s running on the main thread because the
Task
is inherited from its parent context, in this case, it’s theMain Actor
.From my perspective, you should never mix
DispatchQueue
style withAsync Await
. You may want to re-write the function to:I think
Task.detached {..}
(even though it’s an undesirable technique here) will do the job.