Search code examples
androidkotlinandroid-jetpack-composeandroid-jetpack-datastore

Create instance of a class in the hilt in the another module


I have 5 modules (main, splash, pick location, permissions, preferences).

I want to use the Preferences module in Splash and PickLocation.

This is the flow of my modules: Splash -> PickLocation

When I want to use the DataStore class in the PickLocation module to save new locations, I have a problem:

If I create a new instance of the DataStore class in the Splash module and create another instance of the same class in the PickLocation module DataStore not working, but if i just create an instance in the PickLocation everything is working. how can I use dagger hilt to create one instance and access from all modules?

Preference module DI:

package com.mykuyaclient.preference.di

import android.content.Context
import com.mykuyaclient.preference.datastores.CheckLocationIsSetDataStore
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent

@Module
@InstallIn(SingletonComponent::class)
object PreferenceModule {

    @Provides
    fun provideCheckLocationIsSetDataStore(@ApplicationContext appContext: Context): CheckLocationIsSetDataStore =
        CheckLocationIsSetDataStore(appContext)
}

PickLocationScreen codes:

@Composable
fun MapScreen(navigatorViewModel: PickLocationViewModel) {
    **val context = LocalContext.current
    val dataStore = CheckLocationIsSetDataStore(context = context)**
    Surface(color = AppColor.ThemeColor.BACKGROUND) {
        val mapView = rememberMapViewWithLifecycle()
        Column(Modifier.fillMaxSize()) {
            Box(modifier = Modifier.fillMaxSize()) {

                MapViewContainer(mapView, navigatorViewModel)
                MapPinOverlay()

                Column(modifier = Modifier.align(Alignment.BottomCenter)) {
                    Button(
                        colors = ButtonDefaults.buttonColors(
                            backgroundColor = AppColor.brandColor.BLUE_DE_FRANCE,
                            contentColor = AppColor.neutralColor.DOCTOR
                        ),
                        onClick = {
                            navigatorViewModel.apply {
//                                popBackStack()
                                navigate(HomeDestination.route())
                                **viewModelScope.launch {
                                    dataStore.set(
                                        [email protected]
                                    )
                                    
                                }**
                            }
                        }) {
                        Text(
                            text = stringResource(R.string.confirm_address),
                            style = AppFont.PoppinsTypography.button
                        )
                    }
                    Spacer(modifier = Modifier.size(16.dp))
                }
            }
        }
    }
}

SplashScreen codes:

@OptIn(ExperimentalPermissionsApi::class)
@Composable
private fun SplashView(
    modifier: Modifier,
    multiplePermissionsState: MultiplePermissionsState,
    navigator: SplashViewModel = hiltViewModel()
) {
    **val context = LocalContext.current
    val dataStore = CheckLocationIsSetDataStore(context = context)**
    Box(
        modifier = modifier
            .fillMaxSize()
            .background(color = AppColor.brandColor.BLUE_DE_FRANCE)
            .padding(start = 64.dp, end = 64.dp, bottom = 16.dp)
    ) {
        Column(modifier = modifier.align(Alignment.Center)) {
            Image(
                painter = painterResource(id = R.drawable.mykuyatm),
                contentDescription = "mykuya tm image"
            )
            Image(
                painter = painterResource(id = R.drawable.mykuya_powered_by),
                contentDescription = "mykuya powered by image"
            )
        }
        Loading(modifier = modifier.align(Alignment.BottomCenter))
        FeatureThatRequiresPermission(
            multiplePermissionsState = multiplePermissionsState, permissionsState = {
                if (it) {
                    navigator.apply {
                        viewModelScope.launch {
                            delay(Constants.SPLASH_DELAY)
                            **dataStore.get.collect { model ->
                                model.let {
                                   /* if (model.lat == Constants.IF_LOCATION_LAT_NOT_SET && model.lat == Constants.IF_LOCATION_LNG_NOT_SET) {
                                        navigate(PickLocationDestination.route())
                                    }else{
                                        navigate(HomeDestination.route())
                                    }*/
                                    navigate(PickLocationDestination.route())
                                }
                            }**

//                            popBackStack()

                        }
                    }
                }
            })
    }
}

DataStore class codes: (How can i use instance of this class in the all of modules)

class CheckLocationIsSetDataStore @Inject constructor(private val context: Context) :
    IDataStore<Location, Location> {
    override val get: Flow<Location>
        get() = context.dataStore.data.catch { exception ->
            if (exception is IOException) {
                emit(emptyPreferences())
            } else {
                throw exception
            }
            Log.e("DataStore Exception: ", exception.toString())
        }.map { preferences ->
            Location("").let {
                it.latitude = preferences[DataStoreKeys.IS_LOCATION_LAT_SET_KEY]
                    ?: Constants.IF_LOCATION_LAT_NOT_SET
                it.longitude = preferences[DataStoreKeys.IS_LOCATION_LNG_SET_KEY]
                    ?: Constants.IF_LOCATION_LNG_NOT_SET
                it
            }
        }

    override suspend fun set(param: Location?) {
        context.dataStore.edit { preferences ->
               preferences[DataStoreKeys.IS_LOCATION_LAT_SET_KEY] =
                   param?.latitude ?: Constants.IF_LOCATION_LAT_NOT_SET
               preferences[DataStoreKeys.IS_LOCATION_LNG_SET_KEY] =
                   param?.longitude ?: Constants.IF_LOCATION_LNG_NOT_SET
           }
    }
}

Solution

  • Hilt can inject dependencies in view models, so you need to create such a model.

    Here is a basic example:

    class CheckLocationIsSetDataStore @Inject constructor(
        @ApplicationContext val context: Context
    ) {
        fun dataStore() = context.dataStore
        
        private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "composePreferences")
    }
    
    @HiltViewModel
    class DataStoreProviderViewModel @Inject constructor(
        private val checkLocationIsSetDataStore: CheckLocationIsSetDataStore,
    ): ViewModel() {
        private val key = booleanPreferencesKey("some_test_key")
    
        val get get() = checkLocationIsSetDataStore.dataStore().data.catch { exception ->
            if (exception is IOException) {
                emit(emptyPreferences())
            } else {
                throw exception
            }
            Log.e("DataStore Exception: ", exception.toString())
        }.map { preferences ->
            preferences[key] ?: false
        }
    
        fun set(value: Boolean) {
            viewModelScope.launch {
                checkLocationIsSetDataStore.dataStore().edit {
                    it[key] = value
                }
            }
        }
    }
    
    @Composable
    fun TestScreen(
    ) {
        val viewModel = hiltViewModel<DataStoreProviderViewModel>()
        val some by viewModel.get.collectAsState(initial = false)
        Switch(checked = some, onCheckedChange = { viewModel.set(it) })
    }