Search code examples
iosswiftswiftuidata-bindingconcurrency

Whole class @MainActor or just one function?


I have the following code:

/* @MainActor */ class Locator : NSObject, ObservableObject
{
    private let locationManager: CLLocationManager
    private var authorizationContinuation: CheckedContinuation<CLAuthorizationStatus, Never>?

    @Published var authorizationStatus: CLAuthorizationStatus
    @Published var location: CLLocation?
    @Published var error: Error?

    override init()
    {
        locationManager = CLLocationManager()
        authorizationStatus = locationManager.authorizationStatus

        super.init()

        locationManager.delegate = self
    }

    /* @MainActor */ func checkAuthorizationStatus() async -> CLAuthorizationStatus
    {
        let status = locationManager.authorizationStatus
        if status == .notDetermined
        {
            return await withCheckedContinuation
            { continuation in
                authorizationContinuation = continuation

                locationManager.requestWhenInUseAuthorization()
            }
        }
        else
        {
            authorizationStatus = status <=== WARNING

            return status
        }
    }
}

extension Locator : CLLocationManagerDelegate
{
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager)
    {
        authorizationStatus = manager.authorizationStatus

        authorizationContinuation?.resume(returning: authorizationStatus)
    }

    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error)
    {
        print(error)
        
        self.error = error
        location = nil
    }
}

Without either making the whole class or checkAuthorizationStatus() @MainActor, I get the following run-time warning:

Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:))

What would be your considerations for choosing to make the whole class @MainActor or just the function/'part' that needs it, in this case and in general?

EDIT: To satisfy people that vote to close this questions because it's seems based on opinion: Is there in the above code any run-time difference between the two options?


Solution

  • UI needs to be updated on the MainActor or you get the “publishing changes in the background warning”. If the purpose of the class is to update UI it should be wrapped with MainActor.