All Projects → smilecs → Ketro

smilecs / Ketro

Licence: mit
Simple and sane Retrofit request library for Kotlin helps wrap responses and provides easy error handling that can be easily translated to custom exception objects for easy and proper handling. Ketro supports LiveData request and also Coroutines functionality. As well easily propagate errors to the parent fragment/activity or handle within the ViewModel without losing your sanity🔥. Ketro is highly flexible and is a good tool for clean response parsing and management https://smilecs.github.io/ketro/

Programming Languages

kotlin
9241 projects

Projects that are alternatives of or similar to Ketro

Food Ordering App Like Swiggy Uber Eats Mvvm And Room Database
Food ordering app using MVVM architecture patterns, Architecture Lifecycle components and Room database.
Stars: ✭ 53 (-39.77%)
Mutual labels:  livedata
Easychatandroidclient
EasyChat是一个开源的社交类的App。主要包含消息、好友、群组等相关的IM核心功能。部分界面参照了QQ、微信等相关社交APP。EasyChat APP整体采用MVVM模式,基于JetPack(Lifecycle,LiveData,ViewModel,Room)构建
Stars: ✭ 64 (-27.27%)
Mutual labels:  livedata
Ktarmor
👻 Android快速开发框架, KtArmor 寓意着 为Android 赋予战斗装甲, 方便开发者快速进行Android 开发。
Stars: ✭ 76 (-13.64%)
Mutual labels:  livedata
Tdcapp
Sample app which access the TDC (The Developer's Conference) REST API.
Stars: ✭ 55 (-37.5%)
Mutual labels:  livedata
Githubprojectbrowser
This is a sample Android Project that is based on Clean Architecture
Stars: ✭ 64 (-27.27%)
Mutual labels:  livedata
Kotlin Pokedex
🌀 A Pokedex app using ViewModel, LiveData, Room and Navigation
Stars: ✭ 1,156 (+1213.64%)
Mutual labels:  livedata
Newssync
Sample application with MVVM pattern using RxJava and Architecture Components
Stars: ✭ 46 (-47.73%)
Mutual labels:  livedata
Live
A RxJava Transformer handle Android Lifecycle as same as LiveData.
Stars: ✭ 81 (-7.95%)
Mutual labels:  livedata
Moviefinderusingmvvm Android
🔥 MVVM + Clean Architecture + Best Practices | 🍿Movie Finder is a sample Android application 📱to search movies using OMDb API which is built to demonstrate use of Modern Android development tools - (Kotlin, Coroutines, Kodein, Architecture Components, MVVM, Retrofit, Gson, Material Components) 😊😊😉
Stars: ✭ 66 (-25%)
Mutual labels:  livedata
Elements
⚒ Modular components for RecyclerView development enforcing clean, reusable and testable code, with built-in support for paging and complex hierarchies of data.
Stars: ✭ 75 (-14.77%)
Mutual labels:  livedata
Jetpackmvvm
🐔🏀一个Jetpack结合MVVM的快速开发框架,基于MVVM模式集成谷歌官方推荐的JetPack组件库:LiveData、ViewModel、Lifecycle、Navigation组件 使用Kotlin语言,添加大量拓展函数,简化代码 加入Retrofit网络请求,协程,帮你简化各种操作,让你快速开发项目
Stars: ✭ 1,100 (+1150%)
Mutual labels:  livedata
Android Architecture Components Mvvm Retrofit Java
This repository contains Android Architecture Components ( LiveData , View Model and MVVM pattern with retrofit for consuming rest api )
Stars: ✭ 63 (-28.41%)
Mutual labels:  livedata
Android Mvvm Rx3 Dagger2 Navcomponent
Implemented using MVVM, LiveData, Room, RX3, Dagger2, Coil, View Binding, Navigation Component and AndroidX
Stars: ✭ 72 (-18.18%)
Mutual labels:  livedata
Retrokotlin
Simple Android app to show how unit testing with MockWebServer and Architecture Components (ViewModel + LiveData)
Stars: ✭ 55 (-37.5%)
Mutual labels:  livedata
Price Tracker
Price Tracking Application - An experimental Kotlin Android project with complex android app requirements.
Stars: ✭ 80 (-9.09%)
Mutual labels:  livedata
Gsygithubappkotlin
超完整的Android Kotlin 项目,功能丰富,适合学习和日常使用。GSYGithubApp系列的优势:目前已经拥有Flutter、Weex、ReactNative、Kotlin四个版本。 功能齐全,项目框架内技术涉及面广,完成度高。开源Github客户端App,更好的体验,更丰富的功能,旨在更好的日常管理和维护个人Github,提供更好更方便的驾车体验Σ( ̄。 ̄ノ)ノ。同款Weex版本: https://github.com/CarGuo/GSYGithubAppWeex 、同款React Native版本 : https://github.com/CarGuo/GSYGithubApp 、 同款Flutter版本: https://github.com/CarGuo/GSYGithubAppFlutter
Stars: ✭ 1,066 (+1111.36%)
Mutual labels:  livedata
Aacomponents
基于google Android Architecture Components 封装实现组件式MVP快速开发框架
Stars: ✭ 66 (-25%)
Mutual labels:  livedata
Sample Code Movies
This repository contains sample code. Its purpose being, to quickly demonstrate Android and software development in general, clean code, best practices, testing and all those other must know goodies.
Stars: ✭ 81 (-7.95%)
Mutual labels:  livedata
Locationlivedata
A simple LiveData implementation of Android Location API
Stars: ✭ 81 (-7.95%)
Mutual labels:  livedata
Dagger2 Sample
A sample app to demo how to implement dagger in Android using Dagger Android Support library
Stars: ✭ 72 (-18.18%)
Mutual labels:  livedata

ketro

Alt text

Ketro is a Retrofit response wrapper written in Kotlin that can be used to easily wrap REST API response to LiveData and exception/error handling both from the retrofit calls all the way to displaying an error in the view. Ketro allows and encourages the addition of custom exceptions so errors can easily be grouped and managed with adequate actions and feedback to your app users.

Include Dependency

Currently Ketro is hosted on Jcenter, just add the below line to your app gradle file

implementation 'past3.smilecs.ketro:ketro:1.2.4'

Multi - module projects:

Ketro now supports multi-module projects, the Ketro modules such as Wrapper and ApiErrorHandler have been put into a separate package to allow you expose these in a domain layer without including the Ketro dependency so as to enable separation of concerns between the data, presentation and domain layer.

implementation 'past3.smilecs.ketro:ketro:1.3.3'
implementation 'past3.smilecs.kcore:kcore:1.3.6'

or

api 'past3.smilecs.ketro:ketro:1.3.3'
api 'past3.smilecs.kcore:kcore:1.3.6'

Add the sample below to your top level build.gradle file when including the kcore dependency

allprojects {
    repositories {
        jcenter()
        maven {
            url "https://dl.bintray.com/smilecs/ketro"
        }
    }
}

Note:

Kcore Houses the ketro models, it's seperation is just so you don't need to include, the ketro dependency in your domain module, if you choose to keep your domain layer as a clean kotlin project with no android dependency else, you can just use the ketro 1.3.x dependency in all your modules.

Please check sample if any confusion on how these layers interact and how the dependency between kcore and ketro work . As a side, the actual handling logic is in ketro and kcore is a dependency in ketro.

  • Note: The Kcore models are still accessible via the Ketro project, this other implementation is just for those who want a simpler way to separate their modules without having to include Ketro in every part of their project were only the models are needed.

Ketro Request methods

Ketro offers a selection of methods that wrap your retrofit calls and return a LiveData object with a wrapper that contains an exception object if the request was unsuccessful or as the user defines. These methods are:

  • doRequest() : LiveData<Wrapper<R>>
  • executeRequest(liveData:MutableLiveData<Wrapper<R>>)
  • suspend fun doRequest(): Wrapper<T>
  • suspend fun execute(): KResponse<T>

Usage

Inorder to use these wrappers for your request, you must extend the ketro GenericRequestHandler<T> which takes in a class type which you would like to observe with livedata. Ketro offers two request patterns:

  1. GenericRequestHandler<T>
  2. Request<T>

1. GenericRequestHandler API

class LobbyRequest(private val mainType: String = "") : GenericRequestHandler<VehicleContainer>() {
    private val lobbyService: LobbyService by lazy {
        NetModule.provideRetrofit().create(LobbyService::class.java)
    }

    override fun makeRequest(): Call<VehicleContainer> {
        //Retrofit interface method
        return lobbyService.getManufacturers()
        }
    }
  • Note put in your retrofit request call into the makeRequest() method.
After creating your request handler as above,

To make the actual api call, create an object of the request class and call the doRequest().

fun getManufacturer() {
        LobbyRequest(LobbyRequest.MANUFACTURER).doRequest().observe(this, object : Kobserver<VehicleContainer>() {
            override fun onException(exception: Exception) {
                //handle exceptions here, custom exception types inclusive
            }

            override fun onSuccess(data: VehicleContainer) {

            }
        })
    }
viewModel._liveData.observe(this, object : Kobserver<ResponseModel>() {
            override fun onSuccess(data: ResponseModel) {
                Toast.makeText(this@MainActivity, "Works", Toast.LENGTH_LONG).show()
            }

            override fun onException(exception: Exception) {
                userErrorHanlder(exception)
            }
        })

 private fun userErrorHanlder(ex: Exception) {
        when (ex) {
            is GitHubErrorHandler.ErrorConfig.NetworkException -> {
                Toast.makeText(this@MainActivity, ex.message, Toast.LENGTH_LONG).show()
            }
            is GitHubErrorHandler.ErrorConfig.GitHubException -> {
                Toast.makeText(this@MainActivity, ex.message, Toast.LENGTH_LONG).show()
            }
            else -> Toast.makeText(this@MainActivity, "Oops! Something went wrong.", Toast.LENGTH_LONG).show()
        }
    }

As noted above the Request class doRequest() executes the api call and depending on usage it could either return an observable live data object or a data wrapper of type Wrapper<T> were T represents your object, within this wrapper class you have access to your data or exceptions as well as Status code. Now Ketro offers an extension of the Android Observer class(Kobserver<T>), which attempts to handle api errors/exceptions delegated by the user, hence why we have an exception and a success callback.

  • Note Using the Kobserver with the returned api response is optional, but recommended for proper error handling.

There are situations where you may want to have a separate request method and a separate LiveData object update when the request resolves. In such scenarios, instead of calling doRequest(), we would call executeRequest(liveData: MutableLiveData<Wrapper<R>>) or use the Coroutines helper as described in the next section. This method needs the specified response type to be wrapped with the Wrapper class in Ketro so it can propagate errors effectively. Internally all the methods wrap each object with the Ketro Wrapper.

val wrap = MutableLiveData<Wrapper<VehicleContainer>>()
fun getManufacturer() {
     LobbyRequest(LobbyRequest.MANUFACTURER).executeRequest(responseLiveData)
}

After the request is resolved, the LiveData object passed in will have its value set with the response and all active observers of the LiveData are triggered.

2. Request API Implementation with Coroutines helper

This sections shows examples on how to use the Request API, the samples provided will come in pairs, one would be for responses using the execute -> KResponse<T> and the other would be for doRequest -> Wrapper<T>.

Note: The request api uses Coroutine Suspend functions.

Create your class holding your network requests or you can use one class per request, the doRequest():Wrapper<T> suspention method from the Request class is used to make the network call:

class CoRoutineSampleRequest {
    private val gitHubAPI: GitHubAPI by lazy {
        NetworkModule.createRetrofit().create(GitHubAPI::class.java)
    }

     suspend fun requestGithubUser(user: String): Wrapper<ResponseItems> {
            val req = object : Request<ResponseItems>(GitHubErrorHandler()) {
                override suspend fun apiRequest(): Response<ResponseItems> =
                        gitHubAPI.searchUse(user)
            }
            return req.doRequest()
        }

}

Example using Request API

class GetUserDataSourceRemote @Inject constructor(private val gitHubAPI: GitHubAPI) {

    suspend fun requestGithubUser(user: String): KResponse<ResponseItems> {
        val req = object : Request<ResponseItems>(GitHubErrorHandler()) {
            override suspend fun apiRequest(): Response<ResponseItems> =
                    gitHubAPI.searchUse(user)
        }
        return req.execute()
    }

}

Override the errorHandler class for adding extra/custom ErrorHandling exceptions details in next section Error Handling.

Note:

The doRequest method returns a Wrapper with the response encapsulated within it and returns the error/error code and custom exceptions as you may have defined.

private val viewModelJob = SupervisorJob()

//Scope coroutine to a ViewModel or use global scope
private val scope = CoroutineScope(Dispatchers.Default + viewModelJob)

fun getGitHubUser() {
     scope.launch {
         val user = getUserUseCase(name)
          withContext(Dispatchers.Main) {
          liveData.value = user
           }
        }
    }
private val _errorLiveData: MutableLiveData<Exception> = MutableLiveData()
    val errorLiveData: LiveData<Exception> = _errorLiveData

    private val liveDataHandler = LiveDataHandler(_errorLiveData)

    private val liveData = MutableLiveData<Items>()

    val _liveData: LiveData<Items> = liveData

    private val viewModelJob = SupervisorJob()

    private val scope = CoroutineScope(Dispatchers.Default
            + viewModelJob)

    fun searchUser(name: String) {
        scope.launch(handler()) {
            val user = getUserUseCase(name)
            withContext(Dispatchers.Main) {
                liveDataHandler.emit(user, liveData)
            }
        }
    }

Above is an example of handling data response of type KResponse in the ViewModel and emitting either a failure or the success T The LiveDataHandler is a function within ketro that parses the KResponse return object and can emit either the success or failure object.

private val _errorLiveData: MutableLiveData<Exception> = MutableLiveData()
    val errorLiveData: LiveData<Exception> = _errorLiveData

    private val liveDataHandler = LiveDataHandler(_errorLiveData)

To use the LiveDataHandler initialise it with a LiveData that takes in an exception i.e LiveData<Exception> this is because the KResponse wraps errors in exceptions that can be predefined by the user Using the ApiErrorHandler which allows you to OverRide and add your own error definitions. Then on the view you can collect your LiveData values as normal because if success the LiveDataHandler will emit the success value to the specific LiveData.

   viewModel._liveData.observe(this, Observer {
            toggleViews(true)
            Toast.makeText(this@MainActivity, "Works", Toast.LENGTH_LONG).show()
        })

        viewModel.errorLiveData.observe(this, Observer { ex ->
            ex?.let {
                userErrorHanlder(it)
            }
        })

Error Handling

Handling custom errors with Ketro is quite simple, the library expects you use either the response code gotten from your server or a custom message gotten from your server and map an Exception to which would be return to the request class by overriding the error handler object to return your class with your error mapping implementation. Note if this is not provided, a default exception is returned and propaged to the views callback interface. First off you need to create a class which extends ApiErrorHandler then you can either put your own Exception cases there or create a new class for each exception case depends on your preference. Ketro now provides a Kexception class which return the Respoonse Error Body, though the getExceptionType returns the exception super type, so as to make using the Kexception class optional.

import com.past3.ketro.api.ApiErrorHandler
import retrofit2.Response

class LobbyErrorHandler : ApiErrorHandler() {

    override fun getExceptionType(response: Response<*>): Exception {
        return when (response.code()) {
            LOGIN_ERROR -> LoginException(response.errorBody(), response.message())
            UPDATE_ERROR -> UpdateException()
            else -> Exception()
        }
    }

    companion object ErrorConfig {
        const val LOGIN_ERROR = 401
        const val UPDATE_ERROR = 404
        //sub-classing kexception allows you to have access to the errorbody
        class LoginException(val errorBody: ResponseBody?, message: String?, cause: Throwable? = null)
         : Kexception(message, cause) {
            override val message = "Error processing login details"
        }

        class UpdateException : Exception() {
            override val message = "Error updating details"
        }
    }

}

Now you can choose to map your errors anyway you like to an exception, for me I prefer to use http error status codes to determine what kind of exception I return to the Wrapper object you can as well choose to return an error object from your server and map that out to your exception, the possibilities are endless.

Also, remember the request class you created earlier? you will need to override the ApiErrorhandler field and initialise your custom class, the rest will be handled by Ketro.

class LobbyRequest(private val page: Int) : GenericRequestHandler<VehicleContainer>() {

    private val lobbyService: LobbyService by lazy {
        NetModule.provideRetrofit().create(LobbyService::class.java)
    }

    override val errorHandler: ApiErrorHandler = LobbyErrorHandler()

    override fun makeRequest(): Call<VehicleContainer> {
        return lobbyService.getManufacturers(page, pageSize, Urls.KEY)
    }
}

After creating your class and modifying your request handler you can go ahead to check for the exception in your View(Activity/Fragment) Here you can Also check if the exception is of type Kexception and use the errorBody included within the object. Note: the onException override is now optional, and as well you can pass in a function into the kobserver constructor to handle your errors and as well get a cleaner interface

viewModel.responseData().observe(this, object : Kobserver<List<VehicleContainer>>() {
            override fun onException(exception: Exception) {
             when (exception) {
                is Kexception -> {
                    //exception.errorBody do something with errorBody
                 }
                 is LobbyErrorHandler.ErrorConfig.UpdateException ->{
                    // handle error e.g. show dialog, redirect user etc.
                 }
             }
            }

            override fun onSuccess(data: List<VehicleContainer>) {
                // Update UI
                swipeRefresh.isRefreshing = false
                searchView.visibility = View.VISIBLE
                helperContainer.visibility = View.VISIBLE

                viewModel.genericList.addAll(data)
                vehicleAdapter.notifyDataSetChanged()
                searchView.setAdapter(searchAdapter)
                searchAdapter.notifyDataSetChanged()

            }
        })

Alternatively

Passing in an error handling function will allow you to omit the onException callback which is now optional when using the Kobserver.

Note: that passing in a function will stop the onException callback from executing

so it's a choice between using either one.

private fun userErrorHanlder(ex: Exception) {
        //handle errors here
}

 viewModel.searchUser(editText.text.toString()).observe(this, object : Kobserver<ResponseModel>(::userErrorHanlder) {
            override fun onSuccess(data: ResponseModel) {
                if (data.items.isEmpty()) {
                    toggleViews(false)
                    return
                }
                toggleViews(true)
                viewModel.list.let {
                    it.clear()
                    it.addAll(data.items)
                    listAdapter.submitList(it)
                }
            }

        })

Also for any request or anything unclear with the library feel free to hit me up, on [email protected] or create an issue ticket.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].