⭐ Contribute!
⭐ Contribute!
  • Home
  • About Kotlin
  • 🚀 Let's start
  • Configure KMP
  • User interface
  • Navigation
  • Ressources
  • Architecture
  • Connectivity
  • Preferences
  • Local Database

Preferences

Let's use preferences to store the last time the quiz was updated. We will use a multiplatform library called Kstore

Kstore is a tiny Kotlin multiplatform library that assists in saving and restoring objects to and from disk using kotlinx.coroutines, kotlinx.serialization and kotlinx.io. Inspired by RxStore

More settings options

if you want alternate library to store simple key-value data, you can use Multiplatform-Settings or DataStore multiplatform. Be carefull, not all target web platform

Add kstore dependency to your project for each target platform

build.gradle.kts (composeMain)
  commonMain.dependencies {
            ...
            implementation(libs.kstore)
        }
        androidMain.dependencies {
            ...
            implementation(libs.kstore.file)
        }
        desktopMain.dependencies {
            ...
            implementation(libs.kstore.file)
        }
        iosMain.dependencies {
            implementation(libs.kstore.file)
        }
        wasmJsMain.dependencies {
            implementation(libs.kstore.storage)
        }
    ...

:::

Define the native call to get the kstore instance

platform.kt (commonMain)
    expect fun getKStore(): KStore<Quiz>?

:::

Define each platform call to get the kstore instance for Android, iOS, Web, Desktop

platform.kt (androidMain)
    actual fun getKStore(): KStore<RequestTime>? {
        return storeOf(QuizApp.context().dataDir.path.plus("/quiz.json").toPath())
    }

:::

Also Android needs context to instanciate the kstore. Without injection library, you can use an App context singleton.

QuizApp.kt (androidMain)
class QuizApp : Application() {
    init {
        app = this
    }

    companion object {
        private lateinit var app: QuizApp
        fun context(): Context = app.applicationContext
    }
} 

:::

Add the QuizApp to the AndroidManifest.xml

AndroidManifest.xml (androidMain)
...
    <application
        android:name=".QuizApp"
...
platform.kt (iosMain)
@OptIn(ExperimentalKStoreApi::class, ExperimentalForeignApi::class)
actual fun getKStore(): KStore<RequestTime>? {
    return NSFileManager.defaultManager.URLForDirectory(
        directory = NSDocumentDirectory,
        appropriateForURL = null,
        create = false,
        inDomain = NSUserDomainMask,
        error = null
    )!!.path?.let {
        storeOf(
        file= Path(it)
    )
    }
}

:::

platform.kt (wasmJsMain)
    actual fun getKStore(): KStore<RequestTime>? {
        return storeOf(key = "kstore_quiz")
     }

:::

platform.kt (desktopMain)
actual fun getKStore(): KStore<RequestTime>? {
    return storeOf(Path("quiz.json"))
}

:::

Add a RequestTime object with an updatable timestamp

Quiz.kt (commonMain)
@Serializable
data class RequestTime(val updateTime: Long = 0L)

Create a KStoreDataSource class to store the kstore data

KStoreDataSource.kts (commonMain)

class KStoreDataSource() {

private val kStoreQuiz: KStore<RequestTime>? = getKStore()

suspend fun getUpdateTimeStamp(): Long {
    return kStoreQuiz?.get()?.updateTime ?: kStoreQuiz?.set(RequestTime(0L)).let {
        0L
    }
}

suspend fun setUpdateTimeStamp(timeStamp: Long) {
    kStoreQuiz?.update { requestTime: RequestTime? ->
        requestTime?.copy(updateTime = timeStamp)
    }
}

}

:::


Update the QuizRepository class to use the kstore

::: details QuizRepository.kts (commonMain) 

``` kotlin
class QuizRepository {
   private val mockDataSource = MockDataSource()
   private val quizApiDatasource = QuizApiDatasource()
   private var quizKStoreDataSource = KStoreDataSource()

   private suspend fun fetchQuiz(): List<Question> = quizApiDatasource.getAllQuestions().questions

   private suspend fun fetchAndStoreQuiz(): List<Question> {
       val questions = fetchQuiz()
       //Later on we will store the question in a database SQLite
       return questions
   }

   @OptIn(ExperimentalTime::class)
   suspend fun updateQuiz(): List<Question> {
       try {
           val lastRequest = quizKStoreDataSource.getUpdateTimeStamp()
           return if (lastRequest == 0L || lastRequest - Clock.System.now().epochSeconds > 300000) {
               fetchAndStoreQuiz()
           } else {
               fetchQuiz() //later on we will fetch from the database
           }
       } catch (e: NullPointerException) {
           return fetchAndStoreQuiz()
       } catch (e: Exception) {
           e.printStackTrace()
           return mockDataSource.generateDummyQuestionsList()
       }
   }
}

:::

Sources

The full sources can be retrieved here

🎬 Summary video of the course

Edit this page
Last Updated: 7/30/25, 1:37 PM
Contributors: Ibrahim Gharbi, Brah
Prev
Connectivity
Next
Local Database