`.task(id:_:)` starting in the `Task.isCancelled` state

I'm running into a weird SwiftUI concurrency issue that only seems to happen on a real device (iOS 18.5 in my case), and not in simulator. I have a NavigationSplitView where the Detail view uses .task(id:_:) to perform some async work upon appearance (in my real-world case, a map snapshot). When running this on a real device, the first execution of the task works as expected. However, any task subsequent executions, even ones for the same id, always start in the Task.isCancelled state.

Is there something about .task(id:_:) that I'm misunderstanding, or have I stumbled upon a serious bug here?

import SwiftUI

struct ContentView: View {
    var body: some View {
        TaskBugSplitView()
    }
}

struct TestItem: Identifiable, Hashable {
    var id: Int
    var name: String
}

struct TaskBugSplitView: View {
    @State
    private var selectedItemIndex: [TestItem].Index?
    
    @State
    private var testItems: [TestItem] = [
        TestItem(id: 1, name: "First Item"),
        TestItem(id: 2, name: "Second Item"),
        TestItem(id: 3, name: "Third Item"),
        TestItem(id: 4, name: "Fourth Item"),
        TestItem(id: 5, name: "Fifth Item")
    ]
    
    var body: some View {
        NavigationSplitView {
            List(testItems.indices, id: \.self, selection: $selectedItemIndex) { item in
                Text(testItems[item].name)
            }
        } detail: {
            if let selectedItemIndex {
                TaskBugDetailView(item: testItems[selectedItemIndex])
            } else {
                Text("Select an item")
                    .foregroundStyle(.secondary)
            }
        }
    }
}

struct TaskBugDetailView: View {
    @State var item: TestItem
    @State private var taskResult = "Not started"
    
    var body: some View {
        VStack(spacing: 20) {
            Text("Item: \(item.name)")
                .font(.title2)
            
            Text("Task Result:")
                .font(.headline)
            
            Text(taskResult)
                .foregroundStyle(taskResult.contains("CANCELLED") ? .red : .primary)
            
            Spacer()
        }
        .padding()
        .navigationTitle(item.name)
        .task(id: item.id) {
            
            // BUG: On real devices, Task.isCancelled is true immediately for all tasks
            // after the first successful one, even though the ID has changed
            
            if Task.isCancelled {
                taskResult = "CANCELLED at start"
                print("Task cancelled at start for item \(item.id)")
                return
            }
            
            taskResult = "SUCCESS"
            print("Task completed successfully for item \(item.id)")
        }
    }
}
Answered by djs-code in 852818022

I've filed FB19469650 for this, with a sample project attached,

I've filed FB19469650 for this, with a sample project attached,

I've filed FB19469650

Thanks. I think that’s the best path forward here.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

`.task(id:_:)` starting in the `Task.isCancelled` state
 
 
Q