Use Parceler to put your parcels on a diet

kotlin-parcelize is a great tool. Its simple to use and it helps in avoiding writing a lot of boilerplate code. There are times though that we need to take control of writing and reading to/from the parcel. One of these times is to cut down a few bytes from it (TransactionTooLargeException I am looking at you).

Meet me in the middle

@Parcelize takes full control and creates everything. Without the annotation, the developer has to do this on her own. Parceler lives in the middle of this spectrum. The plugin will create all necessary methods and classes but the actual write and read to/from the parcel will be the developer’s responsibility.

Without a Parceler the write/read looks like this:

public void writeToParcel(@NotNull Parcel parcel, int flags) {
Intrinsics.checkNotNullParameter(parcel, "parcel");
parcel.writeInt(this.id);
parcel.writeString(this.description);
parcel.writeString(this.priority.name());
parcel.writeParcelable(this.status, flags);
Attachment var10001 = this.attachment;
if (var10001 != null) {
parcel.writeInt(1);
var10001.writeToParcel(parcel, 0);
} else {
parcel.writeInt(0);
}
}
@NotNull
public final Task createFromParcel(@NotNull Parcel in) {
Intrinsics.checkNotNullParameter(in, "in");
return new Task(
in.readInt(),
in.readString(),
(Priority)Enum.valueOf(Priority.class, in.readString()),
(Status)in.readParcelable(Task.class.getClassLoader()),
in.readInt() != 0 ? (Attachment)Attachment.CREATOR.createFromParcel(in) : null
);
}

with a Parceler like this (where the Companion object is acting as a Parceler):

public void writeToParcel(@NotNull Parcel parcel, int flags) {
Intrinsics.checkNotNullParameter(parcel, "parcel");
Companion.write(this, parcel, flags);
}
@NotNull
public final Task createFromParcel(@NotNull Parcel in) {
Intrinsics.checkNotNullParameter(in, "in");
return Task.Companion.create(in);
}

Cutting down parcel’s size

The above-generated code is based on Task

@Parcelize
class Task(
val id: Int,
val description: Description,
val priority: Priority = Normal,
val status: Status = NotStarted,
val attachment: Attachment? = null
) : Parcelable
@Parcelize
class Attachment(val path: String) : Parcelable
@Parcelize
@JvmInline
value class Description(val value: String) : Parcelable
enum class Priority {
Low,
Normal,
High
}
sealed class Status : Parcelable {
@Parcelize
object NotStarted : Status()
@Parcelize
object InProgress : Status()
@Parcelize
class Completed(val completedAt: LocalDate) : Status()
}

which, creates a parcel of 248 bytes. The code does not do anything weird. All primitives, which include the value classes too, are well handled. So nothing to do here. This leaves parcelables and enums.

But first, let’s use a Parceler. This means that writing and reading to/from the parcel has to be implemented by us. For starters, we will do exactly what the generated code does except for the attachment property. For that, the generated code uses parcelable’s methods and CREATOR. In the Parceler we don’t have access to the CREATOR.

companion object : Parceler<Task> {
override fun create(parcel: Parcel): Task {
return Task(
parcel.readInt(),
Description(parcel.readString()!!),
Priority.valueOf(parcel.readString()!!),
parcel.readParcelable(Status::class.java.classLoader)!!,
parcel.readParcelable(Attachment::class.java.classLoader)
)
}
override fun Task.write(parcel: Parcel, flags: Int) {
with(parcel) {
writeInt(id)
writeString(description.value)
writeString(priority.name)
writeParcelable(status, flags)
writeParcelable(attachment, flags)
}
}
}

That leaves us with writeParcelable and readParcelable but now the parcel’s size is bigger, it is 328 bytes! Turns out that writeParcelable first writes the parcelable’s name and then the parcelable itself!

We need to use the CREATOR. After searching around I found parcelableCreator. A function that solved a well-known problem and will be added to Kotlin 1.6.20.

inline fun <reified T : Parcelable> Parcel.readParcelable(): T? {
val exists = readInt() == 1
if (!exists) return null
return parcelableCreator<T>().createFromParcel(this)
}
@Suppress("UNCHECKED_CAST")
inline fun <reified T : Parcelable> parcelableCreator(): Parcelable.Creator<T> =
T::class.java.getDeclaredField("CREATOR").get(null) as? Parcelable.Creator<T>
?: throw IllegalArgumentException("Could not access CREATOR field in class ${T::class.simpleName}")
fun <T : Parcelable> Parcel.writeParcelable(t: T?) {
if (t == null) {
writeInt(0)
} else {
writeInt(1)
t.writeToParcel(this, 0)
}
}

This allows us to revert the size increment back to 248 bytes

companion object : Parceler<Task> {
override fun create(parcel: Parcel): Task {
return Task(
//
parcel.readParcelable()
)
}
override fun Task.write(parcel: Parcel, flags: Int) {
with(parcel) {
//
writeParcelable(attachment)
}
}
}

Use enum’s ordinal than its name. The generated code writes enum’s name so that it can use Enum.valueOf when reading. We can write an int instead by using enum’s ordinal

companion object : Parceler<Task> {
override fun create(parcel: Parcel): Task {
return Task(
//
parcel.readEnum()
)
}
override fun Task.write(parcel: Parcel, flags: Int) {
with(parcel) {
//
writeEnum(priority)
}
}
}
inline fun <reified T : Enum<T>> Parcel.readEnum(): T {
return enumValues<T>()[readInt()]
}
inline fun <reified T : Enum<T>> Parcel.writeEnum(t: T) {
writeInt(t.ordinal)
}

and use Enum.values() when reading. This drops the parcel’s size to 232 bytes.

Skip a class’s parcelable implementation. This of course depends on each implementation.
For instance, Status is a sealed class that only one of its children has a construction parameter. We can leverage this by writing only that value

companion object : Parceler<Task> {
override fun create(parcel: Parcel): Task {
return Task(
//
parcel.readStatus()
)
}
override fun Task.write(parcel: Parcel, flags: Int) {
with(parcel) {
//
writeStatus(status)
}
}
}
fun Parcel.readStatus(): Status {
return readLong().let { value ->
when (value) {
0L -> NotStarted
1L -> InProgress
else -> Completed(LocalDate.ofEpochDay(value))
}
}
}
fun Parcel.writeStatus(status: Status) {
when (status) {
is Completed -> writeLong(status.completedAt.toEpochDay())
InProgress -> writeLong(1)
NotStarted -> writeLong(0)
}
}

this drops the parcel’s size to 136 bytes!

Conclusion

Fortunately, the generated code does a pretty good job and making any optimizations is not that common. But when needed Parceler and parcelableCreator are great tools.

PS: for measuring the parcel’s size I was using this method

fun Parcelable.sizeInBytes(): Int {
val parcel = Parcel.obtain()
try {
parcel.writeParcelable(this, 0)
return parcel.dataSize()
} finally {
parcel.recycle()
}
}

which was shamelessly stolen from Guardian’s TooLargeTool.

Create a seam for testing using default values and function references

I learned about seams after reading Micheal Feathers’s book Working Effectively with Legacy Code. In essence a seam is a way to circumvent code that makes testing hard or even impossible.

For example, lets say we have a class that checks if a given task is valid. For reasons that do not interest us that same class makes a connection to another service and sends some data to it. That connection alone makes the class hard to test since we need to have and maintain a connection to that service during testing:

class TaskChecker {
fun check(task: Task): CheckResult {
if (isNotCreatedInCurrentWeek(task)) return Invalid
if (isResolved(task)) return Invalid
if (isNotAssigned(task)) return Invalid
return Valid
}
private fun isNotAssigned(task: Task): Boolean {
if (task.assignedTo != Nobody) return false
val connection = Connection()
val assigner = TaskAssigner(connection)
assigner.add(task)
return true
}
//
}

In this example, isNotAssigned() makes the necessary checks but also sends the task to TaskAssigner so if we want to write some tests for TaskChecker we need to make sure that assigner is up and running.

Object seams

According to Mr Feathers there are three types of seams. The one that fits our case is called object seam and we are going to use it in order to bypass entirely making a connection and talking to the assigner.

Following the book’s example we end up with this:

class TestingTaskChecker : TaskChecker() {
override fun sendTaskToAssigner(task: Task) {
// do nothing
}
}
open class TaskChecker {
fun check(task: Task): CheckResult {
if (isNotCreatedInCurrentWeek(task)) return Invalid
if (isResolved(task)) return Invalid
if (isNotAssigned(task)) return Invalid
return Valid
}
private fun isNotAssigned(task: Task): Boolean {
if (task.assignedTo != Nobody) return false
sendTaskToAssigner(task)
return true
}
protected open fun sendTaskToAssigner(task: Task) {
val connection = Connection()
val assigner = TaskAssigner(connection)
assigner.add(task)
}
//
}

which does exactly what we want since it provides a way to write tests that do not involve the assigner. We just need to use TestingTaskChecker in our tests and we are good to go.

The downside with this approach is that we had to open our class which might not meet the project’s standards.

Function reference

Lets see what we can do without opening the class.

Just like before we need to extract the behavior that we want to override to its own method but this time we are also going to assign this method to a value and use the value in the calling site:

class TaskChecker {
private val safeSendTaskToAssigner: (Task) -> Unit = ::sendTaskToAssigner
fun check(task: Task): CheckResult {
if (isNotCreatedInCurrentWeek(task)) return Invalid
if (isResolved(task)) return Invalid
if (isNotAssigned(task)) return Invalid
return Valid
}
private fun isNotAssigned(task: Task): Boolean {
if (task.assignedTo != Nobody) return false
safeSendTaskToAssigner(task)
return true
}
private fun sendTaskToAssigner(task: Task) {
val connection = Connection()
val assigner = TaskAssigner(connection)
assigner.add(task)
}
//
}

isNotAssigned() will keep talking with the assigner only this time it does it through safeSendTaskToAssigner.

Default value

Having this function reference means that we can force isNotAssigned() to change its behavior by simply assigning a new value to safeSendTaskToAssigner! And this is what we are going to do:

class TaskChecker(
seamToAssigner: ((Task) -> Unit)? = null
) {
private val safeSendTaskToAssigner: (Task) -> Unit = seamToAssigner ?: ::sendTaskToAssigner
fun check(task: Task): CheckResult {
if (isNotCreatedInCurrentWeek(task)) return Invalid
if (isResolved(task)) return Invalid
if (isNotAssigned(task)) return Invalid
return Valid
}
private fun isNotAssigned(task: Task): Boolean {
if (task.assignedTo != Nobody) return false
safeSendTaskToAssigner(task)
return true
}
private fun sendTaskToAssigner(task: Task) {
val connection = Connection()
val assigner = TaskAssigner(connection)
assigner.add(task)
}
//
}

By default the seam is null which leads in having safeSendTaskToAssigner referencing the original behavior allowing the entire project to keep working as before without any additional changes to other files.

If now we pass a non null value then it gets assigned to safeSendTaskToAssigner and ends up being called instead of sendTaskToAssigner. This way we remove the communication from our flow allowing us to finally write some tests.

Testing

All we need to do is to write our tests by simply creating a checker with a do nothing seam:

@Test fun `a task that is not assigned is invalid`() {
val task = Task(AssignedTo.Nobody)
val taskChecker = TaskChecker {} // <– check with seam
val actual = taskChecker.check(task)
assertThat(actual, equalTo(Invalid))
}

Use suspendCoroutine to connect callbacks and coroutines

Whenever we need to write asynchronous code we tend to use callbacks which allow us to trigger an action and, instead of waiting for it to finish, get notified through the callback for the action’s completion. Coroutines change that and help us write asynchronous code but in a sequential way.

This means that instead of writing code like this:

fun main() {
downloadTasks(
object : DownloadCallback {
override fun onDownloaded(tasks: List<Task>) {
printTasks(tasks)
}
}
)
}
interface DownloadCallback {
fun onDownloaded(tasks: List<Task>)
}
private fun downloadTasks(callback: DownloadCallback) {
println("Downloading…")
// code that makes a network call and returns the list of tasks
callback.onDownloaded(listOf(Task(1), Task(2), Task(3)))
}
private fun printTasks(tasks: List<Task>) {
tasks.forEach { task -> println(task.id) }
}

we can write it like this:

fun main(): Unit = runBlocking {
launch {
val tasks = downloadTasks()
printTasks(tasks)
}
}
private suspend fun downloadTasks() {
println("Downloading…")
// code that makes a network call and returns the list of tasks
return listOf(Task(1), Task(2), Task(3))
}
private fun printTasks(tasks: List<Task>) {
tasks.forEach { task -> println(task.id) }
}

But what do we do when there is no easy way to remove callbacks from existing code or when we use a third party library that is not coroutines ready? This is where suspendCoroutine comes to save the day.

suspendCoroutine

suspendCoroutine is a function that does exactly what is says. It suspends the coroutine that it was called from and provides a way to resume it.

Lets have an example. The code here:

fun main(): Unit = runBlocking {
launch {
print("1 ")
print("2 ")
print("3 ")
print("4 ")
println("Done!")
}
}

simply prints 1 2 3 4 Done!. If we change it to:

fun main(): Unit = runBlocking {
launch {
print("1 ")
print("2 ")
print("3 ")
suspendCoroutine<Unit> { }
print("4 ")
println("Done!")
}
}

it will print 1 2 3 and then it will just wait. We suspended the coroutine but we did not resume it. To do so we will use the continuation instance that suspendCoroutine provides:

fun main(): Unit = runBlocking {
launch {
print("1 ")
print("2 ")
print("3 ")
suspendCoroutine<Unit> { continuation ->
print("")
continuation.resume(Unit)
}
print("4 ")
println("Done!")
}
}

now it prints 1 2 3 โ€ฆ 4 Done!. The coroutine printed the first three numbers, got suspended, while being suspended another block of code got executed and printed the dots and then resumed the coroutine allowing it to print the final number and done.

Continuation adapter

Back to our first example. Lets say that downloadTasks cannot be changed. We still need to call it and provide a callback for its results.

What we need to do is to suspend the coroutine, call downloadTasks to.. well.. download the tasks and provide a callback that upon completion it will resume the coroutine with the tasks at hand.

To achieve that we first need to create an adapter that will connect the callback with a continuation:

private class ContinuationAdapter(
private val continuation: Continuation<List<Task>>
) : DownloadCallback {
override fun onDownloaded(tasks: List<Task>) {
continuation.resume(tasks)
}
}

and then call suspendCoroutine:

fun main(): Unit = runBlocking {
launch {
val tasks = suspendCoroutine<List<Task>> { continuation -> downloadTasks(ContinuationAdapter(continuation)) }
printTasks(tasks)
}
}

That’s it. The adapter resumes the coroutine by providing the tasks that are then returned to the suspension point.

One more thing

Along side suspendCoroutine there is also suspendCancellableCoroutine which provides a cancellable continuation. That means that in addition of resuming we can also execute code upon cancellation:

fun main(): Unit = runBlocking {
val job = launch(Dispatchers.IO) {
val tasks = downloadAllTasks()
printTasks(tasks)
}
delay(100)
job.cancel()
}
private suspend fun downloadAllTasks(): List<Task> {
return suspendCancellableCoroutine { continuation ->
continuation.invokeOnCancellation { print("Cancelled…") }
downloadTasks(ContinuationAdapter(continuation))
}
}
private class ContinuationAdapter(
private val continuation: Continuation<List<Task>>
) : DownloadCallback {
override fun onDownloaded(tasks: List<Task>) {
continuation.resume(tasks)
}
}
interface DownloadCallback {
fun onDownloaded(tasks: List<Task>)
}
private fun downloadTasks(callback: DownloadCallback) {
println("Downloading…")
sleep(150) // simulate network latency
val tasks = listOf(Task(1), Task(2), Task(3))
callback.onDownloaded(tasks)
}
private fun printTasks(tasks: List<Task>) {
tasks.forEach { task -> println(task.id) }
}
to improve the readability of the code we can hide the suspension inside another function

this will print Downloading… and then Cancelled…

Lets build a coroutine

Ever since Kotlin introduced coroutines there has been a plethora of posts that showcased their usage in networking or multi threading scenarios. Inevitably many developers when asked what a coroutine is their first answer was “a lightweight thread” or “a way to write clean asynchronous code”. Unfortunately this is not the definition of a coroutine but rather a couple of things that a coroutine can help us with.

Routine (aka Subroutine)

So what is a coroutine? To answer that we must first understand what a routine is.

A routine is nothing more than the common function that we all use every day. Its main characteristic is that its execution must come to a completion before returning to the caller. It does not hold any state and calling it multiple times is like calling it for the first time.

An example:

// a routine:
fun saveUserTasks(userId: Int) {
val user = loadUser(userId)
println("user loaded")
val tasks = loadTasks(user)
println("tasks loaded")
saveTasks(tasks)
}
// when called multiple times:
fun main() {
println("Call #1:")
saveUserTasks(7)
println("Call #2:")
saveUserTasks(7)
println("Call #3:")
saveUserTasks(7)
println("done!")
}
// its like calling it for the first time:
/*
Call #1:
loading a user…
user loaded
loading tasks for provided user…
tasks loaded
saving provided tasks…
Call #2:
loading a user…
user loaded
loading tasks for provided user…
tasks loaded
saving provided tasks…
Call #3:
loading a user…
user loaded
loading tasks for provided user…
tasks loaded
saving provided tasks…
done!
*/
Coroutine

On the other hand a coroutine (a concept that is way older than Kotlin) is a function that can hold its current state allowing us to pause and resume its execution at certain suspension points. This can be of great help when we need to write concurrent code, meaning, when we need to run two tasks at the same time by executing small parts of those tasks one at a time.

For example, if we have task A broken in two parts (A1, A2) and task B broken in two parts as well (B1, B2), we can write code that executes first A1, then B1, then A2 and finally B2.

Building a coroutine

Our goal is to convert the above routine into a coroutine and to do so we need to have a blueprint:

fun saveUserTasks(userId: Int) {
val user = loadUser(userId)
println("user loaded")
// save loaded user
// pause execution
// resume execution
// restore saved user
val tasks = loadTasks(user)
println("tasks loaded")
// save loaded tasks
// pause execution
// resume execution
// restore saved tasks
saveTasks(tasks)
}

So, step #1 is to separate the states into blocks of code and to do that we are going to use the when statement:

fun saveUserTasks(userId: Int, state: Int) {
when (state) {
0 -> {
val user = loadUser(userId)
println("user loaded")
// save loaded user
// pause execution
}
1 -> {
// resume execution
// restore saved user
val tasks = loadTasks(user) // ERROR: unknown user
println("tasks loaded")
// save loaded tasks
// pause execution
}
2 -> {
// resume execution
// restore saved tasks
saveTasks(tasks) // ERROR: unknown tasks
}
}
}

the problem here is that the code does not compile since the states do not communicate and are missing important information.

To fix it we are moving to step #2 where the state parameter will be used as a vessel to pass data from one state to the other:

class State(
var label: Int = 0,
var result: Any? = null
)
fun saveUserTasks(userId: Int, state: State) {
when (state.label) {
0 -> {
val user = loadUser(userId)
println("user loaded")
state.result = user
// pause execution
}
1 -> {
// resume execution
val user = state.result as User
val tasks = loadTasks(user)
println("tasks loaded")
state.result = tasks
// pause execution
}
2 -> {
// resume execution
val tasks = state.result as List<Task>
saveTasks(tasks)
}
}
}

the code now has distinct states that share data but it cannot resume correctly since there is no way to move from one state to the other.

Step #3 addresses that by updating the state’s label allowing the function to resume from where it was paused:

fun saveUserTasks(userId: Int, state: State) {
when (state.label) {
0 -> {
val user = loadUser(userId)
println("user loaded")
state.result = user
state.label = 1
return
}
1 -> {
// resume execution
val user = state.result as User
val tasks = loadTasks(user)
println("tasks loaded")
state.result = tasks
state.label = 2
return
}
2 -> {
// resume execution
val tasks = state.result as List<Task>
saveTasks(tasks)
}
}
}

and that’s it. saveUserTasks is now a coroutine where every call executes a small part of the function allowing us to pause and resume the task:

fun main() {
val state = State()
println("Call #1:")
saveUserTasks(7, state)
println("Call #2:")
saveUserTasks(7, state)
println("Call #3:")
saveUserTasks(7, state)
println("done!")
}
// results in:
/*
Call #1:
loading a user…
user loaded
Call #2:
loading tasks for provided user…
tasks loaded
Call #3:
saving provided tasks…
done!
*/

Concurrency

We did manage to convert a routine to a coroutine but the example did not show the power of using coroutines which is concurrency. Lets change that.

To make things a little bit easier we are going to package a few common functionalities together

abstract class Coroutine {
var isFinished: Boolean = false
private set
protected val state: State by lazy { State() }
protected fun resumeWith(result: Any) {
state.label++
state.result = result
}
protected fun <T> restore(): T {
return state.result as T
}
protected fun finish() {
state.label++
isFinished = true
}
protected class State(
var label: Int = 0,
var result: Any? = null
)
}

which allows us to create these

class SaveUserTasks : Coroutine() {
operator fun invoke(userId: Int) {
saveUserTasks(userId, state)
}
private fun saveUserTasks(userId: Int, state: State) {
when (state.label) {
0 -> {
val user = loadUser(userId)
println("user loaded")
resumeWith(user)
return
}
1 -> {
val user = restore<User>()
val tasks = loadTasks(user)
println("tasks loaded")
resumeWith(tasks)
return
}
2 -> {
val tasks = restore<List<Task>>()
saveTasks(tasks)
finish()
}
}
}
}
class ApplyDownloadedSettings : Coroutine() {
operator fun invoke() {
applyDownloadedSettings(state)
}
private fun applyDownloadedSettings(state: State) {
when (state.label) {
0 -> {
val settings = downloadSettings()
println("settings downloaded")
resumeWith(settings)
return
}
1 -> {
val settings = restore<Settings>()
applySettings(settings)
println("setting applied")
finish()
}
}
}
}

This way we can call the two functions as such:

fun main() {
val saveUserTasks = SaveUserTasks()
val applyDownloadedSettings = ApplyDownloadedSettings()
while (!saveUserTasks.isFinished || !applyDownloadedSettings.isFinished) {
saveUserTasks(7)
applyDownloadedSettings()
}
println("done!")
}
/*
loading a user…
user loaded
downloading settings…
settings downloaded
loading tasks for provided user…
tasks loaded
applying settings…
setting applied
saving provided tasks…
done!
/*

As you can see the two functions are being executed concurrently. First we load the user, then we download the settings, then we load the user’s task and so on and so forth!

Further reading

My goal with this post was to give you a better understanding on what a coroutine is and I did it by using the concept of a finite state machine. The same concept that Kotlin’s implementation of coroutines uses.

For digging in the actual implementation of coroutines I suggest you take a look at a couple of great posts that helped me a lot in grasping the concept:

IoC: Inversion of Control principle

This is the one principle that, chances are, you have applied even if you didn’t do it on purpose.
In essence, if you have written code that does not have any control over the execution flow, then you most probably have applied the IoC principle. How?

IoC through design patterns

If you have implemented the strategy or the template pattern then you have applied the IoC principle. For example, having a report generator and feeding it with the necessary filtering code.
All the code you write for filtering data, does not have any control over the execution’s flow. The generator is the one that decides when and if it will be invoked:

class Report(
val women: List<Person>,
val men: List<Person>
) {
fun isEmpty(): Boolean {
return women.isEmpty() && men.isEmpty()
}
}
class ReportGenerator(
private val repository: Repository
) {
fun create(filter: (Report) -> Report): Report {
val people = repository.fetchAllPeople()
val rawReport = splitByGender(people)
if (rawReport.isEmpty()) {
return rawReport
}
// the filter function does not have any control over its invokation
val filteredReport = filter(rawReport)
return sortByName(filteredReport)
}
//
}
// example:
val reportGenerator = ReportGenerator(repository)
val withPeopleOver21 = { rawReport: Report ->
val over21 = { person: Person -> person.age > 21 }
Report(
rawReport.women.filter(over21),
rawReport.men.filter(over21)
)
}
val reportWithPeopleOver21 = reportGenerator.create(withPeopleOver21)
Strategy pattern

The same goes with the template pattern too. The code you write in the template’s hooks is being controlled by the template and you don’t get to control it:

class Report(
val women: List<Person>,
val men: List<Person>
) {
fun isEmpty(): Boolean {
return women.isEmpty() && men.isEmpty()
}
}
abstract class ReportGenerator(
private val repository: Repository
) {
fun create(): Report {
val people = repository.fetchAllPeople()
val rawReport = splitByGender(people)
if (rawReport.isEmpty()) {
return rawReport
}
val filteredReport = filter(rawReport)
return sortByName(filteredReport)
}
abstract fun filter(report: Report): Report
//
}
class AdultsReportGenerator(repository: Repository) : ReportGenerator(repository) {
override fun filter(report: Report): Report {
val over21 = { person: Person -> person.age > 21 }
return Report(
report.women.filter(over21),
report.men.filter(over21)
)
}
}
// example:
val generator = AdultsReportGenerator(repository)
val reportWithPeopleOver21 = generator.create()
Template pattern

Both of these examples might seem weird since we are the ones that wrote both the filtering code and the generator so we feel that we control the flow. The thing is that we need to separate them in our heads and observe them individually. The generator is the one that controls the flow and dictates the actions that will take place. The filtering code is one of the actions. We just write them, provide them to the generator and that’s it.

IoC through frameworks

Another way of applying IoC is by using frameworks that have adopted it. Most popular are the IoC containers that are used to inject dependencies.
Another example is the android’s framework. In android you don’t have control over an activity’s lifecycle and you simply extend the Activity class and override hooks to run your code.

IoC vs DI

Because of the aforementioned IoC containers, many people assume that dependency injection and IoC are the same. They are not. DI is just a way to help in applying the IoC principle. For example:

class Report(
val women: List<Person>,
val men: List<Person>
) {
fun isEmpty(): Boolean {
return women.isEmpty() && men.isEmpty()
}
}
class ReportGenerator(
private val repository: Repository,
private val filter: (Report) -> Report
) {
fun create(): Report {
val people = repository.fetchAllPeople()
val rawReport = splitByGender(people)
if (rawReport.isEmpty()) {
return rawReport
}
val filteredReport = filter(rawReport)
return sortByName(filteredReport)
}
//
}
// Example:
val withPeopleOver21 = { report: Report ->
val over21 = { person: Person -> person.age > 21 }
Report(
report.women.filter(over21),
report.men.filter(over21)
)
}
val generator = ReportGenerator(repository, withPeopleOver21)
val reportWithPeopleOver21 = generator.create()

we can change the generator and have the filtering code being injected to it by constructor. The inversion is already happening, DI is simply used to provide the extra code.

The memento design pattern in Kotlin

I started playing with the memento pattern for a use case I was researching when I realized that the Kotlin implementation had a, potentially, show stopper in comparison with the Java one:

I could not use a private property from within the same file

Why was that a show stopper? We’ll see, but first, what is the memento pattern?

Memento pattern

This pattern is a good way to implement a functionality that helps in restoring previous states. One good example is the undo in our text editors. You can write, edit, delete and then, by hitting undo, take each action back.

There are three main ingredients for this pattern:

  1. the originator that holds the current state and creates snapshots of itself,
  2. the memento that, in essence, is the snapshot with perhaps some additional metadata and
  3. the caretaker that orchestrates the backup/restore of the state

So in our example the originator is the editor which knows what the text is, the carets position etc, the memento a copy of those values and the caretaker can be the interface between the user and the editor.

Java implementation

Lets try to have an overly simplified version of the above example in Java:

public final class Editor {
private final List<String> text;
private int caretPosition;
public Editor() {
this.text = new ArrayList<>();
this.caretPosition = 0;
}
public void write(final String sentence) {
text.add(sentence);
caretPosition = calculateCaretPositionInEndOf(text);
}
public void edit(final int index, final String newSentence) {
text.remove(index);
text.add(index, newSentence);
final List<String> subText = text.subList(0, index + 1);
caretPosition = calculateCaretPositionInEndOf(subText);
}
public void delete(final int index) {
final List<String> subText = new ArrayList<>(text.subList(0, index));
text.remove(index);
caretPosition = calculateCaretPositionInEndOf(subText);
}
public void render(final Screen screen) {
final String allText = String.join("", text);
screen.render(allText);
screen.renderCaretAt(caretPosition);
}
public Memento backup() {
return new Memento(text, caretPosition);
}
public void restore(final Memento memento) {
text.clear();
text.addAll(memento.text);
caretPosition = memento.caretPosition;
}
private int calculateCaretPositionInEndOf(final List<String> lines) {
return lines.stream().mapToInt(String::length).sum() + 1;
}
public static final class Memento {
private final List<String> text;
private final int caretPosition;
public Memento(List<String> text, int caretPosition) {
this.text = new ArrayList<>(text);
this.caretPosition = caretPosition;
}
}
}

Here the editor, besides manipulating text, is able to produce snapshots of its state in a way that only itself can access the state’s values. The Memento class might be public, in order to allow the caretaker to handle instances of it, but its fields are private and only the originator can read them.
A great way to copy something while having the smallest possible API surface and maximum privacy.

As a matter of fact, here is the caretaker and its usage:

class UI(
private val screen: Screen
) {
private val editor = Editor()
private val backups = mutableListOf<Memento>()
fun write(text: String) {
backups.add(0, editor.backup())
editor.write(text)
editor.render(screen)
}
fun edit(index: Int, text: String) {
backups.add(0, editor.backup())
editor.edit(index, text)
editor.render(screen)
}
fun delete(index: Int) {
backups.add(0, editor.backup())
editor.delete(index)
editor.render(screen)
}
fun undo() {
val memento = backups.removeAt(0)
editor.restore(memento)
editor.render(screen)
}
}
fun main() {
val screen = StdoutScreen()
val ui = UI(screen)
with(ui) {
write("Hello, there! ")
write("How are you? ")
write("I hope you feel good ๐Ÿ™‚")
edit(1, "Kotlin! ")
delete(1)
undo()
undo()
undo()
undo()
undo()
}
}
/* which produces this:
Hello, there! |
Hello, there! How are you? |
Hello, there! How are you? I hope you feel good :)|
Hello, there! Kotlin! |I hope you feel good ๐Ÿ™‚
Hello, there! |I hope you feel good ๐Ÿ™‚
Hello, there! Kotlin! |I hope you feel good ๐Ÿ™‚
Hello, there! How are you? I hope you feel good :)|
Hello, there! How are you? |
Hello, there! |
|
/*

As you can see the UI uses the editor to write, edit, delete but before that it saves a backup with the editor’s state in order to restore it every time the user hits undo!

Kotlin implementation

So lets move originator and memento to Kotlin. Ctrl+Alt+Shift+K and boom.. we have a problem:

Kotlin, in contrast with Java, does not allow accessing private properties when in the same file.

What do we do? Well we can always make the properties public:

class Memento(text: List<String>, caretPosition: Int) {
val text: List<String>
val caretPosition: Int
init {
this.text = ArrayList(text)
this.caretPosition = caretPosition
}
}

but this way we, indirectly, expose the editors state:

Another way to implement the pattern is to have Memento as an interface with no state for the public API and have a private implementation of it for internal usage:

class Editor {
//
fun backup(): Memento {
return ActualMemento(text, caretPosition)
}
fun restore(memento: Memento) {
if (memento !is ActualMemento) return
text.clear()
text.addAll(memento.text)
caretPosition = memento.caretPosition
}
//
interface Memento
private class ActualMemento(text: List<String>, caretPosition: Int) : Memento {
val text: List<String>
val caretPosition: Int
init {
this.text = ArrayList(text)
this.caretPosition = caretPosition
}
}
}

this way we do not expose any state but we do open a bit our API. We now have an interface that can be implemented and given to the restore() function.

Inner classes

Fortunately Kotlin has inner classes. An inner class can access the outer class’s members but, most importantly, can be extended only from within the outer class. This means that this:

class Editor {
//
fun backup(): Memento {
return ActualMemento(text, caretPosition)
}
fun restore(memento: Memento) {
memento as ActualMemento
text.clear()
text.addAll(memento.text)
caretPosition = memento.caretPosition
}
//
open inner class Memento
private inner class ActualMemento(text: List<String>, caretPosition: Int) : Memento() {
val text: List<String>
val caretPosition: Int
init {
this.text = ArrayList(text)
this.caretPosition = caretPosition
}
}
}

checks all our boxes. We keep the originator’s state private and our overall API small!

I wrote a GitHub Action using Kotlin

I decided to take a look at GitHub Actions so for the past week I’ve been watching and reading everything about it. I even wrote a post, an Introduction to GitHub Actions. What I found really interesting is the fact that you can write an action using the language you feel more comfortable with. And so I did!

I wrote a small action that:

  1. collects all the changed files from a PR,
  2. keeps those that have the .kt suffix,
  3. runs ktlint on them and
  4. makes a comment in the PR for every error that ktlint reports

and all that using Kotlin and some bash!

You can find and use it here: ktlint-pr-comments

GitHub Action using Docker

There are three ways to create an action but only the one using Docker allows us to use the language we want.

In all three ways the main two ingredients are:

  • the action’s code
  • the action’s metadata, a file called action.yml which is placed in the root folder of your project and defines how the action will run and what inputs/outputs it has.

In our case there is also a third ingredient, a Dockerfile or a docker image which is used by the action’s runner to create a container and execute the action’s code inside it. All you have to do is to make sure that the action’s executable parts are being copied in the container and that are called upon its start.

The runner makes sure that the working space is being mounted to the container (in the state that it was just before the action is started) along with all the environment variables and the inputs the action needs. You can read more in the documentation.

Ktlint PR comments

Action’s code

The action has three distinct parts.

The first part is responsible for using GitHub’s REST API to collect all of the PR’s changes and then keep those that are in Kotlin files and were added or modified. For that I used kscript and I was able to leverage all the libraries that I was accustomed to, like Retrofit and Moshi. When I was happy with the resulted script I used its --package option to create a standalone binary and copy it in the action’s Docker image.

The second part is a combination of bash commands that execute the ktlint binary by passing to it the results of the first part. Ktlint is being called with the --reporter=json parameter in order to create a JSON report.

The third and final part is again a kscript script that uses the report created before and GitHub’s REST API to make a PR line comment for every ktlint error that is part of the PR’s diff. Again a standalone binary was created and put in the image.

Note:

I like kscript since I can write things fast, easy and with all the libraries that I know but I also like writing test first and that proved to be quite difficult. So what I ended up doing was to act as if I was in a Kotlin project. I created my tests (using all my favorites like junit5, hamkrest and MockWebServer) and from that I created .kt files with the proper functionality. And for having a script I created a .kts file where I defined the external dependencies and included the .kt files:

import kotlin.system.exitProcess
//DEPS com.squareup.moshi:moshi:1.9.3
//DEPS com.squareup.moshi:moshi-kotlin:1.9.3
//DEPS com.squareup.retrofit2:retrofit:2.9.0
//DEPS com.squareup.retrofit2:converter-moshi:2.9.0
//INCLUDE logging.kt
//INCLUDE common.kt
//INCLUDE collectPrChanges.kt
//INCLUDE createGithubEvent.kt
val result = collectPrChanges(args)
if (result != 0) {
exitProcess(result)
}
println("Changes collected")
Action’s metadata

From the start what I wanted for this action was to be as autonomous as possible leaving very little responsibilities to the consumer and allowing her to just plug it in and watch it play.

For that the only input the action needs is a token, for allowing the kscript scripts to communicate with the API, which will most likely be the default secrets.GITHUB_TOKEN making the action’s usage as simple as adding the following lines in your workflow:

- uses: le0nidas/ktlint-pr-comments@v1
  with:
    repotoken: ${{ secrets.GITHUB_TOKEN }}
Docker image

A lot of things must happen in order to have the action ready to run. Sdkman, Kotlin and kscript must be installed, the code needs to be retrieved from the repository and both kscript scripts have to be packaged. On top of that ktlint must be downloaded and placed in the proper path.

For all that, and to shave a few seconds from the action, I decided to have an image that has everything ready. So I created a workflow that gets triggered every time there is a push in the main branch, builds and packs everything in an image and pushes the result to Docker Hub.

So now the action simply uses that image to run a container without any other ceremonies.

Note:

Before using Docker Hub I tried to use GitHub Packages but it turns out that public is not that public* since it requires an authentication to retrieve a package.

Summary

That’s it! An action for having ktlint’s report as comments in your PR. A result of trial and error since I wrote it while learning about actions but I hope that someone might find it useful. If you do let me know!

An example of how to use it can be found in the action’s repository where I dogfood it to the project.

* [3 Sep 2020]: looks like things will change

A smooth refactor using sealed classes and a factory function

The problem

Lets say we have a contacts app and one of the screens shows the contact’s phone number.

// domain:
class PhoneNumber(val value: String)
class Contact(val phoneNumber: PhoneNumber)
// screen:
class PhoneNumberScreen(
private val phoneNumber: PhoneNumber
) {
fun render() {
println("Phone number: ${phoneNumber.value}")
}
}
// presentation layer:
fun main() {
val contact = Contact(PhoneNumber("12345"))
show(contact.phoneNumber) // "Phone number: 12345"
val contactWithInvalidPhoneNumber = Contact(PhoneNumber(""))
show(contactWithInvalidPhoneNumber.phoneNumber) // "Phone number: "
}
private fun show(phoneNumber: PhoneNumber) {
val phoneNumberScreen = PhoneNumberScreen(phoneNumber)
phoneNumberScreen.render()
}

The problem with that code is that we can easily end up with instances that contain invalid state:

A phone number screen with an empty phone number!

Approach #1:

One way to prevent it is to add some logic in the presentation layer:

Open the phone number screen only if the phone number is not empty

fun main() {
val contact = Contact(PhoneNumber("12345"))
show(contact.phoneNumber) // "Phone number: 12345"
val contactWithInvalidPhoneNumber = Contact(PhoneNumber(""))
show(contactWithInvalidPhoneNumber.phoneNumber) // does not show anything
}
private fun show(phoneNumber: PhoneNumber) {
if (phoneNumber.value.isEmpty()) {
return
}
val phoneNumberScreen = PhoneNumberScreen(phoneNumber)
phoneNumberScreen.render()
}

Unfortunately this approach does not provide an actual solution but a patch. Our main goal is to have a PhoneNumberScreen that handles ONLY valid phone numbers.

Approach #2:

What we need is to move the necessary checks inside the PhoneNumberScreen class.

We could check the number’s validity on render and show some kind of message when there is no phone number.

class PhoneNumberScreen(
private val phoneNumber: PhoneNumber
) {
fun render() {
if (phoneNumber.value.isNotEmpty()) {
println("Phone number: ${phoneNumber.value}")
} else {
println("Invalid phone number")
}
}
}
fun main() {
val contact = Contact(PhoneNumber("12345"))
show(contact.phoneNumber) // "Phone number: 12345"
val contactWithInvalidPhoneNumber = Contact(PhoneNumber(""))
show(contactWithInvalidPhoneNumber.phoneNumber) // "Invalid phone number"
}
private fun show(phoneNumber: PhoneNumber) {
val phoneNumberScreen = PhoneNumberScreen(phoneNumber)
phoneNumberScreen.render()
}

It is quite clear that this solution provides a bad UX. Why open a screen when the user cannot use it? Also, in any additional usage of phoneNumber inside the PhoneNumberScreen we need to make the same check as in render() and handle both of its states.

Approach #3:

What we really need is to make sure that if the screen is created, then it is certain that it has a valid phone number. There are two ways to achieve that. The first one is by checking upon creation that the phone number is valid and throw an exception if it is not.

class PhoneNumberScreen(
private val phoneNumber: PhoneNumber
) {
init {
require(phoneNumber.value.isNotEmpty()) { "cannot handle invalid phone number" }
}
fun render() {
println("Phone number: ${phoneNumber.value}")
}
}
fun main() {
val contact = Contact(PhoneNumber("12345"))
show(contact.phoneNumber) // "Phone number: 12345"
val contactWithInvalidPhoneNumber = Contact(PhoneNumber(""))
show(contactWithInvalidPhoneNumber.phoneNumber) // prints nothing
}
private fun show(phoneNumber: PhoneNumber) {
try {
val phoneNumberScreen = PhoneNumberScreen(phoneNumber)
phoneNumberScreen.render()
} catch (ex: IllegalArgumentException) {
}
}

It works but we need to document it and add a try-catch wherever we create a screen instance.

The second one is by having a helper function that creates the screen only if the phone number is valid.

class PhoneNumberScreen private constructor(
private val phoneNumber: PhoneNumber
) {
fun render() {
println("Phone number: ${phoneNumber.value}")
}
companion object {
fun create(phoneNumber: PhoneNumber): PhoneNumberScreen? {
return when {
phoneNumber.value.isNotEmpty() -> PhoneNumberScreen(phoneNumber)
else -> null
}
}
}
}
fun main() {
val contact = Contact(PhoneNumber("12345"))
show(contact.phoneNumber) // "Phone number: 12345"
val contactWithInvalidPhoneNumber = Contact(PhoneNumber(""))
show(contactWithInvalidPhoneNumber.phoneNumber) // prints nothing
}
private fun show(phoneNumber: PhoneNumber) {
val phoneNumberScreen = PhoneNumberScreen.create(phoneNumber)
phoneNumberScreen?.render()
}

This also works as expected but once again we need to document it and on top of that handle any null values returned by the helper function.

Nevertheless this solution seems fine and for a very small code base is quite acceptable. The problem is that it does not scale alongside the code base. Imagine how many helper functions we need to implement every time we have to use a phone number instance in our components if we want to keep them “clean”.

The actual problem

The actual problem lies in the PhoneNumber itself. It represents more than one states and each time we use an instance of it we must “dive” in its value and translate it to that state.

What we really need is a better representation of a valid and invalid phone number.

Final approach

This is where we use sealed classes and separate the two states:

sealed class PhoneNumber
object InvalidPhoneNumber : PhoneNumber()
data class ValidPhoneNumber(val value: String) : PhoneNumber() {
init {
require(value.isNotEmpty()) { "the number cannot be empty" }
}
}

This way we accomplish our main goal: we change the PhoneNumberScreen to accept only instances of ValidPhoneNumber and can now be certain that the screen will be used only with valid data. The code is self documented and any further development in the screen class will not have to consider other states for the phone number:

class PhoneNumberScreen(
private val phoneNumber: ValidPhoneNumber
) {
fun render() {
println("Phone number: ${phoneNumber.value}")
}
}

One big drawback of this change is that every usage of the PhoneNumber class has just broke (see: creation of Contact instances).

Fortunately there is a quick solution for that! Factory function:

A function that has the same name with the previously used class (PhoneNumber) and takes a single string parameter. If the parameter is not empty it returns a ValidPhoneNumber. In any other case it returns an InvalidPhoneNumber:

fun PhoneNumber(value: String): PhoneNumber =
when {
value.isNotEmpty() -> ValidPhoneNumber(value)
else -> InvalidPhoneNumber
}

The end result is almost the same as the starting point but this time we have clear states and components that can guarantee that they will not crash because of erroneous data:

fun main() {
val contact = Contact(PhoneNumber("12345"))
show(contact.phoneNumber) // "Phone number: 12345"
val contactWithInvalidPhoneNumber = Contact(PhoneNumber(""))
show(contactWithInvalidPhoneNumber.phoneNumber) // prints nothing
}
private fun show(phoneNumber: PhoneNumber) {
if (phoneNumber is ValidPhoneNumber) {
val phoneNumberScreen = PhoneNumberScreen(phoneNumber)
phoneNumberScreen.render()
}
}