Search code examples
androidkotlinarraylistsharedpreferences

How to add item to list stored in sharedPreferences?


I'm trying to store list of string in local storage using sharedPreferences When I lunch my app for the first time repository return null as it should. When i tried to write any new item to my storage and then read it, function

getSearchHistoryItems()

doesn't return any data. Is something wrong with my function to write new items?

interface LocalPreferencesRepository {
    fun getSearchHistoryItems(): List<String>?
    fun addSearchHistoryItem(item: String)
}

class LocalPreferencesRepositoryImpl(
    private val sharedPreferences: SharedPreferences
) : LocalPreferencesRepository {

    override fun getSearchHistoryItems(): List<String>? {
        return Gson().fromJson(
            sharedPreferences.getString(PREF_SEARCH_HISTORY, null),
            object : TypeToken<ArrayList<String>>() {}.type
        )
    }

    override fun addSearchHistoryItem(item: String) {
        val listToSave = listOf(item).plus(getSearchHistoryItems())
        val json = Gson().toJson(listToSave)
        with(sharedPreferences.edit()) { putString(PREF_SEARCH_HISTORY, json); commit() }
    }

    companion object {
        private const val PREF_SEARCH_HISTORY = "PREF_SEARCH_HISTORY"
    }
}

Edit:

override fun getSearchHistoryItems(): List<String>? =
        try {
            Gson().fromJson(
                sharedPreferences.getString(PREF_SEARCH_HISTORY, "").orEmpty(),
                object : TypeToken<ArrayList<String>>() {}.type
            )
        } catch (e: Exception) {
            null
        }

Solution

  • listToSave is a List<Any> because List<String> + List<String>? is going to use the overload of plus that adds the second argument as a single element instead of iterating it and adding all its items. Why not make getSearchHistoryItems() return a non-nullable List (return with .orEmpty())?

    Also, I think you are in danger of a crash when there is nothing currently stored in the SharedPreference. I don't use Gson much, but doesn't it throw an exception if you pass it an invalid Json String or null?

    Also, a tip. There's a KTX extension function SharedPreferences.edit function that lets you pass a lambda where you can put the edits and not have to commit manually. A little bit cleaner to use. By default it internally uses apply() instead of commit() which is what you typically should be doing anyway. If you really need to guarantee there's a write before returning, you should be using coroutines or using Jetpack Datastore instead of SharedPreferences.

    interface LocalPreferencesRepository {
        fun getSearchHistoryItems(): List<String>
        fun addSearchHistoryItem(item: String)
    }
    
    class LocalPreferencesRepositoryImpl(
        private val sharedPreferences: SharedPreferences
    ) : LocalPreferencesRepository {
    
        override fun getSearchHistoryItems(): List<String> {
            return Gson().fromJson(
                sharedPreferences.getString(PREF_SEARCH_HISTORY, ""),
                object : TypeToken<ArrayList<String>>() {}.type
            ).orEmpty()
        }
    
        override fun addSearchHistoryItem(item: String) {
            val listToSave = listOf(item).plus(getSearchHistoryItems())
            val json = Gson().toJson(listToSave)
            sharedPreferences.edit { putString(PREF_SEARCH_HISTORY, json) }
        }
    
        companion object {
            private const val PREF_SEARCH_HISTORY = "PREF_SEARCH_HISTORY"
        }
    }