I want my NewsListSubscriber to inherit from an RxJava Subscriber which use a generic type but I get a "Type mismatch" error when I call the UseCase execute method. I read many times the generics page from the Kotlin documentation but I can't find the solution.
Here is my UseCase:
abstract class UseCase(private val threadExecutor: IThreadExecutor,
private val postExecutionThread: IPostExecutionThread) {
private var subscription = Subscriptions.empty()
fun execute(UseCaseSubscriber: rx.Subscriber<Any>) {
subscription = buildUseCaseObservable()
.subscribeOn(Schedulers.from(threadExecutor))
.observeOn(postExecutionThread.getScheduler())
.subscribe(UseCaseSubscriber)
}
protected abstract fun buildUseCaseObservable(): Observable<out Any>
fun unsubscribe() {
if (!subscription.isUnsubscribed) {
subscription.unsubscribe()
}
}
}
And here is how I call it:
override fun loadNewsList() {
getNewsListInteractor.execute(NewsListSubscriber())
}
private inner class NewsListSubscriber : rx.Subscriber<List<NewsModel>>() {
override fun onCompleted() {// TODO}
override fun onError(e: Throwable) {// TODO}
override fun onNext(t: List<NewsModel>) {// TODO}
}
The error is
"Type mismatch. Required: rx.Subscriber. Found: Presenters.NewsListPresenter.NewsListSubscriber"
in the "execute(NewsListSubscriber())" line. I tried playing with the "in" and "out" keywords but I still have the same error.
There is actually a better way to solve this problem. I ran into the same issue and a type cast inside every derived subscriber class was not an option.
Just update the abstract UseCase class with an generic type parameter.
abstract class UseCase<T>(private val threadExecutor: IThreadExecutor,
private val postExecutionThread: IPostExecutionThread) {
private var subscription = Subscriptions.empty()
fun execute(UseCaseSubscriber: rx.Subscriber<T>) {
subscription = buildUseCaseObservable()
.subscribeOn(Schedulers.from(threadExecutor))
.observeOn(postExecutionThread.getScheduler())
.subscribe(UseCaseSubscriber)
}
protected abstract fun buildUseCaseObservable(): Observable<T>
fun unsubscribe() {
if (!subscription.isUnsubscribed) {
subscription.unsubscribe()
}
}
}
When you declare your derived UseCase classes, use your concrete type for the generic parameter when calling the super class.
class ConcreteUseCase(val threadExecutor: IThreadExecutor,
val postExecutionThread: IPostExecutionThread)
: UseCase<ConcreteType>(threadExecutor, postExecutionThread)
Doing so, you can use typed Subscribers in your execute call.
getNewsListInteractor.execute(NewsListSubscriber())
...
private inner class NewsListSubscriber : rx.Subscriber<List<NewsModel() {
override fun onCompleted() {// TODO}
override fun onError(e: Throwable) {// TODO}
override fun onNext(t: List<NewsModel>) {// TODO}
}
I found the solution that is pretty simple actually: my NewsListSubscriber class has to extends from rx.Subscriber<Any> instead of rx.Subscriber<MyWantedClass>. It means I need to cast the received objects to the wanted type.
private inner class NewsListSubscriber : DefaultSubscriber<Any>() {
override fun onCompleted() {}
override fun onError(e: Throwable) {}
override fun onNext(t: Any?) {
val newsList = t as List<News>
...
}
}
In Java the cast is done in background but in Kotlin we need to do it ourself.
I also removed all "in" or "out" keywords in my UseCase class.
Related
interface SomeAPIHandler {
fun getUserContent(apiInterface: APIInterface<UserModel>)
}
interface APIInterface<T> {
fun onSuccess(responseModel: T)
fun onError(errorModel: ErrorModel)
}
In my presenter class, it is called like:
apiClient.getUserContent(object : APIInterface<UserModel> {
override fun onSuccess(responseModel: UserModel) = handleSuccess(responseModel)
override fun onError(errorModel: ErrorModel) = handleGetUserModelError()
})
I am getting code coverage issue from SonarQube for this two lines in presentor class:
override fun onSuccess(responseModel: UserModel) = handleSuccess(responseModel)
override fun onError(errorModel: ErrorModel) = handleGetUserModelError()
I am using mockk.io and I think i need to use slot for this. Can someone help how to cover this ?
I have created a Generic Fragment class to handle all type of responses from server. I want to do some sort of DataTableProvider<*> to hanle any type of response.
How could I achieve this.
class TestFragmentActivity : AppCompatActivity(), DataTableProvider<Any> {
protected val mTabPatientsFragment = TabPatientsFragment()
protected val mTabObservationsFragment = TabObservationsFragment()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test_fragment)
replaceFragment()
}
private fun replaceFragment(){
supportFragmentManager.beginTransaction().replace(R.id.frame_container,
mTabPatientsFragment).commit()
}
override fun getDataTableListener(mTableFragment: DataTableFragment<Any>): DataTableListener<Any> {
val dataTableId = mTableFragment.dataTableId
if (dataTableId.equals("observations"))
return mTabObservationsFragment
else return mTabPatientsFragment
}
override fun getDataTableConfig(mTableFragment: DataTableFragment<Any>): DataTableConfig {
val dataTableId = mTableFragment.dataTableId
val config = DataTableConfig()
config.noRecordCell = R.layout.cell_no_record
config.showSearch = false
config.showAddButton = false
if (dataTableId.equals("observations"))
{
config.cellResourceId = R.layout.home_observation_cell
} else config.cellResourceId = R.layout.home_patient_cell
return config
}
}
getDataTableListener callback in above fragment has error type mismatch required DataTableListener found TabObservationFragment
TabObservationFragment
class TabObservationFragment : AppBaseFragment(),DataTableListener<Observation>
TabPatientFragment
class TabPatientFragment : AppBaseFragment(),DataTableListener<Patient>
How could I set it to work for all type of responses.
I tried DataTableListener<*> but could not achieve
The error states
projections are not allowed for immediate arguments of a supertype
How could I use DataTableProvider<*> to work for all type of responses
Edit
I have couple of fragment with fragmentViewpager inside TestFragmentActivity .
I have got a structure that helps to implement pagination ,search and implement everything in a fragment. But according to that structure DataTableProvider must be handle in activity and basis of tableId I updated callback of getDataTableListener and getDataTableListener
The above callback should return some type of
Is there a way to achieve callback like below
override fun getDataTableConfig(mTableFragment: DataTableFragment<*>?): DataTableConfig?
override fun getDataTableListener(mTableFragment: DataTableFragment<*>?): DataTableListener<*>?
Quick answer, use "out" modifier:
fun getDataTableListener(mTableFragment: DataTableFragment<Any>): DataTableListener<out Any>
Long answer:
What you are looking for is variance, which can you read about in official kotlin docs:
https://kotlinlang.org/docs/reference/generics.html
Because for example List interface looks like this:
public interface List<out E>
You can do assigement like this:
val list: List<Any> = listOf(1,2,3)
But it is not possible to do:
val mutableList : MutableList<Any> = listOf(1,2,3)
Because MutableList doesn't have "out" modifier. It makes sense, because MutableList can be changed, to MutableList you could add for example String, but it already points to List.
In your example you can use "out" modifier, if, and only if, your DataTableListener doesn't use generic type as input. For example:
interface DataTableListener<T>{
fun assignValue(t:T)
fun getValue():T
}
With interface like above, you still could use "out" modifier in your function, but you won't be able to execute "assignValue" function.
Whole example:
class Patient
class Observation
class DataTableFragment<T>
interface DataTableListener<T> {
fun assignValue(t: T)
fun getValue(): T
}
class TabObservationFragment : DataTableListener<Observation> {
override fun getValue(): Observation {
TODO("Not yet implemented")
}
override fun assignValue(t: Observation) {
TODO("Not yet implemented")
}
}
class TabPatientFragment : DataTableListener<Patient> {
override fun getValue(): Patient {
}
override fun assignValue(t: Patient) {
TODO("Not yet implemented")
}
}
val mTabObservationsFragment = TabObservationFragment()
val mTabPatientsFragment = TabPatientFragment()
fun getDataTableListener(mTableFragment: DataTableFragment<Any>): DataTableListener<out Any> {
val test = "observations"
if (test == "observations")
return mTabObservationsFragment
else return mTabPatientsFragment
}
fun getIt() {
val listener = getDataTableListener(DataTableFragment())
listener.assignValue("test")
}
I am looking for a way of completion block for my kotlin code. In Swift i have my function:
func fetchRegister(with request: RegisterRequest, completion: #escaping (Result<RegisterResponse,DataResponseError>) -> Void) {
//do some stuff
// if i got error i can use completion(Result.failure(DataResponseError.networking))
}
in kotlin my current code is:
fun fetchRegister(withRequest: RegisterRequest, callback: (Result<RegisterResponse,DataResponseError>) -> Unit) {
//do some stuff
//cant use callback.onFailure(DataResponseError.networking)
}
My result interface:
interface Result<T,U: DataResponseError> {
fun onSuccess(data: T)
fun onFailure(Error: U)
}
and my DataResponseError:
enum class DataResponseError(val errorMessage: String) {
httpBody("An error occured while creating httpBody"),
token("An error occured while getting token"),
networking("An error occured while fetching data"),
decoding("An error occured while decoding data")
}
at this moment data cant get out from this function , i cant use callback.onfailure or onSuccess with passing data. How can i fix it?
There are two ways of implementing callbacks in kotlin.
You can have some interface and pass the reference of interface from actvity to the viewModel or the adapter and then you can call specific function from there. Example: Interface:
interface CompletionHandler {
fun onSuccess(data: SomeClass)
fun onFailure(error: String)
}
Class:
class MainActivity : AppCompatActivity() {
private val viewModel: MainViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel.fetchData(this)
}
fun onSuccess(data: SomeClass) {
//onSuccess
}
fun onFailure(error: String) {
//onFailure
}
}
ViewModel / Adapter class:
class MainViewModel(): ViewModel() {
fun fetchData(completion: CompletionHandler) {
//Logic
completion.onSuccess(responseData)
}
}
Just like in IOS(swift) we can also use anonymous functions for callback into activities.Example:Class:
class MainActivity : AppCompatActivity() {
private val viewModel: MainViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel.fetchData(data) { data ->
//Logic
}
}
}
ViewModel / Adapter class:
class MainViewModel(): ViewModel() {
fun fetchData(data: String, completion: (SomeClass) -> Unit) {
//Logic
completion(responseData)
}
}
Replace your fetchRegister function with:
fun fetchRegister(withRequest: RegisterRequest, callback: Result<RegisterResponse,DataResponseError>){
//In case of success
callback.onSuccess(data)
// In case of failure
callback.onFailure(DataResponseError.networking)
}
val callback = object : Result<RegisterResponse,DataResponseError> {
override fun onSuccess(response: RegisterResponse) {
// Do Something
}
override fun onFailure(error: DataResponseError) {
// Do Something
}
}
fetchRegister(request, callback)
I have the following UI flow when searching items from a data source:
Display a progress indicator while retrieving from source -> assign livedata to Outcome.loading(true)
Display results -> assign LiveData Outcome.success(results)
Hide progress indicator in -> assign LiveData Outcome.loading(false)
Now the problem is when #2 and #3 are called while the app is in the background. Resuming the app, the LiveData observers are only notified of #3 and not of #2 resulting to non-populated RecyclerView.
What is the correct way of handling this kind of situation?
class SearchViewModel #Inject constructor(
private val dataSource: MusicInfoRepositoryInterface,
private val scheduler: Scheduler,
private val disposables: CompositeDisposable) : ViewModel() {
private val searchOutcome = MutableLiveData<Outcome<List<MusicInfo>>>()
val searchOutcomLiveData: LiveData<Outcome<List<MusicInfo>>>
get() = searchOutcome
fun search(searchText: String) {
Timber.d(".loadMusicInfos")
if(searchText.isBlank()) {
return
}
dataSource.search(searchText)
.observeOn(scheduler.mainThread())
.startWith(Outcome.loading(true))
.onErrorReturn { throwable -> Outcome.failure(throwable) }
.doOnTerminate { searchOutcome.value = Outcome.loading(false) }
.subscribeWith(object : DisposableSubscriber<Outcome<List<MusicInfo>>>() {
override fun onNext(outcome: Outcome<List<MusicInfo>>?) {
searchOutcome.value = outcome
}
override fun onError(e: Throwable) {
Timber.d(e, ".onError")
}
override fun onComplete() {
Timber.d(".onComplete")
}
}).addTo(disposables)
}
override fun onCleared() {
Timber.d(".onCleared")
super.onCleared()
disposables.clear()
}
}
And below is my Outcome class
sealed class Outcome<T> {
data class Progress<T>(var loading: Boolean) : Outcome<T>()
data class Success<T>(var data: T) : Outcome<T>()
data class Failure<T>(val e: Throwable) : Outcome<T>()
companion object {
fun <T> loading(isLoading: Boolean): Outcome<T> = Progress(isLoading)
fun <T> success(data: T): Outcome<T> = Success(data)
fun <T> failure(e: Throwable): Outcome<T> = Failure(e)
}
}
You should not make your loading state a "double" state (true/false).
Your progress state should be dispatch only when loading, then you go either on success or failure state. Never go back to loading state at the end. Doing so you always know which state your view need to display.
if loading -> show loader
if success -> hide loader, show data
if error -> hide loader, show error
Here is an example extract from my Android Conductor + MVVM + Dagger project template, it uses conductor but you can replace conductor controller with fragment or activity, that's the same logic.
sealed class DataRequestState<T> {
class Start<T> : DataRequestState<T>()
class Success<T>(var data: T) : DataRequestState<T>()
class Error<T>(val error: Throwable) : DataRequestState<T>()
}
ViewModel:
#ControllerScope
class HomeControllerViewModel
#Inject
constructor(homeRepositoryManager: HomeRepositoryManager) : BaseControllerViewModel(),
DataFetchViewModel<Home> {
private val _dataFetchObservable: DataRequestLiveData<Home> =
DataRequestLiveData(homeRepositoryManager.home())
override val dataFetchObservable: LiveData<DataRequestState<Home>> = _dataFetchObservable
override fun refreshData() {
_dataFetchObservable.refresh()
}
}
Base data Controller (fragment/activity/conductor):
abstract class BaseDataFetchController<VM, D> :
BaseViewModelController<VM>() where VM : BaseControllerViewModel, VM : DataFetchViewModel<D> {
override fun onViewCreated(view: View) {
super.onViewCreated(view)
viewModel.dataFetchObservable.observe(this, Observer {
it?.let {
when (it) {
is DataRequestState.Start -> dataFetchStart()
is DataRequestState.Success -> {
dataFetchSuccess(it.data)
dataFetchTerminate()
}
is DataRequestState.Error -> {
dataFetchError(it.error)
dataFetchTerminate()
}
}
}
})
}
protected abstract fun dataFetchStart()
protected abstract fun dataFetchSuccess(data: D)
protected abstract fun dataFetchError(throwable: Throwable)
}
Loading state and loaded data should be strictly separate, and you should maintain two live datas and two observers.
That way, loading == false and you'll receive latest data on re-subscription.
Think about it: loading state isn't really an outcome.
I have this issue ;)
I'm trying to call this usecase, which at the end returns an Observable.
But, despite using schedulers, constantly is being invoked on the main thread. I don't know why:
It looks like this:
class MainViewModel #Inject constructor(private val loadNewsUseCase: LoadNews) : Model {
override fun loadNews() {
loadNewsUseCase.execute(NewsObserver(), "")
}
override fun dispose() {
loadNewsUseCase.dispose()
}
}
class NewsObserver : DisposableObserver<Result>() {
override fun onComplete() {
Log.i("TAG", "")
}
override fun onNext(t: Result) {
Log.i("TAG", "")
}
override fun onError(e: Throwable) {
Log.i("TAG", "")
}
}
-
abstract class UseCase<T, in P>(
private val computationThreadExecutor: ComputationThreadExecutor,
private val mainThreadExecutor: MainThreadExecutor,
private val compositeDisposable: CompositeDisposable = CompositeDisposable()
) {
abstract fun createUseCase(params: P): Observable<T>
fun execute(disposableObserver: DisposableObserver<T>, params: P) {
requireNotNull(disposableObserver)
val observable = createUseCase(params)
.subscribeOn(Schedulers.newThread())
.observeOn(mainThreadExecutor.getThread())
val disposable = observable.subscribeWith(disposableObserver)
addDisposable(disposable)
}
private fun addDisposable(disposable: Disposable) {
requireNotNull(disposable)
compositeDisposable.add(disposable)
}
fun dispose() {
!compositeDisposable.isDisposed.apply { compositeDisposable.dispose() }
}
}
UseCase concrete implementation uses DataService to fetch data from api, which looks like this:
open class NewsDataService(private val newsDataProvider: NewsDataProvider) : NewsService {
override fun loadNews(): Observable<Result> {
return Observable.just(newsDataProvider.fetchData())
}
}
Inside NewsDataProvider is normal sync retrofit call.
Problem is, that from every begininning useCase is invoked in the mainThread(). Shouldn't be called in a new thread?
Replace
Observable.just(foo)
with something like
Observable.fromCallable(() -> foo)
Observable.just() creates an observable from the supplied values and you're computing the value on the main thread. fromCallable() takes in a callback that can be invoked on your subscription thread.
Make fetchData return an Observable<Result> (or potentially Single but that would require wider updates in your code). Retrofit supports RxJava.