Skip to content

Commit

Permalink
[#2855] Add Flow support for multiplatform settings
Browse files Browse the repository at this point in the history
  • Loading branch information
mustafaozhan committed Oct 13, 2023
1 parent 14e946b commit 00f2515
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 8 deletions.
3 changes: 3 additions & 0 deletions client/core/persistence/client-core-persistence.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ kotlin {
dependencies {
libs.common.apply {
implementation(koinCore)
implementation(coroutines)
implementation(multiplatformSettings)
implementation(multiplatformSettingsCoroutines)
}
}
}
Expand All @@ -30,6 +32,7 @@ kotlin {
libs.common.apply {
implementation(test)
implementation(mockative)
implementation(coroutinesTest)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import android.content.Context
import android.content.SharedPreferences
import com.oztechan.ccc.client.core.persistence.Persistence
import com.oztechan.ccc.client.core.persistence.PersistenceImpl
import com.russhwolf.settings.Settings
import com.russhwolf.settings.ObservableSettings
import com.russhwolf.settings.SharedPreferencesSettings
import org.koin.core.module.dsl.bind
import org.koin.core.module.dsl.singleOf
Expand All @@ -14,7 +14,7 @@ private const val KEY_APPLICATION_PREFERENCES = "application_preferences"

actual val clientCorePersistenceModule = module {
singleOf(::provideSharedPreferences)
single<Settings> { SharedPreferencesSettings(get()) }
single<ObservableSettings> { SharedPreferencesSettings(get()) }
singleOf(::PersistenceImpl) { bind<Persistence>() }
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.oztechan.ccc.client.core.persistence

import kotlinx.coroutines.flow.Flow

interface Persistence {
fun <T : Any> getValue(key: String, defaultValue: T): T
fun <T : Any> setValue(key: String, value: T)
fun <T : Any> getFlow(key: String, defaultValue: T): Flow<T>
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
package com.oztechan.ccc.client.core.persistence

import com.oztechan.ccc.client.core.persistence.error.UnsupportedPersistenceException
import com.russhwolf.settings.Settings
import com.russhwolf.settings.ObservableSettings
import com.russhwolf.settings.coroutines.getBooleanFlow
import com.russhwolf.settings.coroutines.getFloatFlow
import com.russhwolf.settings.coroutines.getIntFlow
import com.russhwolf.settings.coroutines.getLongFlow
import com.russhwolf.settings.coroutines.getStringFlow
import kotlinx.coroutines.flow.Flow

internal class PersistenceImpl(private val settings: ObservableSettings) : Persistence {

internal class PersistenceImpl(private val settings: Settings) : Persistence {
@Suppress("UNCHECKED_CAST")
override fun <T : Any> getValue(key: String, defaultValue: T): T = when (defaultValue) {
is Long -> settings.getLong(key, defaultValue) as T
Expand All @@ -22,4 +29,18 @@ internal class PersistenceImpl(private val settings: Settings) : Persistence {
is Float -> settings.putFloat(key, value)
else -> throw UnsupportedPersistenceException()
}

@Suppress("UNCHECKED_CAST", "OPT_IN_USAGE")
override fun <T : Any> getFlow(key: String, defaultValue: T): Flow<T> =
when (defaultValue) {
is Long -> settings.getLongFlow(key, defaultValue) as Flow<T>
is String -> settings.getStringFlow(key, defaultValue) as Flow<T>
is Int -> settings.getIntFlow(key, defaultValue) as Flow<T>
is Boolean -> settings.getBooleanFlow(key, defaultValue) as Flow<T>
is Float -> settings.getFloatFlow(key, defaultValue) as Flow<T>
else -> {
settings
throw UnsupportedPersistenceException()
}
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
package com.oztechan.ccc.client.core.persistence

import com.oztechan.ccc.client.core.persistence.error.UnsupportedPersistenceException
import com.russhwolf.settings.Settings
import com.russhwolf.settings.ObservableSettings
import com.russhwolf.settings.coroutines.getBooleanFlow
import com.russhwolf.settings.coroutines.getFloatFlow
import com.russhwolf.settings.coroutines.getIntFlow
import com.russhwolf.settings.coroutines.getLongFlow
import com.russhwolf.settings.coroutines.getStringFlow
import io.mockative.Mock
import io.mockative.classOf
import io.mockative.configure
import io.mockative.every
import io.mockative.mock
import io.mockative.verify
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import kotlin.random.Random
import kotlin.test.Test
import kotlin.test.assertEquals
Expand All @@ -19,7 +27,8 @@ internal class PersistenceTest {
}

@Mock
private val settings = configure(mock(classOf<Settings>())) { stubsUnitByDefault = true }
private val settings =
configure(mock(classOf<ObservableSettings>())) { stubsUnitByDefault = true }

private val key = "key"

Expand Down Expand Up @@ -60,6 +69,38 @@ internal class PersistenceTest {
.wasInvoked()
}

@Suppress("OPT_IN_USAGE")
@Test
fun `getFlow returns the same type`() = runTest {
every { settings.getFloatFlow(key, mockFloat) }
.returns(flowOf(mockFloat))
every { settings.getBooleanFlow(key, mockBoolean) }
.returns(flowOf(mockBoolean))
every { settings.getIntFlow(key, mockInt) }
.returns(flowOf(mockInt))
every { settings.getStringFlow(key, mockString) }
.returns(flowOf(mockString))
every { settings.getLongFlow(key, mockLong) }
.returns(flowOf(mockLong))

assertEquals(mockFloat, persistence.getFlow(key, mockFloat).firstOrNull())
assertEquals(mockBoolean, persistence.getFlow(key, mockBoolean).firstOrNull())
assertEquals(mockInt, persistence.getFlow(key, mockInt).firstOrNull())
assertEquals(mockString, persistence.getFlow(key, mockString).firstOrNull())
assertEquals(mockLong, persistence.getFlow(key, mockLong).firstOrNull())

verify { settings.getFloatFlow(key, mockFloat) }
.wasInvoked()
verify { settings.getBooleanFlow(key, mockBoolean) }
.wasInvoked()
verify { settings.getIntFlow(key, mockInt) }
.wasInvoked()
verify { settings.getStringFlow(key, mockString) }
.wasInvoked()
verify { settings.getLongFlow(key, mockLong) }
.wasInvoked()
}

@Test
fun `setValue sets the same type`() {
persistence.setValue(key, mockFloat)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ package com.oztechan.ccc.client.core.persistence.di
import com.oztechan.ccc.client.core.persistence.Persistence
import com.oztechan.ccc.client.core.persistence.PersistenceImpl
import com.russhwolf.settings.NSUserDefaultsSettings
import com.russhwolf.settings.Settings
import com.russhwolf.settings.ObservableSettings
import org.koin.core.module.dsl.bind
import org.koin.core.module.dsl.singleOf
import org.koin.dsl.module

actual val clientCorePersistenceModule = module {
single<Settings> {
single<ObservableSettings> {
NSUserDefaultsSettings(get<NativeDependencyWrapper>().userDefaults)
}
singleOf(::PersistenceImpl) { bind<Persistence>() }
Expand Down
1 change: 1 addition & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ common-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotl
common-testAnnotations = { module = "org.jetbrains.kotlin:kotlin-test-annotations-common", version.ref = "kotlin" }
common-koinCore = { module = "io.insert-koin:koin-core", version.ref = "koinCore" }
common-multiplatformSettings = { module = "com.russhwolf:multiplatform-settings", version.ref = "multiplatformSettings" }
common-multiplatformSettingsCoroutines = { module = "com.russhwolf:multiplatform-settings-coroutines", version.ref = "multiplatformSettings" }
common-kotlinXDateTime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinXDateTime" }
common-ktorLogging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" }
common-ktorJson = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" }
Expand Down

0 comments on commit 00f2515

Please sign in to comment.