ObservableObject is often used as a kind of controller for SwiftUI View. After structured concurrency (async/await) was introduced we’ll often need to set the value of a @Published property inside of a ObservableObject. This means wrapping the code in DispatchQueue.main.async { ... } or using a @MainActor annotated method. This can be done, but it’s a lot of code.
The solution
A protocol with no requirements that provides a default extension that exposes only one computed property: .setOnMain. This returns an object that can be subscripted into based on the KeyPath‘s of the ObservableObject that implements the protocol.
Usage
Add the protocol MainThreadPropertyAccessor to your ObservableObject
Use self.setOnMain.somePropertyOfYourObject = newValue to set properties on your ObservableObject
class AnObservableObject: ObservableObject, MainThreadPropertyAccessor {
@Published var status:String = ""
func updateStatus() {
Task {
// Do some async work
self.status = "Will update the UI from a background thread" // Warning!
self.setOnMain.status = "Will update the UI from the main dispatch queue" // 😘
}
}
}
MainThreadPropertyAccessor
Syntactic sugar for setting properties on an ObservableObject on the main thread from within a Task.
Installation
You can use the Swift Package Manager by declaring MainThreadPropertyAccessor as a dependency in your
Package.swift
file:For more information, see the Swift Package Manager documentation.
The problem
ObservableObject
is often used as a kind of controller for SwiftUIView
. After structured concurrency (async/await) was introduced we’ll often need to set the value of a@Published
property inside of aObservableObject
. This means wrapping the code inDispatchQueue.main.async { ... }
or using a@MainActor
annotated method. This can be done, but it’s a lot of code.The solution
A protocol with no requirements that provides a default extension that exposes only one computed property:
.setOnMain
. This returns an object that can be subscripted into based on theKeyPath
‘s of theObservableObject
that implements the protocol.Usage
MainThreadPropertyAccessor
to yourObservableObject
self.setOnMain.somePropertyOfYourObject = newValue
to set properties on yourObservableObject