Secondary constructors for easy network request bodies in Kotlin

Recently I stumbled across a great feature of Kotlin that made for some beautiful coding. Secondary constructors are a great option if you need to construct the body of a network request from an existing object that already contains all of the necessary parameters.

The app I’m working on uses Retrofit for some network calls, and the body of these calls contains quite a few parameters. Finding each of these parameters each time I want to make a request can get messy, so my activity maintains an instance of a data class representing a remote device with all of these parameters plus a few extras. In other words, my activity already utilizes a model of what I need to be passing with my network requests. However it does contain extra properties as well, so I can’t just pass that object as my network request body.

Let’s call the data class in my activity Device, and say for example that Device looks like this:

data class Device(
var address: String? = null,
var serialNumber: String? = null,
var count: Int? = null,
var type: Int? = null,
var position: Int? = null,
var rssi: Int? = null,
var battery: Int? = null,
var interval: Int? = null,
var reading: Int? = null,
var firmware: String? = null
)

A new Device object is initialized when the activity is created, and its properties are updated as the user navigates through the activity and calls commands on the remote device. What I need to do is make POST requests to the server containing some, but not all of the properties of this object. Here’s what the body of these requests looks like:

data class RequestBody(
var serialNumber: String? = null,
var count: Int? = null,
var type: Int? = null,
var position: Int? = null,
var rssi: Int? = null,
var battery: Int? = null,
var interval: Int? = null,
var reading: Int? = 0,
)

As you can see, the body requires almost everything that I already keep track of in the Activity, minus the address and firmware.

Rather than make an entire new data class for my activity just to keep track of the body’s parameters, I was looking for a way to utilize the Device object I already have. One way was to pass the property values of the Device manually as each parameter of the request body:

retrofit.postModelBody(
ModelBody(
device.serialNumber,
device.count,
device.type,
device.position,
device.rssi,
device.battery,
device.interval,
device.reading
)
)

This is still better than having to figure out each parameter individually for the request, but I really wanted some way to pass in the complete Device object and just have my network request logic do the work at figuring out which parameters were needed for the specific call.

This is when I found out about Kotlin’s secondary constructors feature, which basically allows us to pass a bulky object model with extra properties in to construct a network request body with just the properties used for the request. I think of this as a way to “trim down” a large object with extra properties into a smaller model that fits what an API is expecting. This makes our activity easier to manage because instead of breaking apart our data to make the request, we let our request break down the data. All we have to do is add a little bit of code to our model body to implement the secondary constructor:

data class RequestBody(
var serialNumber: String? = null,
var count: Int? = null,
var type: Int? = null,
var position: Int? = null,
var rssi: Int? = null,
var battery: Int? = null,
var interval: Int? = null,
var reading: Int? = 0,
) {
constructor(device: Device) : this(
device.serialNumber,
device.count,
device.type,
device.position,
device.rssi,
device.battery,
device.interval,
device.reading
)
}

So essentially, by adding this extra constructor to our request body we save ourselves the same amount of code every single time we make a request in our activity. Now all we have to do is pass our requests a Device object and the secondary constructor will use it to create the model body!

retrofit.postModelBody(ModelBody(device))

Secondary constructors give us a way to create network request models from objects we already have in our Activity, View Model, or wherever else runtime data is kept! This can make our requests easier to manage and save code for multiple requests of the same type in our apps.