MainThreadPropertyAccessor

Syntactic sugar for setting properties on an ObservableObject on the main thread from within a Task.

Task {
    self.setOnMain.status = "Now I'll never get purple warnings again!"
}

Installation

You can use the Swift Package Manager by declaring MainThreadPropertyAccessor as a dependency in your Package.swift file:

.package(url: "https://github.com/theisegeberg/MainThreadPropertyAccessor", from: "0.1.0")

For more information, see the Swift Package Manager documentation.

The problem

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

  1. Add the protocol MainThreadPropertyAccessor to your ObservableObject
  2. 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" // ?
        }
    }
}

GitHub

View Github