Blog Infos
Author
Published
Topics
,
Published

Data persistence is a very important aspect of app development. It could make or break the user experience. Storing and managing app settings/data is essential for creating a responsive, reliable and effective user experience. DataStore is a toolkit that helps address this issue. In this guide, I will try to answer What? Why? and how? of DataStore.

What is DataStore?

DataStore is a part of the Jetpack Libraries provided by Google. It’s a simple framework that provides an asynchronous way to store and retrieve both simple and complex data types. It’s the perfect candidate for dealing with app settings, user preferences or any other kind of persistent information.

Why DataStore?

DataStore was introduced as a solution to some of the problems or limitations faced by SharePreferences and SQLite.

  • Asynchronous Operations: All operations are done in an async fashion when it comes to datastore ensuring that the app never goes into ANR state.
  • Strongly Typed Data: SharedPreferences was great but only supported primitive data types, which meant you had to use Gson or Moshi to convert complex data into a JSON and store it as a string and back to an object later on. DataStore solves this by allowing the storing and retrieving of structured data using the Kotlin data class.
  • Type Safety: DataStore provides type safety, reducing the risk of runtime crashes and errors which might occur due to type casting.
  • Modern API: With DataStore you have the option to leverage Kotlin’s modern features like coroutines and flows.
  • Data Consistency: DataStore makes sure that data is read and written atomically. Meaning it does not have common issues like partial writes.
  • Observability: Since DataStore enables modern Kotlin features you can easily observe the changes in the values using flow.
DataStore Types

There are two options when it comes to DataStores, i.e., Preferences DataStore and Proto DataStore.

Preferences DataStore

Preferences DataStore is very similar to SharedPreferences. It’s meant to store simple key-value pairs. However, with the benefit of being built on modern principles and providing all the above-mentioned advantages. Use Preferences DataStore when you need to store simple key-value pairs like user preferences, app settings or some developer flags.

Proto DataStore

Proto DataStore, is more suitable for storing complex data types. It uses protobuf to serialize and deserialize data. It’s the perfect candidate if you want to store more complex data types. Use Proto DataStore when you have to store complex data types and require strong type safety for your data.

Setting Up

Adding DataStore to your android project is fairly straightforward. Head over to the app’s build.gradle file and add the dependency like you would with any other library.

// Add these lines to your app module's build.gradle file
dependencies {
// For Preferences DataStore
implementation "androidx.datastore:datastore-preferences:1.0.0"
// For Proto DataStore
implementation "androidx.datastore:datastore-core:1.0.0"
}
view raw build.gradle hosted with ❤ by GitHub
Using DataStore

We will be using Jetpack compose for these examples but should be very similar for traditional view systems as well.

Storing simple data

Let’s start by storing simple data using Preference DataStore. Take into consideration that you need to store something simple like the app theme the user has selected via a toggle button in the app settings. Toggle on means the app goes into a dark theme and toggle off means the app goes into a light theme.

// Define a DataStore for user preferences
val dataStore: DataStore<Preferences> = context.createDataStore(name = "user_preferences")
// Define a key for the dark mode
val DARK_MODE_KEY = booleanPreferencesKey("dark_mode")
// Function to store the toggle value
suspend fun storeDarkMode(isOn: Boolean) {
dataStore.edit { preferences ->
preferences[DARK_MODE_KEY] = isOn
}
}
// Function to retrieve the dark mode value
val darkModeFlow: Flow<Boolean?> = dataStore.data.map { preferences ->
preferences[DARK_MODE_KEY]
}

Job Offers

Job Offers

There are currently no vacancies.

OUR VIDEO RECOMMENDATION

, ,

Migrating to Jetpack Compose – an interop love story

Most of you are familiar with Jetpack Compose and its benefits. If you’re able to start anew and create a Compose-only app, you’re on the right track. But this talk might not be for you…
Watch Video

Migrating to Jetpack Compose - an interop love story

Simona Milanovic
Android DevRel Engineer for Jetpack Compose
Google

Migrating to Jetpack Compose - an interop love story

Simona Milanovic
Android DevRel Engin ...
Google

Migrating to Jetpack Compose - an interop love story

Simona Milanovic
Android DevRel Engineer f ...
Google

Jobs

In the above code, we first declared a DataStore instance(dataStore) for storing user preferences. We then declared a key (DARK_MODE_KEY) for the value we wanted to store. Then we have a method that would store the value into the DataStore (storeDarkMode) and lastly declare a flow (darkModeFlow) which would retrieve the currently stored value.

Here is an example of how you would retrieve the value and update the value.

// Collect the darkModeFlow and convert it to a State
val darkModeState by darkModeFlow.collectAsState(initial = false)
// Display the currently selected theme in your Composable
Text(text = "is the app in dark mode: ${darkModeState.value}")
// Update teh darkMode value
storeDarkMode(true)

Preference DataStore is great when it comes to simple key-value pairs but when it comes to complex data structures Proto Datastore really thrives. To start using Proto DataStore you must follow these steps.

Define a protobuf schema for your data.

syntax = "proto3";
message UserProfile {
string username = 1;
int32 age = 2;
// Add more fields as needed
}

Generate Kotlin code from the schema using protobuf-gradle-plugin
Add the following to your project.

plugins {
id "com.google.protobuf" version "0.9.4"
}
dependencies {
implementation "com.google.protobuf:protobuf-javalite:3.24.3"
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.24.3"
}
// Generates the java Protobuf-lite code for the Protobufs in this project. See
// https://github.com/google/protobuf-gradle-plugin#customizing-protobuf-compilation
// for more information.
generateProtoTasks {
all().each { task ->
task.builtins {
java {
option 'lite'
}
}
}
}
}
view raw protobuf.gradle hosted with ❤ by GitHub

Rebuild the project. This should generate a file under app/build/generated/source/proto called UserProfile which should have all the methods needed for storing and retrieving values.

Use the generated Kotlin class with Proto DataStore.

// Define a DataStore
val dataStore: DataStore<UserProfile> = context.createDataStore(
fileName = "user_profile.pb",
serializer = UserProfileSerializer // Generated serializer class
)
// Function to retrive the user profile
override suspend fun getUserProfile(): Flow<UserProfle> {
return dataStore.data.map { protoBuilder ->
protoBuilder
}
}
// Function to store the user profile
override suspend fun saveUserProfile(userProfile: UserProfile) {
dataStore.updateData { store ->
store.toBuilder()
.setUserProfile(userProfile)
.build()
}
}
Testing DataStore

Testing is a crucial part of development and DataStore is no exception.

Unit Testing

You can easily write unit tests for DataStore operations by creating a test instance of DataStore. With the test DataStore instance, you can write tests for functions that interact with DataStore. Here is an example:

// Create a test DataStore for Preferences DataStore
val testContext = ApplicationProvider.getApplicationContext<Context>()
val testPreferencesDataStore = testContext.createDataStore(
name = "test_preferences",
serializer = PreferencesSerializer
)
Mocking

You can also mock DataStore using frameworks like Mockito or MockK. This allows you to control the data returned by DataStore and simulate different scenarios of testing.

// Mock the DataStore
val mockDataStore = mockk<DataStore<Preferences>>()
// Define behavior for DataStore functions
coEvery { mockDataStore.data } returns flowOf(mockedPreferences)
Conclusion

In this guide, we explored What is DataStore? Why should one pick DataStore? and how to use DataStore in Jetpack compose? DataStore, being a part of the Jetpack libraries, empowers developers with the tools to manage and store data efficiently and reliably. Happy Coding!!

Feel free to drop a clap or comment or reach out to me on LinkedInTwitterWebsite.

This article was previously published on proandroiddev.com

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
It’s one of the common UX across apps to provide swipe to dismiss so…
READ MORE
blog
In this part of our series on introducing Jetpack Compose into an existing project,…
READ MORE
blog
In the world of Jetpack Compose, where designing reusable and customizable UI components is…
READ MORE
blog

How to animate BottomSheet content using Jetpack Compose

Early this year I started a new pet project for listening to random radio…
READ MORE

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.

Menu