JoeM
JoeM

Reputation: 81

What is the proper way to provide Context to a DataSource with Clean Architecture, MVVM and Koin?

I am developing an Android application with Kotlin in which I need to get the current location of the mobile device. I've already found a way to do it in various examples, but I don't know how to integrate this logic according to Clean Architecture with MVVM.

In my architecture I have the following layers: Presentation, UseCase, Data, Domain and Framework. I have the presentation layer organized with the MVVM pattern. Also I use Koin for dependency injection.

I get all the data that my application needs from the DataSources that are in the Framework layer. For example, data obtained remotely or from a database, or data provided by the device (location).

Here is an example of the files involved in obtaining the location from the ViewModel:

ConfigurationViewModel (Presentation layer):

class ConfigurationViewModel(private val useCase: GetLocationUseCase) : ViewModel() {

    fun onSearchLocationButtonClicked() = liveData<Resource<Location>>(Dispatchers.IO) {
        emit(Resource.loading())
        try {
            emit(Resource.success(data = useCase.invoke(UseCase.None())))
        } catch (exception: Exception) {
            emit(Resource.error(message = exception.message))
        }
    }

GetLocationUseCase (Usecase layer):

class GetLocationUseCase(private val locationRepository: LocationRepository) :
    UseCase<Location, UseCase.None>() {

    override suspend fun invoke(params: None): Location = locationRepository.getLocation()
}

LocationRepositoryImpl (Data layer):

class LocationRepositoryImpl(private val locationDeviceDataSource: LocationDeviceDataSource) :
    LocationRepository {
    override suspend fun getLocation(): Location = locationDeviceDataSource.getLocation()
}

LocationDeviceDataSourceImpl (Framework layer):

class LocationDeviceDataSourceImpl(private val context: Context) : LocationDeviceDataSource {

    override suspend fun getLocation(): Location =
        LocationServices.getFusedLocationProviderClient(context).lastLocation.await()
}

As you can see, in LocationDeviceDataSourceImpl I need the context to get the last location. I don't know what is the best way to provide the context to the DataSource. I have seen several examples but I want to understand what is the best way to do it.

I have seen the following options:

What would be the appropriate way to integrate this logic according to my architecture and why? Or is this problem because of my architecture?

Thanks a lot for your time.

Upvotes: 8

Views: 3069

Answers (1)

Pavlo Ostasha
Pavlo Ostasha

Reputation: 16729

The correct way is not to pass context there at all. LocationDeviceDataSourceImpl should not know about context at all. It should know about the FusedLocationProviderClient though - so pass the location client already prepared to the LocationDeviceDataSourceImpl.

And as FusedLocationProvider should know about Context since it cannot exist without it - have it declared and initialized somewhere where you declare(or inject) all the context-dependent stuff. The rule of thumb here is to use context only where it is required for the object's existence.

I usually have a module that contains context and provides it to all the Android System services that require it, and I provide those objects to all the other modules where they are needed, thus excluding context passing whatsoever.

Upvotes: 1

Related Questions