hmlongco / Factory

A new approach to Container-Based Dependency Injection for Swift and SwiftUI.
MIT License
1.7k stars 107 forks source link

Async Init Support for Actors? #211

Closed joeljfischer closed 1 week ago

joeljfischer commented 1 week ago

I'm not sure how possible this would be, but it would vastly simplify some code that I'm moving into Actors for Swift 6 to allow it to use init() async and set up things like a for await loop directly within the initializer and eliminate possible data races by using Task in the initializer.

If it's not possible, then that's understandable, but it would be a great boon to using actor with Factory in certain use cases where things have to be set-up at initialization time.

hmlongco commented 1 week ago

Will peek, but doubt this will occur as I suspect it would require every step in the resolution chain (registration, caching, etc.) to also support async instances which would then mess with synchronous resolutions.

Might want to consider one of the two following approaches:

// something with an asynchronous initializer
struct AsyncInit {
    let a: Int
    init() async {
        a = 1
    }
}

// generic wrapper for any asynchronous initializer
struct AsyncInitWrapper<T> {
    let wrapped: () async -> T
}

extension Container {
    // Factory using initialization wrapper
    var someAsyncObject: Factory<AsyncInitWrapper<AsyncInit>> {
        self {
            AsyncInitWrapper { await AsyncInit() }
        }
    }
    // Factory using Task isolated context
    var taskAsyncObject: Factory<Task<AsyncInit, Never>> {
        self {
            Task { await AsyncInit() }
        }
    }
}

func testAsyncInit() {
    Task {
        let a1 = await Container.shared.someAsyncObject().wrapped()
        let a2 = await Container.shared.taskAsyncObject().value
    }
}
hmlongco commented 1 week ago

Yeah, not going to work. Better to use one of the options shown earlier.

hmlongco commented 1 week ago

As mentioned, it would require every step in the resolution chain (registration, caching, etc.) to also support async instances which would then mess with synchronous resolutions. Or half the supporting infrastructure would have to be cloned to support async factories.

Too many easy options available for the rare case in which you're want to do it, as shown earlier.