Comments (6)
As a work-around you can return a Deferred
object
@Component
@NetworkScope
internal abstract class NetworkComponent(
@get:Provides protected val apiInstanceCounter: AtomicInteger,
@get:Provides val scope: CoroutineScope,
) {
@NetworkScope
abstract fun api(): Deferred<Api>
@Provides
@NetworkScope
protected fun api(service: Service, scope: CoroutineScope): Deferred<Api> =
scope.async(start = CoroutineStart.LAZY) { service.connect() }
protected val DummyNetworkService.bind: Service
@Provides get() = this
}
interface Api
interface Service {
suspend fun connect(): Api
}
@Inject
class DummyNetworkService(private val apiInstanceCounter: AtomicInteger) : Service {
override suspend fun connect(): Api {
apiInstanceCounter.incrementAndGet()
return object : Api {}
}
}
internal class KotlinInjectTest {
@Test
fun `calling scoped suspending provider should create singleton instance`() {
val apiInstanceCounter = AtomicInteger(0)
runBlocking {
val networkComponent = NetworkComponent::class.create(apiInstanceCounter, this)
val apiInstance0 = networkComponent.api().await()
val apiInstance1 = networkComponent.api().await()
assertEquals(apiInstance0, apiInstance1)
}
}
}
from kotlin-inject.
Marking as a bug because at the very least it should have a good error message. Will look into if this can be supported
from kotlin-inject.
Thanks for looking into it. It'll be great if this can be supported.
My project currently uses Dagger(kotlin jvm) with Deferred as you have suggested. Using Deferred causes all the dependent objects to also return Deferred, which requires boiler plate code for launching the async, awaiting deferred value in deferred provider methods & passing the coroutineScope to the component.
Even with Deferred "kotlin-inject" is better option than Dagger as it will only require Deferred at the point of "suspending provider" call & dependent object relationship can be expressed without Deferred by adding a suspend mapping from Deferred value to value using await().
@Provides
protected suspend fun api(deferredApi: Deferred<Api>): Api = deferredApi.await()
And then api can be returned/injected directly instead of Deferred:
@NetworkScope
abstract suspend fun api(): Api
Since it's technically possible with hand written code, it probably should be possible with code generation.
On seeing the location of the error, the problem seems to be in the init block of the get method of LazyMap:
actual fun <T> get(key: String, init: () -> T): T
Maybe a suspending variant could help:
actual suspend fun <T> get(key: String, init: suspend () -> T): T
from kotlin-inject.
yeah, though it looks like kotlinx.coroutines is required to implement the suspend version, which isn't currently a dependency. Will have to figure out how to make that optional or if we can live with the extra dependency.
from kotlin-inject.
note: looked into this more and you really need a coroutine-aware lock for this to work correctly, this means that either coroutines would have to be a hard-dependency or we'd need actually do different code gen based on a flag or something. both options feel kinda gross 😞
from kotlin-inject.
This is not blocking for my usecase, and is a good to have feature.
The delta utility vs amount of effort required to implement/maintain it doesn't seems worth it.
Providing an error message with the workaround steps should be good enough.
from kotlin-inject.
Related Issues (20)
- Unclear error message for scoped provider HOT 2
- Providing a function instead of property seems to not always work HOT 1
- Ability to bind Implementaion with Interface HOT 1
- Custom component and scope support like in Dagger 2
- Generated code is not accessible from commonMain HOT 5
- Search for a covariant type when checking if a component's abstract property has been implemented
- Cannot scope child components dependencies with parent component's scope using Provides HOT 6
- can't build the project HOT 1
- Error while compiling project HOT 5
- Feature request - support deferring to superclass component properties HOT 2
- Typealiases lead to KSP StackOverflowException
- Scope annotations aren't picked up when scanning superclasses
- Expect Actual Ancestor Components and Common Descendant Components HOT 5
- Kotlin Delegation Support? HOT 4
- Interface Bindings HOT 3
- Support @IntoSet for superclass HOT 1
- Names clash for properties in component with multiple super types HOT 2
- Component with an Int literal property name is used as an Int value
- Implemented function still considered abstract HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from kotlin-inject.