ObservableObject + @MainActor
Imagine a view with the following class as a dependency:
class Photos: ObservableObject {
@Published var items: [SpacePhoto] = []
func updateItems() {
let fetched = /* fetch new items */
items = fetched
}
}When we assign to items (second row in updateItems()):
an
objectWillChangeevent will triggerthen the new data will be stored in
items’s storage
When SwiftUI receives an objectWillChange event:
it takes a snapshot of the object (before its storage is updated).
once the storage is updated, SwiftUI compares the previous snapshot with the current value
if the values are different, SwiftUI will update the view (and all other views depending on this
Photosobject)
This flow works great as long as we are on the main thread/loop. If we assign to items in another thread, both SwiftUI snapshots might be taken before the items storage is actually updated, meaning that the snapshot comparison will find unchanged values, thus not updating our views.
New in Swift 5.5, we can declare our ObservableObject class with swift’s @MainActor attribute and, instead of asynchronous callbacks, use the new await syntax to makes sure all operations are executed in the main thread.
New SwiftUI features
task(_:)is a new view modifier that lets you run a task for the view lifetimeit starts when the view appears (similar to
onAppear)it gets cancelled when the view disappears (similar to
onDisappear)
use
AsyncImageto asynchronously load and display an image
AsyncImage(url: photo.url) { image in
image
.resizable()
.aspectRatio(contentMode: .fill)
} placeholder: {
ProgressView()
}
.frame(minWidth: 0, minHeight: 400)use
refreshable(action:)to add pull to refresh capabilities in your viewsas the
actionparameter expects anasyncclosure, the refresh indicator stays visible for the duration of the awaited operation
