TomR
TomR

Reputation: 3036

Where to put Android Compose global variable (e.g. preferences DataStore) - Context is appropriate for the Composables only?

I am trying to introduce preferences DataStore in my Android Compose app and several tutorials suggest the use of Conext for keeping the reference to the DataStore instance. E.g. https://betterprogramming.pub/using-jetpack-preferences-datastore-more-effectively-414e1126cff7 and https://towardsdev.com/using-android-jetpack-datastore-with-jetpack-compose-6184338cf9c0 are such tutorials.

While it is possible to use Context for this, the Context is accessible from the Composables only and not from the ViewModels and repositories which are expected to be the most heavy users of DataStore. E.g. ViewModels can have functions that execute write functions of the DataStore. Repositories can read the saved URL from the DataStore.

So - Context is not the appropriate object to keep references to that DataStore, but which object is appropriate? Maybe AppContainer?

DataStore is expected to be application wide singleton. Maybe preferencesDataStore is already guaranteeing this?

I.e. maybe I can call

myLocalVariable: DataStore<Preferences> by preferencesDataStore(name = "my_app_configuration")

in each of my ViewModel or repository and then I can use myLocalVariable in those modules freely and the perferencesDataStore guarntees that there is only one DataStore instance for the entire application?

Upvotes: 0

Views: 3788

Answers (2)

Zakir Sheikh
Zakir Sheikh

Reputation: 1038

My suggestion would be to use Hilt to inject code into viewModel Here is a example.

@Module
    @InstallIn(SingletonComponent::class)
    object Singleton {
        /**
         * Provides the Singleton Implementation of Preferences DataStore.
         */
        @Provides
        @Singleton
        fun preferences(@ApplicationContext context: Context) = Preferences(context)
    }

Here the Preferences is a wraaper around DataStore.

Now you inject the instance of Preference into the viewModel easily.

Here I have created a lib to make the integration with compose easily. here is the dependency from jitpack the io implementation 'com.github.prime-zs:support:Tag'

Preference

Upvotes: 1

Thracian
Thracian

Reputation: 66506

Using dependency inversion and abstraction you can keep Android code out of your repository classes as they should and using dependency injection you can pass Context to implementation, assuming Dagger is used you can bind your interface to implementation using @Binds annotation

create an Interface for write/read operations

interface MyDataStore {
  fun read()
  fun write()
}

an implementation that takes Context as argument

class DataStoreImpl(context:Context):MyDataStore {

  // Implementation
}

If you define your repositories or ViewModels as

class Repository(myDataStore: MyDataStore)

you will be able to remove coupling to context or DataStore in your classes. This way if google decides to introduce another class for storage in the future you can easily replace it with existing one or can implement multiple implementation versions and change to required one using polymorphism

Upvotes: 2

Related Questions