Call to main actor-isolated initializer 'init()' in a synchronous nonisolated context

Can't understand why this compiles

extension HomeReducer {
    @MainActor
    @Observable
    final class State: BaseState {
        var isPresentingSubscription = false
    }
}

class BaseState {}

but this gives an error

Call to main actor-isolated initializer 'init()' in a synchronous nonisolated context

extension HomeReducer {
    @MainActor
    @Observable
    final class State {
        var isPresentingSubscription = false
    }
}
extension DependencyValues {
    var homeView: @Sendable () -> HomeView {
        HomeKey().value
    }

    private struct HomeKey: DependencyKey {
        typealias Value = @Sendable () -> HomeView

        let liveValue: Value = {
            HomeView(store: Store(initialState: HomeReducer.State(), reducer: HomeReducer()))
        }

        let testValue: Value = {
            fatalError("testValue not implemented")
        }

        var previewValue: Value = {
            fatalError("previewValue not implemented")
        }
    }
}
Answered by kmisiag in 770364022

Sure thing! Here's example that doesn't depend on other types.

This compiles:

@MainActor
@Observable
final class State: BaseState {
    var isPresentingSubscription = false
}

class BaseState {}

private struct HomeKey {
    typealias Value = @Sendable () -> State

    let liveValue: Value = {
        State()
    }

    let testValue: Value = {
        fatalError("testValue not implemented")
    }

    var previewValue: Value = {
        fatalError("previewValue not implemented")
    }
}

This doesn't:

@MainActor
@Observable
final class State {
    var isPresentingSubscription = false
}

class BaseState {}

private struct HomeKey {
    typealias Value = @Sendable () -> State

    let liveValue: Value = {
        State()
    }

    let testValue: Value = {
        fatalError("testValue not implemented")
    }

    var previewValue: Value = {
        fatalError("previewValue not implemented")
    }
}

It shows error in initialisation of State object that says

Call to main actor-isolated initializer 'init()' in a synchronous nonisolated context

It’s hard to say without more context. I can’t compile the code you posted because it depends on a bunch of types that you don’t show. Can you boil this down into a self-contained example?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Accepted Answer

Sure thing! Here's example that doesn't depend on other types.

This compiles:

@MainActor
@Observable
final class State: BaseState {
    var isPresentingSubscription = false
}

class BaseState {}

private struct HomeKey {
    typealias Value = @Sendable () -> State

    let liveValue: Value = {
        State()
    }

    let testValue: Value = {
        fatalError("testValue not implemented")
    }

    var previewValue: Value = {
        fatalError("previewValue not implemented")
    }
}

This doesn't:

@MainActor
@Observable
final class State {
    var isPresentingSubscription = false
}

class BaseState {}

private struct HomeKey {
    typealias Value = @Sendable () -> State

    let liveValue: Value = {
        State()
    }

    let testValue: Value = {
        fatalError("testValue not implemented")
    }

    var previewValue: Value = {
        fatalError("previewValue not implemented")
    }
}

It shows error in initialisation of State object that says

Call to main actor-isolated initializer 'init()' in a synchronous nonisolated context

Btw can I uncheck solved on this thread? I've done it by accident...

can I uncheck solved on this thread?

No )-: Normally I’d suggest filing a bug about that but in this case we already have one (r. 113568518).


Coming back to your main issue, thanks for the isolated test code. Here’s a much more reduced example:

private class BaseState {}

@MainActor
private final class State: BaseState { }

private struct HomeKey {
    let s = State()
}

versus:

@MainActor
private final class State { }

private struct HomeKey {
    let s = State()
    //      ^ error: call to main actor-isolated initializer 'init()' in a synchronous nonisolated context
}

Note I added a bunch of private access control modifiers so that I could compile both versions in the same project.

With this, the issue is a bit clearer. In the working case, State inherits its initialiser from BaseState, which is not bound to the main actor. Thus, you can call it from a synchronous context like HomeKey.

In contrast, in the failing case, the initialiser for State comes directly from State itself, which is main actor bound, thus calls from a synchronous context fail.

I’ll admit that this emergent behaviour is a bit weird, but I don’t think it’s unreasonable.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

I've stumbled upon a similar issue. I have this implementation which breaks with the same issue:

@MainActor
private class FirstClass {}

@MainActor
private class SecondClass {

    let instance: FirstClass

    init(with instance: FirstClass = .init()) {
        //                           ^ error: call to main actor-isolated initializer 'init()' in a synchronous nonisolated context
        self.instance = instance
    }
}

If I understand correctly, the init method in the second class should be isolated, as it is being marked with @MainActor.

The workaround mentioned above with non-isolated base class works for this case as well, but I have also found another way to make the compiler happy:

@MainActor
private class FirstClass {}

@MainActor
private class SecondClass {

    let instance: FirstClass

    init(with instance: FirstClass?) {
        self.instance = instance ?? .init()
    }
}

In this case the code compiles with no issues.

But I still have question: why the first sample is not working?

Call to main actor-isolated initializer 'init()' in a synchronous nonisolated context
 
 
Q