I have an ObservableObject class and a SwiftUI view. When a button is tapped, I create a Task and call populate (an async function) from within it. I thought this would execute populate on a background thread but instead the entire UI freezes. Here's my code:
class ViewModel: ObservableObject {
    @Published var items = [String]()
    func populate() async {
        var items = [String]()
        for i in 0 ..< 4_000_000 { /// this usually takes a couple seconds
            items.append("\(i)")
        }
        self.items = items
    }
}
struct ContentView: View {
    @StateObject var model = ViewModel()
    @State var rotation = CGFloat(0)
    var body: some View {
        Button {
            Task {
                await model.populate()
            }
        } label: {
            Color.blue
                .frame(width: 300, height: 80)
                .overlay(
                    Text("\(model.items.count)")
                        .foregroundColor(.white)
                )
                .rotationEffect(.degrees(rotation))
        }
        .onAppear { /// should be a continuous rotation effect
            withAnimation(.easeInOut(duration: 2).repeatForever()) {
                rotation = 90
            }
        }
    }
}
Result:
 
The button stops moving, then suddenly snaps back when populate finishes.
Weirdly, if I move the Task into populate itself and get rid of the async, the rotation animation doesn't stutter so I think the loop actually got executed in the background. However I now get a Publishing changes from background threads is not allowed warning.
func populate() {
    Task {
        var items = [String]()
        for i in 0 ..< 4_000_000 {
            items.append("\(i)")
        }
        self.items = items /// Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates.
    }
}
/// ...
Button {
    model.populate()
}
Result:
 
How can I ensure my code gets executed on a background thread? I think this might have something to do with MainActor but I'm not sure.
 
    
 
     
     
    