androidkotlindagger-hiltandroid-jetpack-datastore

Provide preferences datastore with Hilt


I was trying to provide a common DataStore<Preferences> so that the same preference file could be used in multiple places but I got the helpful error message:

Cannot find symbol: DaggerMyApplication_HiltComponents_SingletonC.builder()

@Module
@InstallIn(ApplicationComponent::class)
object DataStoreModule {
    
    @Provides
    fun provideDataStore(@ApplicationContext context: Context): DataStore<Preferences> = context.createDataStore("settings")
}

I can however do the following and use it within an @Inject constructor.

@Singleton
class DataStoreProvider @Inject constructor(@ApplicationContext context: Context) {

    val dataStore: DataStore<Preferences> = context.createDataStore("settings")
}

I assume that the extension createDataStore is doing something that Hilt does not like but I'd appreciate an explanation of what is going on even if the problem is not solvable.


Solution

  • This worked for me:

        @Provides
        @Singleton
        fun dataStore(@ApplicationContext appContext: Context): DataStore<Preferences> =
            appContext.createDataStore("settings")
    

    The idea is to put @Singleton behind the provider method.


    Update on Feb 9, 2021:
    Preferably, create a manager and provide that:

    class DataStoreManager(appContext: Context) {
    
        private val settingsDataStore = appContext.createDataStore("settings")
    
        suspend fun setThemeMode(mode: Int) {
            settingsDataStore.edit { settings ->
                settings[Settings.NIGHT_MODE] = mode
            }
        }
    
        val themeMode: Flow<Int> = settingsDataStore.data.map { preferences ->
            preferences[Settings.NIGHT_MODE] ?: AppCompatDelegate.MODE_NIGHT_UNSPECIFIED
        }
    
    }
    

    AppModule:

    @InstallIn(SingletonComponent::class)
    @Module
    class AppModule {
        @Provides
        @Singleton
        fun dataStoreManager(@ApplicationContext appContext: Context): DataStoreManager =
            DataStoreManager(appContext)
    

    Update on March 20, 2021:
    Version 1.0.0-alpha07

    private val Context.dataStore by preferencesDataStore("settings")
    
    class DataStoreManager(appContext: Context) {
    
        private val settingsDataStore = appContext.dataStore
    
        suspend fun setThemeMode(mode: Int) {
            settingsDataStore.edit { settings ->
                settings[Settings.NIGHT_MODE] = mode
            }
        }
    
        val themeMode: Flow<Int> = settingsDataStore.data.map { preferences ->
            preferences[Settings.NIGHT_MODE] ?: AppCompatDelegate.MODE_NIGHT_UNSPECIFIED
        }
    }
    

    Update on May 1, 2021:

    @Florian is totally right, I had forgotten that.

    Remove dataStoreManager provider from your DI module. Just:

    private val Context.dataStore by preferencesDataStore("settings")
    
    @Singleton //You can ignore this annotation as return `datastore` from `preferencesDataStore` is singletone
    class DataStoreManager @Inject constructor(@ApplicationContext appContext: Context) {
    
        private val settingsDataStore = appContext.dataStore
    
        suspend fun setThemeMode(mode: Int) {
            settingsDataStore.edit { settings ->
                settings[Settings.NIGHT_MODE] = mode
            }
        }
    
        val themeMode: Flow<Int> = settingsDataStore.data.map { preferences ->
            preferences[Settings.NIGHT_MODE] ?: AppCompatDelegate.MODE_NIGHT_UNSPECIFIED
        }
    
    }