Search code examples
swiftswift-concurrency

MainActor vs MainActor(unsafe)


We can create a @MainActor Task like this:

Task { @MainActor in
    print("hi")
}

But also with @MainActor(unsafe) like this:

Task { @MainActor(unsafe) in
    print("hi")
}

What's the difference between these two methods?


Solution

  • The main purpose of @MainActor(unsafe) is to make incremental adoption of concurrency easier. You can read this in detail explained in the proposal. If you mark your type as @MainActor(unsafe) if you try to access properties and methods synchronously in code that hasn't adopted async/await without breaking anything.

    For example following code won't compile with only @MainActor as here actor isolated property accessed from synchronous context:

    @MainActor
    class SomeViewModel {
        let value1 = 0
        var value2 = 0
        func getValue2() -> Int { value2 }
        func setValue2(_ newValue: Int) { value2 = newValue }
    }
    
    func doSomething(with viewModel: SomeViewModel) {
        _ = viewModel.value1
        _ = viewModel.value2 // ERROR: Property 'value2' isolated to global actor 'MainActor' can not be referenced from this synchronous context
        _ = viewModel.getValue2() // ERROR: Call to main actor-isolated instance method 'getValue2()' in a synchronous nonisolated context
        viewModel.setValue2(3) // ERROR: Call to main actor-isolated instance method 'setValue2' in a synchronous nonisolated context
    }
    

    But if you change @MainActor to @MainActor(unsafe) there are no more build errors. Note that @MainActor(unsafe) is only applicable to swift 5 code to make incremental adoption of concurrency easier and in swift 6 it won't have any effect and would behave the same as @MainActor.

    Also @MainActor(unsafe) is interchangeable with @preconcurrency @MainActor. You also can use -warn-concurrency compiler option to get all the errors that you would get with only @MainActor as warnings.