# Use DataStore instead of Shared Preferences

## Introduction

You may come across a requirement in your app to store some configuration (not that big to use Room).

For example, the previous UI mode - (Dark/Light)

You might come across Shared Preferences for this requirement. Shared Preferences are used to persist data of key-value pairs.

I agree that shared preferences work fine but are still way too old. Imagine if you want to observe the live changes of a preference value. you can't.

## DataStore

DataStore is a part of Jetpack libraries that lets you store data of key-value pairs or typed objects in the device.

### Types

* **Preferences** **DataStore** - Stores key-value pairs
    
* **Proto** **DataStore** - Stores typed objects using protocol buffers.
    

In this guide, we will learn about the Preferences DataStore.

Proto DataStore is for another day.

## Why you should use DataStore instead of Shared Preferences?

![SharedPreferences](https://3.bp.blogspot.com/-Vk_q5hWw6DQ/X00ZfiRrB9I/AAAAAAAAPlo/u-kvBvmMfzgRnNViYLwaAim-E7wq5yxKACLcBGAsYHQ/s1600/Screen%2BShot%2B2020-08-31%2Bat%2B11.25.43%2BAM.png align="left")

* Credits: [Android Developers Blog](https://android-developers.googleblog.com/2020/09/prefer-storing-data-with-jetpack.html)
    

## Guide

### Setup

Add the datastore preference in your grade dependency

```kotlin
dependencies {
        implementation "androidx.datastore:datastore:1.0.0"
}
```

### Build DataStore

Create a preference data store as an extension of context using the `preferencesDataStore` delegate in the top level of a Kotlin file.

The `name` of the Preferences DataStore is a mandatory parameter.

This ensures that a single instance of DataStore is created for the application process.

```kotlin
// Top level of any kotlin file.
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "configs")
```

### Create Preference Keys

To store key-value pairs, we have to create keys for the preferences.

```kotlin
object PreferenceKeys {
    val booleanKey = booleanPreferencesKey(name = "key_for_bool_value")
    val byteArrayKey = byteArrayPreferencesKey(name = "key_for_byte_array_value")
    val doubleKey = doublePreferencesKey(name = "key_for_double_value")
    val floatKey = floatPreferencesKey(name = "key_for_float_value")
    val intKey = intPreferencesKey(name = "key_for_int_value")
    val longKey = longPreferencesKey(name = "key_for_long_value")
    val stringKey = stringPreferencesKey(name = "key_for_string_value")
    val stringSetKey = stringSetPreferencesKey("key_for_string_set_value")
}
```

### Write data

Write a value to the preferences with the following code.

```kotlin
suspend fun writeValue(key: Preferences.Key<Int>, value: Int) {
    context.dataStore.edit { preferences ->
        // Set the new value to the preference.
        preferences[key] = value
    }
}
```

Write a value from a fragment or activity

```kotlin
// In an activity or fragment
// You can use any CoroutineScope other than lifeCycleScope.
lifeCycleScope.launch {
    writeValue(key = intKey, value = 17)
}
```

### Read data

Read value from the preferences with the following code.

It returns a Flow.

```kotlin
fun readValue(key: Preferences.Key<Int>, defaultValue: Int): Flow<Int> {
    return context.dataStore.data.map { preferences ->
        preferences[key] ?: defValue
    }
}
```

Collect the value from the Flow

```kotlin
// In an activity or fragment
// You can use any CoroutineScope other than lifeCycleScope.
lifeCycleScope.launch {
    readValue(key = intKey, defaultValue = 0).collect { value ->
        // Utilize the value here
    }
}
```

## Bonus

### Extension function to work with all types of keys.

```kotlin
/***
 *
 * An Extension function to DataStore<Preference>.
 * Generic way to getting preference value from the data store.
 * Returns preference value for the @param key if found,
 * Otherwise it returns @param defValue
 *
 * @param key Preference key
 * @param defValue default value if the preference key is not found
 *
 * @return a Flow of type T
 */
fun <T> DataStore<Preferences>.getValue(
    key: Preferences.Key<T>,
    defValue: T
): Flow<T> {
    return data.map { preferences ->
        preferences[key] ?: defValue
    }
}

/***
 *
 * An Extension function to DataStore<Preference>
 * Generic way to editing preference value from the data store.
 * Edit the value of the preference @param key with @param value.
 *
 * @param key Preference key
 * @param value Value need to be set to the given key
 *
 */
suspend fun <T> DataStore<Preferences>.setValue(
    key: Preferences.Key<T>,
    value: T
) {
    edit { preferences ->
        preferences[key] = value
    }
}
```

That's it for today.

---

Please do like if it helped you.

Comment if you have any queries.

Share if you want to help others.

For more articles like this, Do follow.

Thanks for reading. &lt;3
