Add datastore-preferences module#86
Merged
Merged
Conversation
Adds type-safe key support for Jetpack DataStore (Preferences) via a new deployable module. DataStoreKey is a dedicated async-only key family (AsyncKey over new DataStoreValueGetter/Setter interfaces) with succinct builders that return async keys directly, so no runBlocking is needed anywhere and primitives don't require an async() call. MutableStateFlow emissions are wrapped in DataStoreValue (Uninitialized / Loaded) so consumers can distinguish the uninitialized state from an absent key. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXL3oASXZA1pviJnn8nSbL
Covers key variance / out-of-core key creation, the builder shadowing pattern, datastore-preferences semantics, DataStore testing gotchas (real-time IO vs runTest virtual time), and the new-module checklist. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXL3oASXZA1pviJnn8nSbL
Adds the module to the Object Serialization and Async Support sections of the docs landing page, completing its representation in every relevant section alongside the existing stores. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXL3oASXZA1pviJnn8nSbL
Adds a DataStore screen to the sample app (mirroring the SharedPref screen) with a Hilt-provided DataStore<Preferences>, plus an instrumented test mirroring SharedPrefInstrumentedTest. Backfills an "Observing Keys as Flows" section in the usage docs covering the flow() extensions on SharedPreferences, DataStore and SavedStateHandle. androidx.datastore 1.2+ requires minSdk 23, so the datastore-preferences module and sample-app override the repo's default minSdk 21. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXL3oASXZA1pviJnn8nSbL
Bumps the shared minSdk in buildSrc Config from 21 to 23 (required by androidx.datastore 1.2) and removes the per-module overrides on datastore-preferences and sample-app. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXL3oASXZA1pviJnn8nSbL
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a new deployable module,
com.episode6.typed2:datastore-preferences, providing type-safe keys for Jetpack DataStore (Preferences flavor).Design
DataStoreKey<T, B>is anAsyncKeytypealias over newDataStoreValueGetter/DataStoreValueSetterinterfaces. DataStore has no synchronous access, so the module's API only accepts these async keys and there is norunBlockinganywhere; the suspend boundary lives in theDataStore<Preferences>extensions (data.first()for reads, androidx's suspendedit {}for writes). No core changes were needed.key("someInt").int(default = 2)returns aDataStoreKeydirectly (no.async()on primitives). The builders shadow core's primitive signatures and convert internally withasync(EmptyCoroutineContext), so trivial mappers skip the dispatcher hop. Serialization keys compose via core'sin GETTER/SETTERvariance:key("obj").gson<Foo>().async().contains/removeare name-based (works across value types), anddouble()stays string-backed like typed2's other stores.DataStoreValuewrapper for state flows —mutableStateFlow(key, scope)returnsMutableStateFlow<DataStoreValue<T>>, starting asUninitializedand emittingLoaded(value)per read, so consumers can distinguish "not read yet" from "key not present" (Loaded(null)). SettingLoaded(null)removes the entry.property(key, scope)keeps the ergonomic nullable shape on top.get/edit/launchEdit/set/update/removesuspend extensions andflow(key)with a raw-value prefilter so unrelated edits don't re-run expensive mappers.Testing
Unit tests run against real DataStores on plain JVM (via
androidx.datastore:datastore-preferences-core1.2.1, no Robolectric): builder round-trips, defaults,required(), name-based remove, null-remove semantics, gson composition, raw-androidx interop, and Turbine tests forflow/mutableStateFlow/property. Full./gradlew check dokkaGenerateHtmlpasses.Follow-ups (out of scope)
androidx.datastore:datastore-preferencesartifact + DI wiring)🤖 Generated with Claude Code
https://claude.ai/code/session_01CXL3oASXZA1pviJnn8nSbL