-
Notifications
You must be signed in to change notification settings - Fork 0
Application ShoppingApp
Devrath edited this page Jul 11, 2021
·
21 revisions
- This is very useful in case of handling the live data.
- We make the network request and get either the data in the success state of the resource object we defined earlier of the error state.
- Problem with this approach is that, say there is an error state and we use snack bar to display the error and now we rotate the device and during this, the snack bar is displayed again.
- Now Using this generic event class we can make sure the event is emitted only once.
code snippet
private val _images = MutableLiveData<Event<Resource<ImageResponse>>>()
val images: LiveData<Event<Resource<ImageResponse>>> = _imagesEvent.kt
open class Event<out T>(private val content: T) {
var hasBeenHandled = false
private set // Allow external read but not write
/**
* Returns the content and prevents its use again.
*/
fun getContentIfNotHandled(): T? {
return if (hasBeenHandled) {
null
} else {
hasBeenHandled = true
content
}
}
/**
* Returns the content, even if it's already been handled.
*/
fun peekContent(): T = content
}- When we are creating an observable source like
live dataorflowwe need to give the ability to modify the live data by the components inside the ViewModel. Then expose a variable for outside so it can be observed. - Thus mutable state is for the view model since only the view model should modify it, thus it's private.
- We just expose the Live data since it just can be observed and not be modified and has no mutable state, thus it's public.
Declare in viewmodel
private val _images = MutableLiveData<Event<Resource<ImageResponse>>>()
val images: LiveData<Event<Resource<ImageResponse>>> = _imagesSet the data in viewmodel
images.value = // set the stateObserve the live data from outside the view model, like activity or fragment
viewModel.images.observe(viewLifecycleOwner, Observer {- We create a fake repository when we can
swapbetween areal repositoryand afake repositorydepending on our usage. - We use a
real repositorywhen we are running our production code. - We use a
fake repositorywhen we are testing our test cases. - We inject the interface of the
repositoryin theview modelconstructor. - Now we need to create classes of the repositories (
real repository,fake repository) that implements the interface which we inject into theview model. - We. create the instance of the class that implements the interface using the hilt provides a method using a dagger
ShoppingRepository.kt -> This is useful in swapping the implementation for both the production and test cases of repository implementation
interface ShoppingRepository {
suspend fun insertShoppingItem(shoppingItem: ShoppingItem)
suspend fun deleteShoppingItem(shoppingItem: ShoppingItem)
fun observeAllShoppingItems(): LiveData<List<ShoppingItem>>
fun observeTotalPrice(): LiveData<Float>
suspend fun searchForImage(imageQuery: String): Resource<ImageResponse>
}DefaultShoppingRepository.kt -> This can be used in production code
class DefaultShoppingRepository @Inject constructor(
private val shoppingDao: ShoppingDao,
private val pixabayAPI: PixabayAPI
) : ShoppingRepository {
override suspend fun insertShoppingItem(shoppingItem: ShoppingItem) {}
override suspend fun deleteShoppingItem(shoppingItem: ShoppingItem) {}
override fun observeAllShoppingItems(): LiveData<List<ShoppingItem>> {}
override fun observeTotalPrice(): LiveData<Float> {}
override suspend fun searchForImage(imageQuery: String): Resource<ImageResponse> {}
}FakeShoppingRepository.kt -> This can be used in testing unit tests
class FakeShoppingRepository @Inject constructor(
private val shoppingDao: ShoppingDao,
private val pixabayAPI: PixabayAPI
) : ShoppingRepository {
override suspend fun insertShoppingItem(shoppingItem: ShoppingItem) {}
override suspend fun deleteShoppingItem(shoppingItem: ShoppingItem) {}
override fun observeAllShoppingItems(): LiveData<List<ShoppingItem>> {}
override fun observeTotalPrice(): LiveData<Float> {}
override suspend fun searchForImage(imageQuery: String): Resource<ImageResponse> {}
}AppModule.kt -> Here we will cast the interface to the repository making it return the repository, by doing so we can inject the interface and use the overridden methods used in repository
@Module
@InstallIn(ApplicationComponent::class)
object AppModule {
@Singleton
@Provides
fun provideDefaultShoppingRepository(
dao: ShoppingDao,
api: PixabayAPI
) = DefaultShoppingRepository(dao, api) as ShoppingRepository
}