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 ?
Related
I am using Stripe library which provides me with custom callback functionality.
I want a custom callback convert to Kotlin coroutine
Here is the code
override fun retrievePaymentIntent(clientSecret: String): Flow<Resource<PaymentIntent>> = flow{
emit(Resource.Loading())
Terminal.getInstance().retrievePaymentIntent(clientSecret,
object : PaymentIntentCallback {
override fun onFailure(e: TerminalException) {}
override fun onSuccess(paymentIntent: PaymentIntent) {
emit(Resource.Success(paymentIntent))
}
})
}
The problem is I can't call emit function inside onSuccess/onFailure. The error shown in the picture.
Is it possible to change something here to make it work or how could I convert custom callback to coroutine?
You can use suspendCancellableCoroutine to model your callback-based one-shot request like so:
suspend fun retrievePaymentIntent(clientSecret: String): PaymentIntent =
suspendCancellableCoroutine { continuation ->
Terminal.getInstance().retrievePaymentIntent(clientSecret,
object : PaymentIntentCallback {
override fun onFailure(e: TerminalException)
{
continuation.resumeWithException(e)
}
override fun onSuccess(paymentIntent: PaymentIntent)
{
continuation.resume(paymentIntent)
}
})
continuation.invokeOnCancellation { /*cancel the payment intent retrieval if possible*/ }
}
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 have an Interface that I use as the common data source for my RecyclerView Adapters, which looks like this:
interface GenericRVAdapterDataSource {
fun getCellCount() : Int
fun getViewModelForCell(position : Int) : CellViewModel
}
Now, I have two other Interfaces that extend this one:
interface GroupHomeDataSource : GenericRVAdapterDataSource {
fun getJoinedGroupsCount() : Int
fun getJoinedGroupViewModel(forIndex : Int) : GroupHomeCellViewModel
override fun getCellCount(): Int = getJoinedGroupsCount()
override fun getViewModelForCell(position: Int): CellViewModel = getJoinedGroupViewModel(position)
}
and:
interface GroupSuggestedDataSource : GenericRVAdapterDataSource {
fun getSuggestedGroupsCellCount() : Int
fun getSuggestedGroupViewModelForCell(atIndex : Int) : GroupHomeCellViewModel
override fun getCellCount(): Int = getSuggestedGroupsCellCount()
override fun getViewModelForCell(position: Int): CellViewModel = getSuggestedGroupViewModelForCell(position)
}
However, when I implement both interfaces into the class:
class GroupHomeViewModel(app : Application) : AndroidViewModel(app), GroupHomeDataSource, GroupSuggestedDataSource, GroupsHomeInteractionLogic {...}
I got the error:
Class 'GroupHomeViewModel' must override public open fun getCellCount(): Int defined in GroupHomeDataSource because it inherits multiple interface methods of it
For now, I've avoided the problem by just storing both interfaces as variables:
val joinedGroupsDataSource = object: GroupHomeDataSource {
override fun getJoinedGroupsCount(): Int = joinedGroupsList.size
override fun getJoinedGroupViewModel(forIndex: Int): GroupHomeCellViewModel = joinedGroupsList[forIndex]
}
val suggestedGroupsDataSource = object: GroupSuggestedDataSource {
override fun getSuggestedGroupsCellCount(): Int = suggestedGroupsList.size
override fun getSuggestedGroupViewModelForCell(atIndex: Int): GroupHomeCellViewModel = suggestedGroupsList[atIndex]
}
However, I'm not sure that's the most effective way to resolve this diamond problem - if I can even call it that.
Do I just do what the compiler tells me to do and implement getCellCount() and redirect it to one of the interfaces' implementations using:
//MARK:- super interface implementation
override fun getCellCount(): Int {
return super<GroupHomeDataSource>.getCellCount()
//Or: return super<GroupSuggestedDataSource>.getCellCount()
}
override fun getViewModelForCell(position: Int): CellViewModel {
return super<GroupHomeDataSource>.getViewModelForCell(position)
//Or: return super<GroupSuggestedDataSource>.getViewModelForCell(position)
}
//ENDMARK
Or do I implement that method while determining which of the interfaces calls for it (is there a method for this)?
The compiler cannot choose between multiple implementations on its own. But, the whole implementation looks a little overwhelmed. Usually you shouldn't create extended DataSource for each case, use a Generic interface instead. If GroupHomeViewModel provides multiple data sources, just create different properties, as you did.
interface CellViewModel
interface GroupHomeCellViewModel : CellViewModel
interface RVAdapterDataSource<T : CellViewModel> {
fun getCellCount() : Int
fun getViewModelForCell(position : Int) : T
}
class ListAdapterDataSource<T : CellViewModel>(
private val list: List<T>
) : RVAdapterDataSource<T> {
override fun getCellCount() = list.size
override fun getViewModelForCell(forIndex: Int) = list[forIndex]
}
class GroupHomeViewModel(
joinedGroupList: List<GroupHomeCellViewModel>,
suggestedGroupList: List<GroupHomeCellViewModel>
) {
val joinedGroupsDataSource = ListAdapterDataSource(joinedGroupList)
val suggestedGroupsDataSource = ListAdapterDataSource(suggestedGroupList)
}
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 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.