Koin 2.0 with Nested-Scope or SubComponent concept - android

Is there any way to implement Dagger's SubCompoent concept on Koin?
The thing that i want to do is using instance from parent scope's one.
app_modules.kt
val favoriteModule = module {
scope(named<FavoriteFragment>()) {
scoped { GetFavoriteMovies(get()) }
scoped { FavoriteVMFactory(get(), get()) } // This need 'MovieEntityMovieMapper'
}
}
val popularModule = module {
scope(named<PopularFragment>()) {
scoped { GetPopularMovies(get()) }
scoped { PopularVMFactory(get(), get()) } // This need 'MovieEntityMovieMapper'
}
}
val searchModule = module {
scope(named<SearchFragment>()) {
scoped { SearchMovies(get()) }
scoped { SearchVMFactory(get(), get()) } // This need 'MovieEntityMovieMapper'
}
}
val mainModule = module {
scope(named<MainActivity>()) {
scoped { MovieEntityMovieMapper() }
// this ImageLoader also injected by Fragments
scoped<ImageLoader> { (activity: Activity) -> GlideImageLoader(activity) }
}
}
Using Dagger, this can be done by SubComponent or Component Dependency.
But in Koin(especially 2.0), i cant not find way.
Some answer said use
GlobalContext.get().koin.getScope("Parent").get<>().
https://github.com/InsertKoinIO/koin/issues/513
Koin sharing instances between modules
but i dont think it's not a clean approach and a dependency injection.

You can try https://github.com/beyama/winter which is inspired by Koin but has child graphs that can be used like Dagger's subcomponents. Winter has been used in production for some major apps with several million installs for a few years now. I hope it is ok to post an example here: https://play.google.com/store/apps/details?id=de.prosiebensat1digital.seventv
Disclaimer: I know the author and have been using his library myself since its release. So far it has been stable in my experience.

Related

Android Dynamic Feature Module with kodein frame work

What is the best way to use kodein with Android Dynamic feature module? Dyanamic module will be downloaded latter at some point.
Is there any way to dynamically add module to kodein dependency.
/* current code */
override val di = DI.lazy {
import(androidXModule(this#AppController))
import(timeModule)
bind { singleton { NetworkConnectionInterceptor(instance())
}
}
public void onClickDownloadFeatureModule() {
// Add it to "App level kodein
}
}

How to scope a Usecase to a Feature / Activity in Koin

I'm maintaining a large app (mostly) using a "one feature one activity"-architecture.
Now i'd like to scope a usecase, so it lives as long as the activity, something like this:
// koin module
scope<MyFeatureActivity> {
viewModel { MyFeatureActivityViewModel() }
viewModel { MyFeatureFragmentAViewModel(usecase = get()) }
viewModel { MyFeatureFragmentBViewModel(usecase = get()) }
scoped { MyFeatureUseCase() }
}
// fragments
class FeatureAFragment: AppCompatDialogFragment(){
private val viewModel by viewModel<MyFeatureFragmentAViewModel>()
....
}
// activity
class MyFeatureActivity : ScopeActivity() { ... }
However, this doesn't work. When launching MyFeatureFragmentA from MyFeatureActivity it's throwing an Exception:
org.koin.core.error.NoBeanDefFoundException:
|- No definition found for class:'MyFeatureAViewModel'. Check your definitions!
What am i doing wrong?
Please note: I would not like to just skip scopes and make the usecase a single (or a factory), since it actually stores some data relevant to only this activity: The data should be kept while we're in this feature, but dismissed when leaving it.

How to access an instance from one Kodein module in a different module?

When using Kodein, if I have 2 modules and module B needs to use an instance from module A, is the best practice to import module A into module B or is there a better way to do it?
For example, I have a networkingModule:
val networkingModule = Kodein.Module("networking") {
bind<Retrofit>() with singleton {
Retrofit.Builder()
.baseUrl("https://api.example.com/")
.build()
}
}
And subscribersModule needs the Retrofit instance from networkingModule:
val subscribersModule = Kodein.Module("subscribersModule") {
import(networkingModule)
bind<SubscribersService>() with singleton {
instance<Retrofit>().create(SubscribersService::class.java)
}
}
Is adding the import(networkingModule) in subscribersModule the best way to do it?
At the end, if your modules are used in one project, you're not force to make them dependant.
Instead you can import them in a global container, like this:
val applicationContainer = Kodein {
import(subscribersModule)
import(networkingModule)
// ...
}
Kodein-DI will solve the dependencies for you.

Koin constructor inject few parameters MVVM Android Architecture

I'm using MVVM architecture for my Android app and just started with Koin. When I'm trying to inject more than one parameter problems start appearing.
For now I have Repository class which uses:
RESTApi to perform web calls. RestProvider uses SocketProvider as constructor parameter for this
Utils(Context) as helper to retrieve some basic information(appVersion,
IMEI etc)
My appModule
{
single<RepositoryApi> {
Repository(
Utils(androidContext())
\\ RestProvider(SocketProvider()) Here I get problems
)
}
single<RestApi> { RestProvider(get() as SocketProvider) }
single<SocketApi> { SocketProvider() }
single<UtilsApi> { Utils(androidContext()) }
viewModel { LoginViewModel(get()) }
}
When I use Utils only everything works fine, when I add RestProvider I get exception:
Caused by: org.koin.core.error.InstanceCreationException: Could not
create instance for
[type:Single,primary_type:'com.etrans.ntsdriver.provider.repository.RepositoryApi']
at org.koin.core.instance.DefinitionInstance.create(DefinitionInstance.kt:61)
at org.koin.core.instance.SingleDefinitionInstance.get(SingleDefinitionInstance.kt:40)
at org.koin.core.definition.BeanDefinition.resolveInstance(BeanDefinition.kt:70)
at org.koin.core.scope.Scope.resolveInstance(Scope.kt:165)
at org.koin.core.scope.Sc
I understand that I'm missing something but I didn't find any tutorials or examples to explain such cases.
My gradle dependencies related to Koin(maybe will be useful):
// Koin for Android - ViewModel features
implementation "org.koin:koin-android-viewmodel:$koin_version"
implementation "org.koin:koin-java:$koin_version"
testImplementation "org.koin:koin-test:$koin_version"
androidTestImplementation "org.koin:koin-test:$koin_version"
Thanks in advance for any help
Here is an example of using Koin to setup retrofit.
private val networkModule = module {
single {
HttpLoggingInterceptor(
HttpLoggingInterceptor.Logger { message ->
//Logger.d("NETWORK: $message")
}).apply {
level = HttpLoggingInterceptor.Level.NONE
}
}
single {
DefaultHeadersInterceptor()
}
single {
OkHttpClient.Builder()
.addInterceptor(get<HttpLoggingInterceptor>())
.addInterceptor(get<DefaultHeadersInterceptor>())
.build()
}
single {
Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(JacksonConverterFactory.create(ObjectMapper().registerKotlinModule()))
.client(get())
.build()
}
single { get<Retrofit>().create(ApiInterface::class.java) }
}
So on this way you can configure ApiInterface
single { get().create(ApiInterface::class.java) }
Hope this will help.

Android Tests: use Dagger2 + Gradle

I understand how Dagger2 works,
I understand it allows to easily swap dependencies, so we can use mocks for testing.
Point is that I am not sure I understand how am I supposed to provide different Dagger2 Components implementations for testing and for debug/production.
Would I need to create 2 Gradle productFlavors (e.g "Production"/"Test")
that would contain 2 different Components definition?
Or can I specify that I want to use the mock Component for test compile and the non mock Component for non test builds?
I am confused, please some clarification would be great!
Thanks a lot!
Unit testing
Don’t use Dagger for unit testing
For testing a class with #Inject annotated constructor you don't need dagger. Instead create an instance using the constructor with fake or mock dependencies.
final class ThingDoer {
private final ThingGetter getter;
private final ThingPutter putter;
#Inject ThingDoer(ThingGetter getter, ThingPutter putter) {
this.getter = getter;
this.putter = putter;
}
String doTheThing(int howManyTimes) { /* … */ }
}
public class ThingDoerTest {
#Test
public void testDoTheThing() {
ThingDoer doer = new ThingDoer(fakeGetter, fakePutter);
assertEquals("done", doer.doTheThing(5));
}
}
Functional/integration/end-to-end testing
Functional/integration/end-to-end tests typically use the production
application, but substitute fakes[^fakes-not-mocks] for persistence,
backends, and auth systems, leaving the rest of the application to
operate normally. That approach lends itself to having one (or maybe a
small finite number) of test configurations, where the test
configuration replaces some of the bindings in the prod configuration.
You have two options here:
Option 1: Override bindings by subclassing modules
#Component(modules = {AuthModule.class, /* … */})
interface MyApplicationComponent { /* … */ }
#Module
class AuthModule {
#Provides AuthManager authManager(AuthManagerImpl impl) {
return impl;
}
}
class FakeAuthModule extends AuthModule {
#Override
AuthManager authManager(AuthManagerImpl impl) {
return new FakeAuthManager();
}
}
MyApplicationComponent testingComponent = DaggerMyApplicationComponent.builder()
.authModule(new FakeAuthModule())
.build();
Option 2: Separate component configurations
#Component(modules = {
OAuthModule.class, // real auth
FooServiceModule.class, // real backend
OtherApplicationModule.class,
/* … */ })
interface ProductionComponent {
Server server();
}
#Component(modules = {
FakeAuthModule.class, // fake auth
FakeFooServiceModule.class, // fake backend
OtherApplicationModule.class,
/* … */})
interface TestComponent extends ProductionComponent {
FakeAuthManager fakeAuthManager();
FakeFooService fakeFooService();
}
More about it in the official documentation testing page.

Categories

Resources