When it comes to apps, the fundamental thing that you want to keep in mind is that any data which the UI needs must live on the main thread (i.e. be @MainActor
-isolated). The UI should never have to wait for data it needs it render.
Unfortunately, right now none of Apple's UI frameworks have comprehensively adopted @MainActor
annotations. Perhaps contrary to expectations, SwiftUI is the framework that is most lacking in this regard -- simple closures such as Button
's action callback or gesture callbacks are not annotated as @MainActor
(even though they absolutely will always and only be called on the main actor), meaning that they cannot safely and synchronously manipulate data required by the UI. Even something as simple as a button which increments a counter cannot be safely expressed in SwiftUI as things stand.
Currently, basically the entire Swift ecosystem on Apple platforms is relying on a compiler bug, which causes the compiler to forget to check @MainActor
annotations when expressed using closure syntax. It has been like this for several years now. My biggest hope for the upcoming SDK releases is that Apple will comprehensively audit and annotate closures which are known to be invoked on the main actor, and that it will allow the compiler bug to be fixed so we can actually have consistent static enforcement of global-actor-isolated data.
Until that happens, I'm sorry to inform you that Swift concurrency is not really usable as intended for UI applications on Apple systems. You will see inconsistencies, and frameworks will appear to be basically unusable by the rules that you know, and you (or your team members) will naturally doubt their understanding as a result of that. Let us hope that the situation improves.