I have a model (AModel) which I wanted to use as singleton. I created a custom scope named @ApplicationScope to use it for every class I need only once. So AppComponent and AModel shares this @ApplicationScope. I have some fragment (ConfirmationFragment) where I would like to use both AModel and BModel. BModel has a different scope because I'd like to use it in 3 fragments but Amodel needed everywhere.
The idea to access both AModel and BModel was to let ConfirmationComponent depend on AppComponent where AModel already available. In this way if I inject ConfirmationComponent to ConfirmationFragment I could use AModel, too.
But I got the following error:
[Dagger/IncompatiblyScopedBindings] ConfirmationComponent scoped with @ConfirmationScope may not reference bindings with different scopes:
Build succeeds when injecting AModel is commented out from ConfirmationFragment but fails when it isn't. I need AModel in that fragment, too.
How could I fix this problem?
(In case it's important: I use only one activity, and let Android navigation do the work with fragments.)
open class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
val appComponent = initializeComponent()
}
val appComponent: AppComponent by lazy {
initializeComponent()
}
val confirmationComponent: ConfirmationComponent by lazy {
initializeConfirmationComponent()
}
open fun initializeComponent(): AppComponent {
return DaggerAppComponent.factory().create(applicationContext)
}
open fun initializeConfirmationComponent(): ConfirmationComponent {
return DaggerConfirmationComponent.builder().appComponent(appComponent).build()
}
}
@ApplicationScope
@Component(modules = [NetworkModule::class])
interface AppComponent {
@Component.Factory
interface Factory {
fun create(@BindsInstance context: Context) : AppComponent
}
fun inject(activity: MainActivity)
fun inject(fragment: ConfirmationFragment)
}
@ConfirmationScope
@Component(dependencies = [AppComponent::class])
interface ConfirmationComponent {
fun inject(fragment: ConfirmationFragment)
}
@ApplicationScope
class AModel @Inject constructor() {}
@ConfirmationScope
class BModel @Inject constructor() {}
class ConfirmationFragment : Fragment() {
@Inject
lateinit var modelA : AModel
@Inject
lateinit var modelB : BModel
override fun onAttach(context: Context) {
super.onAttach(context)
(requireActivity().applicationContext as MyApplication).confirmationComponent.inject(this)
}
// Rest of the code
}
I think you should put it other way around. If I got you correctly ModelA has larger scope than ModelB this means you can have ModelB as a subcomponent of ModelA with narrower scope.
So for this you would need:
//@YourScopeAnnotation
@Subcomponent(modules = [...]) // if it is dependent on any modules
interface ConfirmationComponent {
// needed for dagger to create component
@Subcomponent.Factory
interface Factory {
fun create(): ConfirmationComponent
}
fun inject(yourFragment: Fragment) // fun inject your fragment
}
@Module(
subcomponents = [ConfirmationComponent::class]
)
class SubcomponentsModule
//@ApplicationScopeAnnotation I think you can also use @Singleton
@Component(
modules = [NetworkModule::class, SubcomponentsModule::class]
)
interface ApplicationComponent {
fun inject(activity: MainActivity)
fun confirmationComponent(): ConfirmationComponent.Factory
}
Than just initialize ApplicationComponent in your Application class as usual
Now in your main activity inject ModelA which should be available globally. Also create ConfirmationComponent in your activity
@Inject
lateinit var modelA : AModel
lateinit var confirmationComponent: ConfirmationComponent
override fun onCreate(savedInstanceState: Bundle?) {
confirmationComponent = (applicationContext as MyApplication).appComponent
.confirmationComponent()
.create()
modelA = (applicationContext as MyApplication).appComponent.inject(this)
}
@Inject
lateinit var modelB: ModelB // inject modelB
lateinit var modelA: ModelA // get ModelA from activity where it was already injected
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
(activity as MainActivity).confirmationComponent.inject(this)
modelA = (activity as MainActivity).modelA
}
I hope this helps :)