Skip to content

Commit

Permalink
Merge tag '3.5.4'
Browse files Browse the repository at this point in the history
  • Loading branch information
arnaudgiuliani committed Apr 5, 2024
2 parents d4582c5 + f995b1c commit 5fa8e1f
Show file tree
Hide file tree
Showing 74 changed files with 2,505 additions and 180 deletions.
269 changes: 269 additions & 0 deletions docs/quickstart/android-annotations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
---
title: Android - Annotations
---

> This tutorial lets you write an Android application and use Koin dependency injection to retrieve your components.
> You need around __10/15 min__ to do the tutorial.
## Get the code

:::info
[The source code is available at on Github](https://github.com/InsertKoinIO/koin-getting-started/tree/main/android)
:::

## Gradle Setup

Let's configure the KSP Plugin like this:

```groovy
apply plugin: 'com.google.devtools.ksp'
android {
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
test.java.srcDirs += 'src/test/kotlin'
}
// For KSP
applicationVariants.configureEach { variant ->
kotlin.sourceSets {
getByName(name) {
kotlin.srcDir("build/generated/ksp/${variant.name}/kotlin")
}
}
}
}
```

Add the Koin Android dependency like below:

```groovy
dependencies {
// Koin
implementation "io.insert-koin:koin-android:$koin_version"
implementation "io.insert-koin:koin-annotations:$koin_ksp_version"
ksp "io.insert-koin:koin-ksp-compiler:$koin_ksp_version"
}
```



## Application Overview

The idea of the application is to manage a list of users, and display it in our `MainActivity` class with a Presenter or a ViewModel:

> Users -> UserRepository -> (Presenter or ViewModel) -> MainActivity
## The "User" Data

We will manage a collection of Users. Here is the data class:

```kotlin
data class User(val name : String)
```

We create a "Repository" component to manage the list of users (add users or find one by name). Here below, the `UserRepository` interface and its implementation:

```kotlin
interface UserRepository {
fun findUser(name : String): User?
fun addUsers(users : List<User>)
}

class UserRepositoryImpl : UserRepository {

private val _users = arrayListOf<User>()

override fun findUser(name: String): User? {
return _users.firstOrNull { it.name == name }
}

override fun addUsers(users : List<User>) {
_users.addAll(users)
}
}
```

## The Koin module

Let's declare a `AppModule` module class like below.

```kotlin
@Module
@ComponentScan("org.koin.sample")
class AppModule
```

* We use the `@Module` to declare our class as Koin module
* The `@ComponentScan("org.koin.sample")` allow to scann any Koin definition in `"org.koin.sample"`package

Let's simply add `@Single` on `UserRepositoryImpl` class to declare it as singleton:

```kotlin
@Single
class UserRepositoryImpl : UserRepository {
// ...
}
```

## Displaying User with Presenter

Let's write a presenter component to display a user:

```kotlin
class UserPresenter(private val repository: UserRepository) {

fun sayHello(name : String) : String{
val foundUser = repository.findUser(name)
return foundUser?.let { "Hello '$it' from $this" } ?: "User '$name' not found!"
}
}
```

> UserRepository is referenced in UserPresenter`s constructor
We declare `UserPresenter` in our Koin module. We declare it as a `factory` definition with the `@Factory` annotation, to not keep any instance in memory (avoid any leak with Android lifecycle):

```kotlin
@Factory
class UserPresenter(private val repository: UserRepository) {
// ...
}
```

## Injecting Dependencies in Android

The `UserPresenter` component will be created, resolving the `UserRepository` instance with it. To get it into our Activity, let's inject it with the `by inject()` delegate function:

```kotlin
class MainActivity : AppCompatActivity() {

private val presenter: UserPresenter by inject()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

//...
}
}
```

That's it, your app is ready.

:::info
The `by inject()` function allows us to retrieve Koin instances, in Android components runtime (Activity, fragment, Service...)
:::

## Start Koin

We need to start Koin with our Android application. Just call the `startKoin()` function in the application's main entry point, our `MainApplication` class:

```kotlin
// generated
import org.koin.ksp.generated.*

class MainApplication : Application(){
override fun onCreate() {
super.onCreate()

startKoin{
androidLogger()
androidContext(this@MainApplication)
modules(AppModule().module)
}
}
}
```

The Koin module is generated from `AppModule` with the `.module` extension: Just use the `AppModule().module` expression to get the Koin module from the annotations.

:::info
The `import org.koin.ksp.generated.*` import is required to allow to use generated Koin module content
:::

## Displaying User with ViewModel

Let's write a ViewModel component to display a user:

```kotlin
@KoinViewModel
class UserViewModel(private val repository: UserRepository) : ViewModel() {

fun sayHello(name : String) : String{
val foundUser = repository.findUser(name)
return foundUser?.let { "Hello '$it' from $this" } ?: "User '$name' not found!"
}
}
```

> UserRepository is referenced in UserViewModel`s constructor
The `UserViewModel` is tagged with `@KoinViewModel` annotation to declare the Koin ViewModel definition, to not keep any instance in memory (avoid any leak with Android lifecycle).


## Injecting ViewModel in Android

The `UserViewModel` component will be created, resolving the `UserRepository` instance with it. To get it into our Activity, let's inject it with the `by viewModel()` delegate function:

```kotlin
class MainActivity : AppCompatActivity() {

private val viewModel: UserViewModel by viewModel()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

//...
}
}
```

## Compile Time Checks

Koin Annotations allows to check your Koin configuration at compile time. This is available by jusing the following Gradle option:

```groovy
ksp {
arg("KOIN_CONFIG_CHECK","true")
}
```

## Verifying your App!

We can ensure that our Koin configuration is good before launching our app, by verifying our Koin configuration with a simple JUnit Test.

### Gradle Setup

Add the Koin Android dependency like below:

```groovy
// Add Maven Central to your repositories if needed
repositories {
mavenCentral()
}
dependencies {
// Koin for Tests
testImplementation "io.insert-koin:koin-test-junit4:$koin_version"
}
```

### Checking your modules

The `verify()` function allow to verify the given Koin modules:

```kotlin
class CheckModulesTest : KoinTest {

@Test
fun checkAllModules() {

AppModule().module.verify(
extraTypes = listOf(
SavedStateHandle::class
))
}
}
```

With just a JUnit test, you can ensure your definitions configuration are not missing anything!
Loading

0 comments on commit 5fa8e1f

Please sign in to comment.