Proto DataStore

Typed Preferences with Proto DataStore

Preferences DataStore 234 has no schema: a misspelled key compiles, and a value's type is checked only when read. A typed DataStore stores one immutable object instead, written by a Serializer<T> you supply. The classic form, Proto DataStore, generates that class from a Protocol Buffers schema (message SyncState { string source = 1; ... }) with the com.google.protobuf Gradle 19,597 plugin. The same Serializer interface also accepts JSON through kotlinx.serialization 5,947 , which BookNest already uses for its routes, so it stores its sync state that way, with no protobuf toolchain:

data/Settings.kt: a typed store for the last sync (excerpt)Kotlin
@Serializable
data class SyncState(val source: String = "none", val books: Int = 0, val at: Long = 0)
object SyncStateSerializer : Serializer<SyncState> {
  override val defaultValue = SyncState()
  override suspend fun readFrom(input: InputStream): SyncState = try {
    Json.decodeFromString(SyncState.serializer(), input.readBytes().decodeToString())
  } catch (e: SerializationException) {
    throw CorruptionException("sync.json is not a SyncState", e)
  }
  override suspend fun writeTo(t: SyncState, output: OutputStream) =
    output.write(Json.encodeToString(SyncState.serializer(), t).encodeToByteArray())
}
val Context.syncStore: DataStore<SyncState> by dataStore("sync.json", SyncStateSerializer)

Updates are transformations of the whole object, sync.updateData { it.copy(...) } or a new instance, applied atomically. After the repository of Local Catalog Cache seeded the cache, the file on the device read:

Output of 149
{"source":"assets","books":6,"at":1790286303059}

Protobuf 59,573 gives a compact binary file and schema evolution by field number; JSON gives a readable file and no extra plugin.