Domain-Specific Language (DSL) using Kotlin

The concept of DSL or domain-specific language is pretty simple, it gives a context of what we doing, for example, a common Android Gradle script will have the following structure:

android {
  compileSdkVersion 26
  defaultConfig {
    applicationId "com.example.you"
    minSdkVersion 15
    targetSdkVersion 26
    versionCode 1
    versionName "1.0
  }
}

Inside the Android section, we put android specific configuration and inside defaultConfig we put the general configuration, by this structure we know the context of each section.



The same idea of structure the use of API in that way can be applied also on single class with specific responsibility. These are some examples to learn more about it here for ArtilceBuilder class and here for Person class.

Let’s discuss here another practical example of using Retrofit for calling any API in android with kotlin.

The Normal Way Of Retrofit Call

var call = retrofit.create(MyApiService::class.java).anyTestApi("param1", "param2")
  call.enqueue(object : Callback<String>{
    
        override fun onResponse(call: Call<String>?, response: Response<String>?) {
            // Do anything with response here
        }
        
        override fun onFailure(call: Call<String>?. throwable: Throwable?){
            throwable?.printStackTrace()
            // Do anything here with error
        }
  })

The DSL-Way of Retrofit Call

   var call = retrofit.create(MyApiService::class.java).anyTestApi("param1", "param2")
    call.enqueue( RetrofitCallback {
        
        onSuccessCallback { call, response ->
            // Do anything with response here
        }
        
        onFailureCallback { call, throwable ->
            throwable?.printStackTrace()
            // Do anything here with error
        }
    })

Show Me The Code!!!

Let’s start by creating a class RetrofitCallback which will implement the original Callback<T> from Retrofit library and override the methods as well the like this:

class RetrofitCallback<T> : Callback<T> {
  
  override fun onFailure(call: Call<T>?, t: Throwable?) {
    
  }

  override fun onResponse(call: Call<T>?, response: Response<T>?) {
    
  }
  
}

Now, let’s create our own callback variable references which will be called in DSL way and these are the blocks to be invoked.

class RetrofitCallback<T> : Callback<T> {
  
  private var _failureCallback: (call: Call<T>?, throwable: Throwable?) -> Unit = { _, _ -> }
  private var _successCallback: (call: Call<T>?, response: Response<T>?) -> Unit = { _, _ -> }

  // ... Rest of the class code
}

Finally, assign these variables values (function references or higher order functions) in kotlin like this:

class RetrofitCallback<T> : Callback<T> {
  
  // ... Rest of the class code
  
  fun onFailureCallback( function: (call: Call<T>?, throwable: Throwable?) -> Unit)
  {
      _failureCallback = function
  }

  fun onSuccessCallback( function: (call: Call<T>?, response: Response<T>?) -> Unit)
  {
      _successCallback = function
  }
  
}

Finally, add an init method to start the calls on constructor and call these methods in our original callback methods and final code will be like this:

class RetrofitCallback<T>(initMethod: RetrofitCallback<T>.() -> Unit) : Callback<T> {

    private var _failureCallback: (call: Call<T>?, throwable: Throwable?) -> Unit = { _, _ -> }
    private var _successCallback: (call: Call<T>?, response: Response<T>?) -> Unit = { _, _ -> }

    override fun onFailure(call: Call<T>?, t: Throwable?) {
        onFailureCallback(call, t)
    }

    override fun onResponse(call: Call<T>?, response: Response<T>?) {
        onSuccessCallback(call, response)
    }

    fun onFailureCallback( function: (call: Call<T>?, throwable: Throwable?) -> Unit)
    {
        _failureCallback = function
    }

    fun onSuccessCallback( function: (call: Call<T>?, response: Response<T>?) -> Unit)
    {
        _successCallback = function
    }

    init {
        initMethod()
    }
}

Please note that how initMethod is passed in RetrofitCallback constructor. This is where all the DSL calls get invoked. And voila! We are done. You can customize it more by adding custom methods such as onEmptyCallback, or on404Callback or anything else.


If you liked this article, you can read my new articles below:


Wajahat Karim is Pakistan’s first Google Developer Expert in Android. As an experience Android developer, he deeply cares about it and keeps writing and speaking about it. He has written two worldwide 300+ pages books on Android development with more than 100 articles around the internet either on his website or his Medium publications. He is also a passionate contributor in open source and has created many Android libraries used by thousands of developers in their apps worldwide. As active public speaker, he spends lots of time giving talks in conferences and motivating people on Android development. You can find Wajahat most active on Twitter @WajahatKarim where he regularly shares all the good stuff about Android and community building.

Author's picture

Wajahat Karim

🔥 Google Dev Expert (GDE) in Android .
📱 Android Dev. 💻 Open Source Contributor . 📝 Technical Writer . 🎤 Public Speaker

Senior Android Developer

Karachi, Pakistan