Post

Replies

Boosts

Views

Activity

Manipulating SwiftUI view state in a Task closure (button action)
In the session Discover concurrency in SwiftUI (at 19:48), the following sample code is presented as an example of starting an async task from a Button action (which is synchronous): struct SavePhotoButton: View { var photo: SpacePhoto @State private var isSaving = false var body: some View { Button { Task { isSaving = true await photo.save() isSaving = false } } label: { Text("Save") // … } // … } } (The code on the slide uses async { … }. I replaced this with the current Task { … } syntax.) I'm wondering if manipulating view state from inside the task closure like this is allowed. In fact, when you compile this with -Xfrontend -warn-concurrency, you get compiler warnings on all three lines in the task closure: Task { // warning: Cannot use parameter 'self' with a non-sendable type 'SavePhotoButton' from concurrently-executed code isSaving = true // same warning await photo.save() // same warning isSaving = false } You have to mark the view as @MainActor to get rid of the warnings: @MainActor struct SavePhotoButton: View { … } Questions: Can you confirm that the sample code is invalid without the @MainActor annotation on the view? How does the Task { … } closure guarantees that it runs on the main actor. I know that Task { … } inherits the current actor execution context, but how does that work here? My guess: View.body is annotated with @MainActor in the SwiftUI module interface Actor context inheritance is based on the lexical scope, so the fact that the Task { … } closure is inside body is enough for it to inherit that context, even if it's called from another context. Is this correct? Thanks.
0
0
2.1k
Nov ’21