Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Android Architecture Design With Koin

Android Architecture Design With Koin

Koin (https://insert-koin.io) is a pragmatic dependency injection framework for Kotlin/Android developers, well known for its ease of use. In this session, I propose to see together how we can structure our components, for the following topics:
* Classical Components in MVP, MVVM
* Dealing with Scopes
* Jetpack Navigation
* Jetpack Compose
* Integrating with MultiPlatform

And bonus, we will talk about new features.

Arnaud GIULIANI

April 30, 2022
Tweet

More Decks by Arnaud GIULIANI

Other Decks in Programming

Transcript

  1. class ClassA() class ClassB(val a : ClassA) module { single

    { ClassA() } single { ClassB(???) } }
  2. class ClassA() class ClassB(val a : ClassA) module { single

    { ClassA() } single { ClassB(get()) } } - Constructor injection con fi gured via Koin DSL - Instances are called by Koin Container
  3. class MyApp : KoinComponent { val classB by inject<ClassB>() }

    - Use the Koin API to retrieve a dependency - Host class is not created by Koin MyApp().classB
  4. Since Koin 3.x! // Android implementation "io.insert-koin:koin-android:$koin_version" implementation "io.insert-koin:koin-androidx-viewmodel:$koin_version" implementation

    "io.insert-koin:koin-androidx-scope:$koin_version" implementation "io.insert-koin:koin-androidx-fragment:$koin_version"
  5. Tasks DataSource Tasks Local DataSource class TasksRemoteDataSource : TasksDataSource class

    TasksLocalDataSource( private val tasksDao: TasksDao, private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO ) : TasksDataSource // Koin DSL single<TasksDataSource> { TasksRemoteDataSource() } single<TasksDataSource> { TasksLocalDataSource(get(),get()) }
  6. Tasks DataSource Tasks Local DataSource class TasksRemoteDataSource : TasksDataSource class

    TasksLocalDataSource( private val tasksDao: TasksDao, private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO ) : TasksDataSource // Koin DSL single<TasksDataSource>(named("RemoteDS")) { TasksRemoteDataSource() } single<TasksDataSource>(named("LocalDS")) { TasksLocalDataSource(get(),get()) }
  7. class DefaultTasksRepository( private val tasksRemoteDataSource: TasksDataSource, private val tasksLocalDataSource: TasksDataSource,

    private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO ) : TasksRepository // Koin DSL single<TasksRepository> { DefaultTasksRepository( get(), get(), get() ) } Tasks Repository
  8. class DefaultTasksRepository( private val tasksRemoteDataSource: TasksDataSource, private val tasksLocalDataSource: TasksDataSource,

    private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO ) : TasksRepository // Koin DSL single<TasksRepository> { DefaultTasksRepository( get(named("RemoteDS")), get(named("LocalDS")), get() ) } Tasks Repository
  9. class TasksViewModel( private val tasksRepository: TasksRepository, private val savedStateHandle: SavedStateHandle

    ) : ViewModel() // Koin DSL viewModel { TasksViewModel(get(),get()) } TasksViewModel
  10. // by ViewModel() in Activity & Fragment class TasksActivity :

    AppCompatActivity() { // also inject SavedStateHandle val viewModel : TasksViewModel by viewModel() } TasksActivity
  11. class TasksPresenter( private val tasksRepository: TasksRepository, private val view: TasksView

    ) // Koin DSL - Parameter injection for Presenter factory { TasksPresenter(get(), ??? ) } TasksViewPresenter
  12. class TasksPresenter( private val tasksRepository: TasksRepository, private val view: TasksView

    ) // Koin DSL - Parameter injection for Presenter factory { (view: TasksView) -> TasksPresenter(get(),view) } TasksViewPresenter
  13. class TasksActivity : TasksView, AppCompatActivity() { // inject itself as

    a view val presenter : TasksPresenter by inject { parametersOf(this) } } TasksView
  14. val appModule = module { // Presentation Layers viewModel {

    TasksViewModel(get(),get()) } factory { (view: TasksView) -> TasksPresenter(get(),view) } // Repository single<TasksRepository> { DefaultTasksRepository(get(named("RemoteDS")),get(named("LocalDS")),get()) } // Data Sources single<TasksDataSource>(named("RemoteDS")) { TasksRemoteDataSource() } single<TasksDataSource>(named("LocalDS")) { TasksLocalDataSource(get(),get()) } // Room single { Room.databaseBuilder(...).build() } single { get<ToDoDatabase>().taskDao() } }
  15. val dataModule = module { } val uiModule = module

    { } val appModule = dataModule + uiModule startKoin { modules(appModule) }
  16. class TasksViewModel( private val getTaskUseCase: GetTaskUseCase, private val savedStateHandle: SavedStateHandle

    ) : ViewModel() // Koin DSL viewModel { TasksViewModel(get(),get()) } TasksViewModel
  17. class GetTaskUseCase( private val tasksRepository: TasksRepository ) // Koin DSL

    // Don’t hold instance - ensure it’s stateless factory { GetTaskUseCase(get()) } GetTaskUseCase
  18. val appModule = module { // Presentation Layers viewModel {

    TasksViewModel(get(),get()) } factory { (view: TasksView) -> TasksPresenter(get(),view) } // Repository single<TasksRepository> { DefaultTasksRepository(get(named("RemoteDS")),get(named("LocalDS")),get()) } // Data Sources single<TasksDataSource>(named("RemoteDS")) { TasksRemoteDataSource() } single<TasksDataSource>(named("LocalDS")) { TasksLocalDataSource(get(),get()) } // Room single { Room.databaseBuilder(...).build() } single { get<ToDoDatabase>().taskDao() } }
  19. val appModule = module { // Presentation Layers viewModel {

    TasksViewModel(get(),get()) } factory { (view: TasksView) -> TasksPresenter(get(),view) } // Usecase factory { GetTaskUseCase(get()) } // Repository single<TasksRepository> { DefaultTasksRepository(get(named("RemoteDS")),get(named("LocalDS")),get()) } // Data Sources single<TasksDataSource>(named("RemoteDS")) { TasksRemoteDataSource() } single<TasksDataSource>(named("LocalDS")) { TasksLocalDataSource(get(),get()) } // Room single { Room.databaseBuilder(...).build() } single { get<ToDoDatabase>().taskDao() } }
  20. Tasks Repository Tasks DataSource Tasks Local DataSource TasksPresenter TasksActivity Tasks

    Database TasksViewModel Koin API factory or viewModel single single single
  21. Koin API factory or viewModel single single single Tasks Repository

    Tasks DataSource Tasks Local DataSource TasksViewModel TasksActivity Tasks Database GetTaskUseCase TasksPresenter factory
  22. Koin API factory or viewModel single single single Repository DataSource

    Local DataSource ViewModel Android UI Database UseCase / Interactor Presenter factory
  23. class MyActivity : AppCompatActivity() { val myPresenter : MyPresenter by

    inject() } // Koin DSL factory { MyPresenter(...) } 💥
  24. class MyActivity : ScopeActivity() { // Resolved in MyActivity's scope

    val myPresenter : MyPresenter by inject() val myAdapter : MyAdapter by inject() } // Koin DSL scope<MyActivity> { scoped { MyPresenter() } scoped { MyAdapter(get()) } } class MyAdapter(val presenter : Mypresenter) 💥 ScopeActivity use Activity’s scope by default
  25. class MyActivity : Activity() { val myViewModel : MyViewModel by

    viewModel() } // Koin DSL viewModel { MyViewModel() } ♻ class MyViewModel() : ViewModel()
  26. class MyActivity : ScopeActivity() { // Override current scope to

    Activity's scope by default override val scope : Scope by activityRetainedScope() val myViewModel : MyViewModel by viewModel() val myAdapter : MyAdapter by inject() } All components are backed By ViewModel ✅
  27. class MyFragment : Fragment() { val navViewModel : NavViewModel by

    koinNavGraphViewModel(R.id.nav_graph) } MyFragment Nav Graph
  28. // Koin DSL scope<MyActivity> { scoped { MyPresenter() } scoped

    { MyAdapter(get()) } } Define a scope class MyActivity : ScopeActivity() { } interface AndroidScopeComponent { override val scope : Scope by activityScope() } Use a scope with Android API
  29. // create scope getKoin().createScope(...) // ... get it elsewhere val

    scope = getKoin().getScope(...) scope.get<>() Get the Scope API class MyFragment : Fragment() { val navViewModel : NavViewModel by koinNavGraphViewModel(R.id.nav_graph) } ViewModel scoped to Nav Graph
  30. Koin API factory or viewModel single single single Repository DataSource

    Local DataSource ViewModel Android UI Database Presenter
  31. Koin API factory, viewModel or Scope single single single Repository

    DataSource Local DataSource ViewModel Android UI Database Presenter
  32. // Retrieving instance in a composable @Composable fun App() {

    val myService = get<MyService>() // or by inject<MyService>() } Getting instances in a Composable // Default parameter value @Composable fun App(myService: MyService = get()) { }
  33. // Retrieving ViewModel in a composable @Composable fun App() {

    val myViewModel = getViewModel<MyViewModel>() // or by viewModel<MyViewModel>() } Getting ViewModel in a Composable // Default parameter value @Composable fun App(myViewModel: MyViewModel = getViewModel()) { }
  34. Koin API factory or viewModel single single single Repository DataSource

    Local DataSource ViewModel Android UI Database Presenter
  35. Koin API factory or viewModel single single single Repository DataSource

    Local DataSource ViewModel Composable Database Presenter
  36. Json Serialization Ktor Http Client + API Component Data Repository

    Kermit Logging => Data Storage with SQLDelight
  37. Koin API factory or viewModel single single single Kotlin Multiplatform

    API 🚀 Repository DataSource Local DataSource Native UI (Android, iOS) Database Presentation
  38. KSP Powered 🔥 👉 Generate Koin stu ff DSL for

    you https:/ /github.com/google/ksp
  39. Tasks DataSource Tasks Local DataSource class TasksRemoteDataSource : TasksDatasource class

    TasksLocalDataSource( private val tasksDao: TasksDao, private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO ) : TasksDatasource
  40. Tasks DataSource Tasks Local DataSource @Single class TasksRemoteDataSource : TasksDatasource

    @Single class TasksLocalDataSource( private val tasksDao: TasksDao, private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO ) : TasksDatasource
  41. Tasks DataSource Tasks Local DataSource @Single @Named("RemoteDS") class TasksRemoteDataSource :

    TasksDatasource @Single @Named("LocalDS") class TasksLocalDataSource( private val tasksDao: TasksDao, private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO ) : TasksDatasource
  42. class DefaultTasksRepository( private val tasksRemoteDataSource: TasksDataSource, private val tasksLocalDataSource: TasksDataSource,

    private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO ) : TasksRepository Tasks Repository
  43. @Single class DefaultTasksRepository( private val tasksRemoteDataSource: TasksDataSource, private val tasksLocalDataSource:

    TasksDataSource, private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO ) : TasksRepository Tasks Repository
  44. @Single class DefaultTasksRepository( @Named("RemoteDS") private val tasksRemoteDataSource: TasksDataSource, @Named("LocalDS") private

    val tasksLocalDataSource: TasksDataSource, private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO ) : TasksRepository Tasks Repository
  45. // by ViewModel() in Activity & Fragment class TasksActivity :

    AppCompatActivity() { // also inject SavedStateHandle val viewModel : TasksViewModel by viewModel() } TasksActivity
  46. class TasksActivity : TasksView, AppCompatActivity() { // inject itself as

    a view val presenter : TasksPresenter by inject { parametersOf(this) } } TasksView
  47. Tasks Repository Tasks DataSource Tasks Local DataSource TasksPresenter TasksActivity Tasks

    Database TasksViewModel Koin API @Factory or @koinViewModel @Single @Single @Single
  48. import org.koin.ksp.generated.* startKoin { modules( // DSL Module otherDSLModule, //

    Class Module AppModule().module ) } Mix Classes & DSL Modules
  49. Koin Downloads Volume 📦 📦 5M Downloads in 2020 9M

    Downloads in 2021 🚀✨🌈 2022: Trends of 5M / Month
  50. 🗓 Roadmap 2022 Structured Release Cycle (~6months) - Fix Version

    (bug fi xes, fi x updates) - Minor Version (deprecations, minor features…) - Major Version (hard breaking …)
  51. 🗓 Roadmap 2022 Q4 Releases: Koin 3.3, Koin for Compose

    End of Track: Koin 3.1 Q2 Releases: Koin 3.2, Koin Annotations 1.0 👉 New DSL, Module includes …