Skip to content

Commit ee02196

Browse files
committed
Add data persistence for list of countries with kstore
1 parent d3c0ac2 commit ee02196

File tree

13 files changed

+155
-13
lines changed

13 files changed

+155
-13
lines changed

composeApp/build.gradle.kts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,12 @@ kotlin {
6363
implementation(compose.components.resources)
6464
implementation(compose.components.uiToolingPreview)
6565

66-
66+
6767
implementation(libs.koin.core)
6868
implementation(libs.koin.compose)
6969

70+
implementation(libs.kstore)
71+
7072
implementation(libs.kotlinx.coroutines)
7173
implementation(libs.bundles.ktor.common)
7274

@@ -82,22 +84,27 @@ kotlin {
8284

8385
jsMain.dependencies {
8486
implementation(compose.html.core)
87+
implementation(libs.kstore.storage)
8588
}
8689

8790
androidMain.dependencies {
8891
implementation(libs.compose.ui.tooling.preview)
8992
implementation(libs.androidx.activity.compose)
90-
93+
implementation(libs.koin.android)
94+
implementation(libs.kstore.file)
9195
implementation(libs.ktor.client.android)
9296
}
9397

9498
desktopMain.dependencies {
9599
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-swing:${libs.versions.kotlinx.coroutines}")
96100
implementation(compose.desktop.currentOs)
101+
implementation(libs.harawata.appdirs)
102+
implementation(libs.kstore.file)
97103
implementation(libs.ktor.client.java)
98104
}
99105

100106
appleMain.dependencies {
107+
implementation(libs.kstore.file)
101108
implementation(libs.ktor.client.darwin)
102109
}
103110

@@ -108,6 +115,12 @@ kotlin {
108115
desktopTest.dependencies {
109116
implementation(compose.desktop.currentOs)
110117
}
118+
119+
val wasmJsMain by getting
120+
121+
wasmJsMain.dependencies {
122+
implementation(libs.kstore.storage)
123+
}
111124
}
112125
}
113126

@@ -172,4 +185,4 @@ tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach
172185

173186
kotlin.sourceSets.all {
174187
languageSettings.optIn("kotlin.experimental.ExperimentalObjCName")
175-
}
188+
}

composeApp/src/androidMain/kotlin/dev/johnoreilly/climatetrace/MainActivity.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class MainActivity : ComponentActivity() {
3131
override fun onCreate(savedInstanceState: Bundle?) {
3232
super.onCreate(savedInstanceState)
3333

34-
initKoin()
34+
initKoin(this)
3535

3636
setContent {
3737
MaterialTheme {
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package dev.johnoreilly.climatetrace.di
2+
3+
import android.content.Context
4+
import dev.johnoreilly.climatetrace.remote.Country
5+
import io.github.xxfast.kstore.KStore
6+
import io.github.xxfast.kstore.file.storeOf
7+
import okio.Path.Companion.toPath
8+
import org.koin.android.ext.koin.androidContext
9+
import org.koin.core.module.Module
10+
import org.koin.dsl.module
11+
12+
fun initKoin(context: Context) = initKoin(enableNetworkLogs = false) {
13+
androidContext(context)
14+
}
15+
16+
actual fun dataModule(): Module = module {
17+
single<KStore<List<Country>>> {
18+
val filesDir: String = androidContext().filesDir.path
19+
storeOf(file = "$filesDir/countries.json".toPath(), default = emptyList())
20+
}
21+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package dev.johnoreilly.climatetrace.data
2+
3+
import dev.johnoreilly.climatetrace.remote.ClimateTraceApi
4+
import dev.johnoreilly.climatetrace.remote.Country
5+
import io.github.xxfast.kstore.KStore
6+
7+
class ClimateTraceRepository(
8+
private val store: KStore<List<Country>>,
9+
private val api: ClimateTraceApi
10+
) {
11+
suspend fun fetchCountries() : List<Country> {
12+
val countries: List<Country>? = store.get()
13+
if (countries.isNullOrEmpty()) return api.fetchCountries().also { store.set(it) }
14+
return countries
15+
}
16+
}

composeApp/src/commonMain/kotlin/dev/johnoreilly/climatetrace/di/Koin.kt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package dev.johnoreilly.climatetrace.di
22

3+
import dev.johnoreilly.climatetrace.data.ClimateTraceRepository
34
import dev.johnoreilly.climatetrace.remote.ClimateTraceApi
45
import dev.johnoreilly.climatetrace.viewmodel.ClimateTraceViewModel
56
import io.ktor.client.HttpClient
@@ -10,7 +11,9 @@ import io.ktor.client.plugins.logging.Logger
1011
import io.ktor.client.plugins.logging.Logging
1112
import io.ktor.serialization.kotlinx.json.json
1213
import kotlinx.serialization.json.Json
14+
import org.koin.core.context.loadKoinModules
1315
import org.koin.core.context.startKoin
16+
import org.koin.core.module.Module
1417
import org.koin.dsl.KoinAppDeclaration
1518
import org.koin.dsl.module
1619

@@ -20,16 +23,17 @@ fun initKoin(enableNetworkLogs: Boolean = false, appDeclaration: KoinAppDeclarat
2023
modules(commonModule(enableNetworkLogs = enableNetworkLogs))
2124
}
2225

23-
// called by iOS etc
24-
fun initKoin() = initKoin(enableNetworkLogs = false) {}
25-
2626
fun commonModule(enableNetworkLogs: Boolean = false) = module {
2727
single { createJson() }
2828
single { createHttpClient(get(), enableNetworkLogs = enableNetworkLogs) }
2929
single { ClimateTraceApi(get()) }
3030
single { ClimateTraceViewModel() }
31+
single { ClimateTraceRepository(get(), get()) }
32+
loadKoinModules(dataModule())
3133
}
3234

35+
expect fun dataModule(): Module
36+
3337
fun createJson() = Json { isLenient = true; ignoreUnknownKeys = true }
3438

3539
fun createHttpClient(json: Json, enableNetworkLogs: Boolean) = HttpClient {

composeApp/src/commonMain/kotlin/dev/johnoreilly/climatetrace/viewmodel/ClimateTraceViewModel.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.rickclephas.kmm.viewmodel.KMMViewModel
44
import com.rickclephas.kmm.viewmodel.MutableStateFlow
55
import com.rickclephas.kmm.viewmodel.coroutineScope
66
import com.rickclephas.kmp.nativecoroutines.NativeCoroutinesState
7+
import dev.johnoreilly.climatetrace.data.ClimateTraceRepository
78
import dev.johnoreilly.climatetrace.remote.ClimateTraceApi
89
import dev.johnoreilly.climatetrace.remote.Country
910
import dev.johnoreilly.climatetrace.remote.CountryAssetEmissionsInfo
@@ -15,6 +16,7 @@ import org.koin.core.component.inject
1516

1617
open class ClimateTraceViewModel : KMMViewModel(), KoinComponent {
1718
private val climateTraceApi: ClimateTraceApi by inject()
19+
private val climateTraceRepository: ClimateTraceRepository by inject()
1820

1921
var year: String = "2022"
2022

@@ -37,7 +39,7 @@ open class ClimateTraceViewModel : KMMViewModel(), KoinComponent {
3739
init {
3840
isLoadingCountries.value = true
3941
viewModelScope.coroutineScope.launch {
40-
_countryList.value = climateTraceApi.fetchCountries().sortedBy { it.name }
42+
_countryList.value = climateTraceRepository.fetchCountries().sortedBy { it.name }
4143
isLoadingCountries.value = false
4244
}
4345
}
@@ -51,4 +53,4 @@ open class ClimateTraceViewModel : KMMViewModel(), KoinComponent {
5153
isLoadingCountryDetails.value = false
5254
}
5355
}
54-
}
56+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package dev.johnoreilly.climatetrace.di
2+
3+
import dev.johnoreilly.climatetrace.remote.Country
4+
import io.github.xxfast.kstore.KStore
5+
import io.github.xxfast.kstore.file.storeOf
6+
import io.github.xxfast.kstore.file.utils.FILE_SYSTEM
7+
import net.harawata.appdirs.AppDirsFactory
8+
import okio.Path.Companion.toPath
9+
import org.koin.core.module.Module
10+
import org.koin.dsl.module
11+
12+
private const val PACKAGE_NAME = "dev.johnoreilly.climatetrace"
13+
private const val VERSION = "1.0.0"
14+
private const val AUTHOR = "johnoreilly"
15+
16+
actual fun dataModule(): Module = module {
17+
single<KStore<List<Country>>> {
18+
val filesDir: String = AppDirsFactory.getInstance()
19+
.getUserDataDir(PACKAGE_NAME, VERSION, AUTHOR)
20+
21+
FILE_SYSTEM.createDirectories(filesDir.toPath())
22+
23+
storeOf(file = "$filesDir/countries.json".toPath(), default = emptyList())
24+
}
25+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package dev.johnoreilly.climatetrace.di
2+
3+
import dev.johnoreilly.climatetrace.remote.Country
4+
import io.github.xxfast.kstore.KStore
5+
import io.github.xxfast.kstore.file.storeOf
6+
import io.github.xxfast.kstore.file.utils.DocumentDirectory
7+
import io.github.xxfast.kstore.utils.ExperimentalKStoreApi
8+
import okio.Path.Companion.toPath
9+
import org.koin.core.module.Module
10+
import org.koin.dsl.module
11+
import platform.Foundation.NSFileManager
12+
13+
// called by iOS etc
14+
fun initKoin() = initKoin(enableNetworkLogs = false) {}
15+
16+
@OptIn(ExperimentalKStoreApi::class)
17+
actual fun dataModule(): Module = module {
18+
single<KStore<List<Country>>> {
19+
val filesDir: String? = NSFileManager.defaultManager.DocumentDirectory?.relativePath
20+
requireNotNull(filesDir) { "Document directory not found" }
21+
storeOf(file = "$filesDir/countries.json".toPath(), default = emptyList())
22+
}
23+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package dev.johnoreilly.climatetrace.di
2+
3+
import dev.johnoreilly.climatetrace.remote.Country
4+
import io.github.xxfast.kstore.KStore
5+
import io.github.xxfast.kstore.storage.storeOf
6+
import io.github.xxfast.kstore.utils.ExperimentalKStoreApi
7+
import org.koin.core.module.Module
8+
import org.koin.dsl.module
9+
10+
actual fun dataModule(): Module = module {
11+
single<KStore<List<Country>>> {
12+
storeOf(key = "countries", default = emptyList())
13+
}
14+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package dev.johnoreilly.climatetrace.di
2+
3+
import dev.johnoreilly.climatetrace.remote.Country
4+
import io.github.xxfast.kstore.KStore
5+
import io.github.xxfast.kstore.storage.storeOf
6+
import org.koin.core.module.Module
7+
import org.koin.dsl.module
8+
9+
actual fun dataModule(): Module = module {
10+
single<KStore<List<Country>>> {
11+
storeOf(key = "countries", default = emptyList())
12+
}
13+
}

0 commit comments

Comments
 (0)