Skip to content

MFlisar/KotPreferences

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

248 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Maven Central API Kotlin Kotlin Multiplatform

KotPreferences

Platforms Android iOS Windows macOS WebAssembly

This library provides following main features:

  • declare preferences via kotlin delegates
  • listen to updates from preferences via kotlin flows
  • update dependencies via suspend functions
  • use those preferences inside compose as State, MutableState or StateFlow

Table of Contents

πŸ’» Supported Platforms

Module android iOS windows macOS wasm
core βœ… βœ… βœ… βœ… βœ…
storage-datastore βœ… βœ… βœ… βœ… ❌
storage-keyvalue βœ… βœ… βœ… βœ… βœ…
extension-compose βœ… βœ… βœ… βœ… βœ…
encryption-aes βœ… ❌ ❌ ❌ ❌

πŸ”§ Setup

Using Version Catalogs

Define the dependencies inside your libs.versions.toml file.

[versions]

kotpreferences = "<LATEST-VERSION>"

[libraries]

core = { module = "io.github.mflisar.kotpreferences:core", version.ref = "kotpreferences" }
storage.datastore = { module = "io.github.mflisar.kotpreferences:storage-datastore", version.ref = "kotpreferences" }
storage.keyvalue = { module = "io.github.mflisar.kotpreferences:storage-keyvalue", version.ref = "kotpreferences" }
extension.compose = { module = "io.github.mflisar.kotpreferences:extension-compose", version.ref = "kotpreferences" }
encryption.aes = { module = "io.github.mflisar.kotpreferences:encryption-aes", version.ref = "kotpreferences" }

And then use the definitions in your projects build.gradle.kts file like following:

implementation(libs.core)
implementation(libs.storage.datastore)
implementation(libs.storage.keyvalue)
implementation(libs.extension.compose)
implementation(libs.encryption.aes)
Direct Dependency Notation

Simply add the dependencies inside your build.gradle.kts file.

val kotpreferences = "<LATEST-VERSION>"

implementation("io.github.mflisar.kotpreferences:core:${kotpreferences}")
implementation("io.github.mflisar.kotpreferences:storage-datastore:${kotpreferences}")
implementation("io.github.mflisar.kotpreferences:storage-keyvalue:${kotpreferences}")
implementation("io.github.mflisar.kotpreferences:extension-compose:${kotpreferences}")
implementation("io.github.mflisar.kotpreferences:encryption-aes:${kotpreferences}")

πŸš€ Usage

1. Define a SettingsModel

// Depending on the platform:
//   - common: DataStoreStorage.create(name = "preferences")
//   - jvm: DataStoreStorage.create(folder = File(System.getProperty("user.dir")), name = "preferences")
//   - android/iOS: DataStoreStorage.create(name = "preferences")
//   - ...
object Preferences : SettingsModel(DataStoreStorage.create(name = "preferences")) {

    // main data types
    val someString by stringPref("value")
    val someBool by boolPref(false)
    val someInt by intPref(123)
    val someLong by intPref(123L)
    val someFloat by intPref(123f)
    val someDouble by intPref(123.0)

    // enum
    val someEnum by enumPref(Enum.Value1)

    // custom
    val someCustomClass1 by anyStringPref(TestClass.CONVERTER, TestClass()) // converts TestClass to a string and saves this string
    val someCustomClass2 by anyIntPref(TestClass.CONVERTER, TestClass())    // converts TestClass to an int and saves this int
    val someCustomClass3 by anyLongPref(TestClass.CONVERTER, TestClass())   // converts TestClass to a long and saves this long

    // sets
    val someStringSet by stringSetPref(setOf("a"))
    val someIntSet by intSetPref(setOf(1))
    val someLongSet by longSetPref(setOf(1L))
    val someFloatSet by floatSetPref(setOf(1f))
    val someDoubleSet by doubleSetPref(setOf(1.0))

    // NULLABLE vs NON NULLABLE
    val nonNullableString by stringPref()
    val nullableString by nullableStringPref()
    val nonNullableInt by intPref()
    val nullableInt by nullableIntPref()
    val nonNullableFloat by floatPref()
    val nullableFloat by nullableFloatPref()
    val nonNullableDouble by doublePref()
    val nullableDouble by nullableDoublePref()
    val nonNullableLong by longPref()
    val nullableLong by nullableLongPref()
    val nonNullableBool by boolPref()
    val nullableBool by nullableBoolPref()

    // custom
    val someCustomClass4 by nullableAnyStringPref(TestClass.CONVERTER, TestClass())
    val someCustomClass5 by nullableAnyIntPref(TestClass.CONVERTER, TestClass())
    val someCustomClass6 by nullableAnyLongPref(TestClass.CONVERTER, TestClass())
}

2a) Use the SettingsModel in plain kotlin (flows + suspending functions)

// 1) get a flow
val flow = Preferences.someString.flow

// 2) read/update values by suspend functions
scope.launch(Dispatchers.IO) {
    val value = Preferences.someInt.read()
    Preferences.someInt.update(value + 1)
}

2b) Use the SettingsModel in views (e.g. with Lifecycle Scope)

// 1) simply observe a setting
Preferences.someString.observe(lifecycleScope) {
    L.d { "someString = $it"}
}

// 2) direct read (not recommended if not necessary but may be useful in many cases)
// => simply returns read() in a blocking way)
val name = Preferences.someString.value

// 3) observe a setting once
Preferences.someString.observeOnce(lifecycleScope) {
    L.d { "someString = $it"}
}

// 4) observe ALL settings
Preferences.changes.onEach {
    L.d { "[ALL SETTINGS OBSERVER] Setting '${it.setting.key}' changed its value to ${it.value}" }
}.launchIn(lifecycleScope)

// 5) observe SOME settings
Preferences.changes
    .filter {
        it.setting == Preferences.someString ||
        it.setting == Preferences.someBool
    }.onEach {
        L.d { "[SOME SETTINGS OBSERVER] Setting '${it.setting.key}' changed its value to ${it.value}" }
    }.launchIn(lifecycleScope)

// 6) read multiple settings in a suspending way
lifecycleScope.launch(Dispatchers.IO) {
    val someString = Preferences.someString.read()
    val someBool = Preferences.someBool.read()
}

2c) Use the SettingsModel in compose

val name = Preferences.someString.collectAsState()
val name = Preferences.someString.collectAsStateWithLifecycle()

// simply use the state inside your composables, the state will change whenever the setting behind it will change

val name = Preferences.someString.asMutableState()

// simple use and even update the state now

πŸ“ Modules

✨ Demo

A full demo is included inside the demo module, it shows nearly every usage with working examples.

ℹ️ More

πŸ“š API

Check out the API documentation.

πŸ’‘ Other Libraries

You can find more libraries (all multiplatform) of mine that all do work together nicely here.

Packages

 
 
 

Contributors

Languages