Skip to content

Support @MainActor-isolated initializers in factory/singleton registrations #59

@redne-w

Description

@redne-w

Support @MainActor-isolated initializers in factory/singleton registrations

Problem

WhoopDIKit's factory closures are stored and called with no actor context, so Swift 6 strict concurrency rejects registering any type with a @MainActor-isolated init.

This comes up with patterns like:

@MainActor
public protocol AsyncViewModel { ... }

@MainActor
final class MyViewModel: AsyncViewModel {
    init(service: MyService) { ... } // implicitly @MainActor
}

Registering this fails to compile:

final class MyModule: DependencyModule {
    override func defineDependencies() {
        factory {
            // ❌ Main actor-isolated initializer cannot be referenced from a non-isolated context
            MyViewModel(service: try self.get())
        }
    }
}

Workarounds

nonisolated init — Swift 6

If the init only sets stored properties, nonisolated is safe. @MainActor isolation applies to methods on self after init, not to setting stored properties.

@MainActor
final class MyViewModel: AsyncViewModel {
    nonisolated init(service: MyService) {
        self.service = service
    }
}

Inject a factory closure — Swift 6

Register a @MainActor () -> MyViewModel instead of the type directly.
WhoopDI resolves dependencies; you construct on the main actor at the call
site.

// Module
factory {
    let service: MyService = try self.get()
    return { @MainActor in MyViewModel(service: service) } as (@MainActor ()
-> MyViewModel)
}

// @MainActor call site
let makeVM: @MainActor () -> MyViewModel = WhoopDI.inject()
let vm = makeVM()

Swift 5 language mode

Without strict concurrency enabled, this compiles and works fine at runtime as long as inject is always called from the main thread

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions