Skip to content

Commit

Permalink
Setup Kotlin inject
Browse files Browse the repository at this point in the history
  • Loading branch information
kirich1409 committed Aug 15, 2024
1 parent 94fa514 commit 4e5e43e
Show file tree
Hide file tree
Showing 36 changed files with 332 additions and 193 deletions.
2 changes: 2 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ android {

packaging {
resources {


excludes += "/META-INF/{AL2.0,LGPL2.1}"
excludes += "/okhttp3/internal/publicsuffix/NOTICE"
excludes += "/kotlin/**"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@ class NewsApplication @JvmOverloads constructor(
debug = BuildConfig.DEBUG,
newsApiKey = BuildConfig.NEWS_API_KEY,
newsApiBaseUrl = BuildConfig.NEWS_API_BASE_URL,
targetAppDeclaration = {
androidContext(this@NewsApplication)
}
platformContext = this
)
}
}
1 change: 1 addition & 0 deletions core/common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ kotlin {
api(libs.kotlinx.datetime)
api(libs.coil.core)
api(libs.koin.core)
api(libs.kotlinInject.runtime)
implementation(libs.coil.network.ktor)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package dev.androidbroadcast.common

import android.content.Context

public actual typealias PlatformContext = Context
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package dev.androidbroadcast.common

import me.tatarka.inject.annotations.Qualifier

@Qualifier
@Target(
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.FUNCTION,
AnnotationTarget.VALUE_PARAMETER,
AnnotationTarget.TYPE
)
public annotation class Named(val value: String)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package dev.androidbroadcast.common

public expect abstract class PlatformContext
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package dev.androidbroadcast.common

import me.tatarka.inject.annotations.Scope

@Scope
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER)
public annotation class Singleton
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package dev.androidbroadcast.common

public actual abstract class PlatformContext {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package dev.androidbroadcast.common

public actual abstract class PlatformContext {
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dev.androidbroadcast.news.data

import dev.androidbroadcast.common.Logger
import dev.androidbroadcast.common.Singleton
import dev.androidbroadcast.news.data.model.Article
import dev.androidbroadcast.news.database.NewsDatabase
import dev.androidbroadcast.news.database.models.ArticleDBO
Expand All @@ -17,7 +18,10 @@ import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach
import me.tatarka.inject.annotations.Inject

@Inject
@Singleton
public class ArticlesRepository(
private val database: NewsDatabase,
private val api: NewsApi,
Expand Down
13 changes: 13 additions & 0 deletions core/platform/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
alias(libs.plugins.androidLibrary)
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.ksp)
}

kotlin {
Expand Down Expand Up @@ -63,3 +64,15 @@ android {
targetCompatibility = JavaVersion.VERSION_1_8
}
}

dependencies {
// 1. Configure code generation into the common source set
// kspCommonMainMetadata(libs.kotlinInject.compiler)

// 2. Configure code generation into each KMP target source set
"kspAndroid"(libs.kotlinInject.compiler)
"kspIosX64"(libs.kotlinInject.compiler)
"kspIosArm64"(libs.kotlinInject.compiler)
"kspIosSimulatorArm64"(libs.kotlinInject.compiler)
"kspJvm"(libs.kotlinInject.compiler)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package dev.androidbroadcast.news.core

import androidx.room.Room
import androidx.room.RoomDatabase
import dev.androidbroadcast.common.AndroidLogcatLogger
import dev.androidbroadcast.common.Logger
import dev.androidbroadcast.common.PlatformContext
import dev.androidbroadcast.news.database.NewsRoomDatabase
import org.koin.android.ext.koin.androidContext

internal actual fun newsRoomDatabaseBuilder(context: PlatformContext): RoomDatabase.Builder<NewsRoomDatabase> {
return Room.databaseBuilder(
context = context,
klass = NewsRoomDatabase::class.java,
name = "news"
)
}

internal actual fun newLogger(): Logger = AndroidLogcatLogger()

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package dev.androidbroadcast.news.core

import androidx.room.RoomDatabase
import dev.androidbroadcast.common.AppDispatchers
import dev.androidbroadcast.common.Logger
import dev.androidbroadcast.common.Named
import dev.androidbroadcast.common.PlatformContext
import dev.androidbroadcast.common.Singleton
import dev.androidbroadcast.news.database.NewsDatabase
import dev.androidbroadcast.news.database.NewsRoomDatabase
import dev.androidbroadcast.newsapi.NewsApi
import kotlinx.serialization.json.Json
import me.tatarka.inject.annotations.Component
import me.tatarka.inject.annotations.KmpComponentCreate
import me.tatarka.inject.annotations.Provides

@KmpComponentCreate
internal expect fun createAppComponent(
debuggable: Boolean,
baseUrl: String,
apiKey: String,
platformContext: PlatformContext,
): AppComponent

internal expect fun newsRoomDatabaseBuilder(context: PlatformContext): RoomDatabase.Builder<NewsRoomDatabase>

internal expect fun newLogger(): Logger

@Component
@Singleton
public abstract class AppComponent(
@get:Provides @get:Named(ConfigProperties.NewsPlatform.Debug) public val debuggable: Boolean,
@get:Provides @get:Named(ConfigProperties.NewsApi.BaseUrl) protected val baseUrl: String,
@get:Provides @get:Named(ConfigProperties.NewsApi.ApiKey) protected val apiKey: String,
@get:Provides protected val platformContext: PlatformContext,
) {

@Singleton
@Provides
protected fun json(): Json {
return Json {
isLenient = true
ignoreUnknownKeys = true
explicitNulls = false
}
}

@Singleton
@Provides
protected fun newsApi(
@Named(ConfigProperties.NewsApi.BaseUrl) baseUrl: String,
@Named(ConfigProperties.NewsApi.ApiKey) apiKey: String,
json: Json,
): NewsApi {
return NewsApi(
baseUrl = baseUrl,
apiKey = apiKey,
json = json
)
}

@Singleton
@Provides
protected fun appDispatchers(): AppDispatchers = AppDispatchers()

@Singleton
@Provides
protected fun newsDatabase(
dispatchers: AppDispatchers,
context: PlatformContext,
): NewsDatabase {
return NewsDatabase(
databaseBuilder = newsRoomDatabaseBuilder(context),
dispatcher = dispatchers.io
)
}

@Singleton
@Provides
protected fun logger(): Logger = newLogger()

public companion object {

public val appComponent: AppComponent
get() = checkNotNull(_appComponent)

private var _appComponent: AppComponent? = null

internal fun create(
debuggable: Boolean,
baseUrl: String,
apiKey: String,
platformContext: PlatformContext,
): AppComponent {
this._appComponent = createAppComponent(debuggable, baseUrl, apiKey, platformContext)
return appComponent
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,54 +1,26 @@
package dev.androidbroadcast.news.core

import coil3.ImageLoader
import coil3.PlatformContext
import coil3.SingletonImageLoader
import org.koin.core.component.KoinComponent
import org.koin.core.component.KoinScopeComponent
import org.koin.core.context.startKoin
import org.koin.core.logger.Level
import org.koin.core.scope.Scope
import org.koin.dsl.KoinAppDeclaration
import dev.androidbroadcast.common.PlatformContext
import kotlin.properties.Delegates.notNull

public class NewsAppPlatform :
KoinComponent,
KoinScopeComponent,
SingletonImageLoader.Factory {
override val scope: Scope
// Root scope id taken from Koin source code
get() = getKoin().getScope("_root_")

public var appComponent: AppComponent by notNull()
private set

public fun start(
debug: Boolean,
newsApiKey: String,
newsApiBaseUrl: String,
targetAppDeclaration: KoinAppDeclaration = {}
platformContext: PlatformContext,
) {
startKoin {
modules(
appKoinModule,
targetKoinModule
)

properties(
mapOf(
ConfigProperties.NewsPlatform.Debug to debug,
ConfigProperties.NewsApi.ApiKey to newsApiKey,
ConfigProperties.NewsApi.BaseUrl to newsApiBaseUrl
)
)

if (debug) {
printLogger(Level.DEBUG)
}

targetAppDeclaration()
}
appComponent = AppComponent.create(debug, newsApiBaseUrl, newsApiKey, platformContext)
}

override fun newImageLoader(context: PlatformContext): ImageLoader =
newImageLoader(
context,
debug = getKoin().getProperty(ConfigProperties.NewsPlatform.Debug, false)
)
override fun newImageLoader(context: coil3.PlatformContext): ImageLoader {
return newImageLoader(context, debug = appComponent.debuggable)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package dev.androidbroadcast.news.core

import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
import dev.androidbroadcast.common.Logger
import dev.androidbroadcast.common.PlatformContext
import dev.androidbroadcast.common.PrintLogger
import dev.androidbroadcast.news.database.NewsRoomDatabase
import dev.androidbroadcast.news.database.instantiateNewsRoomDatabase
import platform.Foundation.NSHomeDirectory

internal actual fun newsRoomDatabaseBuilder(context: PlatformContext): RoomDatabase.Builder<NewsRoomDatabase> {
return Room.databaseBuilder<NewsRoomDatabase>(
name = "${NSHomeDirectory()}/news.db",
factory = { instantiateNewsRoomDatabase() }
).setDriver(BundledSQLiteDriver())
}

internal actual fun newLogger(): Logger = PrintLogger()
Loading

0 comments on commit 4e5e43e

Please sign in to comment.