Developer Community,
I've noticed a significant change in concurrent task execution behavior when testing on macOS 15 beta 4 & Xcode 16 Beta 4 compared to previous versions. Tasks that previously ran concurrently now appear to execute sequentially, impacting performance and potentially affecting apps relying on concurrent execution.
To illustrate this, I've created a simple toy example:
import SwiftUI
struct ContentView: View {
@State private var results: [String] = []
var body: some View {
VStack {
Button("Run Concurrent Tasks") {
results.removeAll()
runTasks()
}
ForEach(results, id: \.self) { result in
Text(result)
}
}
}
func runTasks() {
Task {
async let task1 = countingTask(name: "Task 1", target: 1000)
async let task2 = countingTask(name: "Task 2", target: 5000)
async let task3 = countingTask(name: "Task 3", target: 1500)
let allResults = await [task1, task2, task3]
results = allResults
}
}
func countingTask(name: String, target: Int) async -> String {
print("\(name) started")
var count = 0
for _ in 0..<target {
count += 1
}
print("\(name) finished. Count: \(count)")
return "\(name) completed. Count: \(count)"
}
}
Observed behavior (macOS 15 Beta 4 & Xcode 16 Beta 4):
Tasks appear to execute sequentially:
Task 1 started
Task 1 finished. Count: 1000
Task 2 started
Task 2 finished. Count: 5000
Task 3 started
Task 3 finished. Count: 1500
Expected behavior:
Tasks start almost simultaneously and finish based on their workload:
Task 1 started
Task 2 started
Task 3 started
Task 1 finished. Count: 1000
Task 3 finished. Count: 1500
Task 2 finished. Count: 5000
Observed behavior in macOS 15 Beta:
The profile reveals that the tasks are executing sequentially. This is evidenced by each task starting only after the previous one has completed.
The code you’ve posted looks like it’s working as designed. Your countingTask(…)
method is isolated to the main actor (more on that below) and is entirely CPU bound. Main-actor-isolated code must run on the main thread, and thus you see everything serialised.
I think the point of confusion relates to your use of SwiftUI. Prior Xcode 16 beta, SwiftUI annotated its body
property as @MainActor
. In Xcode 16 beta it annotates View
that way. This was a sensible change, IMO, but it will result in the behaviour you’re seeing.
We did mention this multiple times at WWDC, and discussed it in the release notes (search any of our platform release notes for “120815051”), but it’s a subtle change and thus it’s easy to miss.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"