Search code examples
iosswiftactormainactor

In swift, do we need "@MainActor in" inside the closure of an other object written inside ViewController?


I have seen code like this

func connectVCAndVM() {
    // Setting up a closure to be called when data in the viewModel changes
    viewModel.dataChanged = { [weak self] in

        Task { @MainActor in
            // Reloading the tableView when data changes
            self?.tableView.reloadData()
        }
    }
}

Do we need "@MainActor in" here? If the task is written inside viewcontroller, wouldn't the task automatically be executed in main thread due to context inheritance? I am learning about Actors so consider this as a newbie doubt.


Solution

  • It depends upon how the view model was written and how dataChanged was defined.

    A view model’s entire purpose is to integrate with a view, and all view interaction must be done from the main thread, so we frequently write our view models so that the closures are invoked from the main thread only (explicitly, so you don’t need to do stuff like you’ve got here).

    Personally, I would be inclined to define the closure as such:

    @MainActor
    class ViewModel {
        var dataChanged: (@Sendable @MainActor () -> Void)?
    
        …
    }
    

    That way, both the view and the view model understand that this closure will be invoked on the main actor.

    Then the view controller can simply:

    func connectVCAndVM() {
        // Setting up a closure to be called when data in the viewModel changes
        viewModel.dataChanged = { [weak self] in
            self?.tableView.reloadData()
        }
    }