Android Jetpack Datastore Data Object Serialization With Kotlin Serialization

Edit project build.gradle

buildscript {
    ext {
        kotlin_version = "1.7.20"
    }

    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
    }
}

Edit module build.gradle

plugins {
    id 'kotlinx-serialization'
}

dependencies {
    implementation("androidx.datastore:datastore-preferences:1.0.0")
    implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1"
}

Edit proguard-rules.pro

# Keep `Companion` object fields of serializable classes.
# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.
-if @kotlinx.serialization.Serializable class **
-keepclassmembers class <1> {
    static <1>$Companion Companion;
}

# Keep `serializer()` on companion objects (both default and named) of serializable classes.
-if @kotlinx.serialization.Serializable class ** {
    static **$* *;
}
-keepclassmembers class <2>$<3> {
    kotlinx.serialization.KSerializer serializer(...);
}

# Keep `INSTANCE.serializer()` of serializable objects.
-if @kotlinx.serialization.Serializable class ** {
    public static ** INSTANCE;
}
-keepclassmembers class <1> {
    public static <1> INSTANCE;
    kotlinx.serialization.KSerializer serializer(...);
}

# @Serializable and @Polymorphic are used at runtime for polymorphic serialization.
-keepattributes RuntimeVisibleAnnotations,AnnotationDefault

# Serializer for classes with named companion objects are retrieved using `getDeclaredClasses`.
# If you have any, uncomment and replace classes with those containing named companion objects.
#-keepattributes InnerClasses # Needed for `getDeclaredClasses`.
#-if @kotlinx.serialization.Serializable class
#com.example.myapplication.HasNamedCompanion, # <-- List serializable classes with named companions.
#com.example.myapplication.HasNamedCompanion2
#{
#    static **$* *;
#}
#-keepnames class <1>$$serializer { # -keepnames suffices; class is kept when serializer() is kept.
#    static <1>$$serializer INSTANCE;
#}

Create data class BankInfo

import kotlinx.serialization.Serializable@Serializabledata class BankInfo(    val name: String? = null,    val accountName: String? = null,    val accountNumber: String? = null)

Create BankInfoSerializer

object BankInfoSerializer : Serializer<BankInfo> {    override val defaultValue = BankInfo()    override suspend fun readFrom(input: InputStream): BankInfo {        try {            return Json.decodeFromString(                BankInfo.serializer(), input.readBytes().decodeToString()            )        } catch (serialization: SerializationException) {            return defaultValue            // Serialization might fail if data type of BankInfo changed            // throw CorruptionException("Unable to read UserPrefs", serialization)        }    }    override suspend fun writeTo(t: BankInfo, output: OutputStream) {        output.write(            Json.encodeToString(BankInfo.serializer(), t)                .encodeToByteArray()        )    }}

Edit AppSingleton.kt

import androidx.datastore.core.DataStoreimport androidx.datastore.dataStoreimport androidx.datastore.preferences.core.Preferencesimport androidx.datastore.preferences.preferencesDataStoreclass AppSingleton {    companion object {        // val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")        val Context.bankInfoDataStore: DataStore<BankInfo> by dataStore("bankInfo", BankInfoSerialize)    }}

Usage in compose

@Composablefun HomeScreen(    // viewModel: HomeViewModel = viewModel()) {    val bankInfo by context.bankInfoDataStore.data.collectAsState(initial = null)    var bankAccountName by remember(bankInfo?.accountName) { mutableStateOf(bankInfo?.accountName ?: "") }    var bankAccountNumber by remember(bankInfo?.accountNumber) { mutableStateOf(bankInfo?.accountNumber ?: "") }    suspend fun saveBankInfo(name: String, accountName: String, accountNumber: String) {        context.bankInfoDataStore.updateData { it.copy(name = name, accountName = accountName, accountNumber = accountNumber) }    }}

References:

❤️ Is this article helpful?

Buy me a coffee ☕ or support my work via PayPal to keep this space 🖖 and ad-free.

Do send some 💖 to @d_luaz or share this article.

✨ By Desmond Lua

A dream boy who enjoys making apps, travelling and making youtube videos. Follow me on @d_luaz

👶 Apps I built

Travelopy - discover travel places in Malaysia, Singapore, Taiwan, Japan.