🗡️

Jetpack DataStore(PreferencesDataStore)をHiltで注入する

2022/01/28に公開

一般的な記述法

Jetpack DataStoreはKotlinの言語仕様を生かした記述で簡単に定義できるようになっています。

// トップレベルに記述する必要がある
val Context.dataStore: DataStore<Preferences> by preferencesDataStore("settings")

トップレベル宣言をすると、パッケージをimportするだけで定義した内容へアクセスすることができます。

さらにContext拡張プロパティとして、委譲プロパティを使って定義しています。

拡張メソッドと比較すると拡張プロパティを頻繁に使う印象がないのですけれど、このように定義することでContextを継承したActivityなどに、dataStoreプロパティが拡張されるため便利に使うことができます。

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    val value: Flow<String> = dataStore
        .data
        .map { preferences ->
            preferences[stringPreferencesKey("foo")] ?: ""
        }
}

by preferencesDataStore("settings")の委譲プロパティを使うことで、DataStoreの生成処理などが隠蔽されます。ただのプロパティアクセスでDataStoreが生成され、使えるようになっています。

DIをしたい場合

こうしたKotlinの仕組みを利用した記述は、利便性は高いもののHiltでDataStoreインスタンスを管理して、注入したい場合には少し厄介です。

Contextの拡張プロパティになってしまうのでContextがあればどこでもDataStoreが取得できてしまいます。また、context.dataStoreを取得するためだけに、やれることの多いContextをDIするのも避けたいところです。

この場合は、PreferenceDataStoreFactoryを使ってこのように書くとよさそうです。

@Singleton
@Provides
fun providePreferencesDataStore(
    @ApplicationContext context: Context
): DataStore<Preferences> = PreferenceDataStoreFactory.create(
    produceFile = {
        context.preferencesDataStoreFile("settings")
    }
)

やっていることは、実はby preferencesDataStore()に委譲している初期生成の内容そのままですが、内部でDataStoreインスタンスをSingletonとして扱う部分を、Hiltの@Singletonアノテーションに任せて簡潔にしています。

Discussion