Using Kotlin to build Comics Outmash

The Kotlin logo (a cat-like figure with a head shaped like a rotated uppercase K) wearing a super-hero mask and standing talk on top of a building. In the background is the moon and a bunch of tall buildings.

In 2022, a colleague of mine asked if I’d be able to help write a Swift on server demo for a WWDC session. At the time, I remember answering to their Slack message with something like “if you cut me I will bleed Swift on server” which, at the time, was probably true. Knowing this, and if you know me at all, you might have been slightly surprised when I mentioned that I decided to use Kotlin for the Comics Outmash project.

In The Beginning, There Was Swift

This is not the first time that I started working on Comics Outmash. I originally bootstrapped the project using Swift and Vapor. I have been a big fan of Vapor since the introduction of version 3. I think Vapor is a wonderful framework that I’ve used personally and professionally, at various scales. I even had the opportunity to write a very minor contribution, which is something I hope to repeat. Unfortunately, I was unable to make any progress on this first version of the project. While I think that the state of my mental health and the reduced amount of personal time that I could dedicate to this project were partly to blame, I think I got sick of how unproductive I felt overall.

This project is not complicated. I have 3 models: editors, comics and votes. It needs some kind of administration panel to allow a chosen few to contribute content. That means handling authorization, rendering forms, validating data and presenting errors. Nothing I couldn’t handle, but I couldn’t stop myself from trying to be smart with Swift and generics, as I tried to abstract the logic behind the two (2!) forms in the admin console. I saw this project as an investment into future ones where I would have my own library to present and validate forms. This resulted in a massive case of analysis paralysis with very little results to show for it.

I don’t think this problem was unsolvable given enough time, or more likely given enough time away from the computer to get a bit of fresh air, take some distance and realize I was being dumb. After all, it is not the first time that I had to bind form data to some kind of model to be persisted in the database. Vapor actually handles that very well. The main part that I struggled with was rendering HTML code in a way that I was actually happy with.

Templating Engines

When I was a baby PHP developer all those years ago, templating engines such as Twig were the go to solution to render HTML. It was fast, extensible and allowed you to properly separate the views, from the models, from the controllers. Vapor does support a similar tool named Leaf:

#if(1 + 1 == 2):
    Hello!
#endif

#if(index % 2 == 0):
    This is even index.
#else:
    This is odd index.
#endif

I don’t have much experience with Leaf, because I have since then moved away from text-based templating languages in favour of a Domain Specific Language (DSL) approach.[^1] In case you’re unfamiliar with DSL, it means that instead of writing HTML and using custom tags to render the dynamic part of your layout, you’re actually writing pure Swift code (or Kotlin, or whatever language it is you’re using). I like that… assuming the tooling around it is good.

Section {
    Img(src: "./images/swift.png", alt: "Swift Logo")
        .title("Picture of the Swift Logo")
    H1("Lorem ipsum")
        .class("red")
    P("Lorem ipsum dolor sit amet, consectetur adipiscing elit.")
        .class(["green", "blue"])
        .spellcheck(false)
}

For Comics Outmash, I took a look at available solutions on the Swift Package Index and went for SwiftHtml, which did the job. I did run into some very frustrating limitations such as Xcode’s autocomplete being less than reliable and the Swift compiler complaining about how the expressions were too complex and had to be broken down.

Additionally, after putting the project aside for a little while and trying to update my dependencies (as I like doing), the project simply stopped building. I considered moving to a different solution (such as Plot) but spending time to rewrite my view layer wasn’t a very appealing use of my limited time. At this point, I had given up on the project and could not find the motivation to pick it up again, no matter how much I usually enjoy fixing projects and updating dependencies.

Introducing Kotlin

In 2017 I was making iOS apps for a company based in Montreal. My colleagues were using pure Java to write Android apps, but I’d heard about Kotlin, Java’s cooler cousin. Later, as part of my role as Developer Evangelist at Buddybuild, I had started to work on an Android application written in Kotlin that used our API and was distributed with buddybuild. I didn’t spend a whole lot of time on this project, but it was long enough to understand the appeal for people coming from Java. Kotlin had generics, nullability, value types[^2], some cool syntax stuff like conditional expression (allowing you to store the result of the evaluation of an if, or when, the equivalent of Swift’s switch) and of course, proper support for asynchronous code.

I didn’t pick up Kotlin until it was time to start working on Xcode Cloud a year or two later. Over the following 6 years, I ended up writing a lot of it and spent a significant amount of time in IntelliJ, Jetbrains’ IDE.

Of course, like with most languages and technologies, I ended up developing a love/hate relationship with Kotlin. I felt that for a Java replacement, you could still feel its presence pretty strongly. For example, when your testing code ended up crashing with a NullPointerException while trying to evaluate the content of a non-nullable variable, due to incomplete mocking[^3] of some objects. Stack traces would report failures on lines in the transpiled java code, instead of the Kotlin one, making it really hard to pinpoint a failure. Fortunately, the language kept evolving, making those frustrations less and less frequents. Not that I couldn’t find new reasons to complain, mind you.

When enough time had passed and I felt like picking up Comics Outmash up again, I saw this as an opportunity to experiment with newer tools yet more mature tools. I keep a Craft document with a list of technologies that picked my interest in the past. I put down Swift, Vapor, and Xcode and instead picked up Kotlin, Ktor (a pure Kotlin framework to make web apps) and IntelliJ.

3 weeks of Ktor later

I can say that the experience as been a bliss so far. Ktor is fun to work with, especially when coming from something a little heavier like Vert.x, which we’d be using at work. If you’ve used any frameworks such as Vapor or Express, you’ll feel right at home (assuming you live in a framework, I guess).

fun Application.configure() {
    routing {
        get("/hello") {
            call.respondText("Hello World!")
        }
    }
}

Ktor uses a DSL to configure routing, security and so on. I got a little lost at the beginning when it asked me to select an HTTP engine (why should I care about this?), but the documentation is quite thorough and Kotlin itself is mature, so I didn’t have to search solutions to my problem for long.

As a bonus, while I don’t like IntelliJ’s non native UI, supports for plugins and an autocomplete you can truly rely on[^4] put me on Cloud 9 as I blazed through implementing my tiny project.

Conclusion

This post is not a diss on Swift, which I still enjoy very much, as you can see on the recent work I’ve done on my tiny command line tool, Agamotto. While I enjoy the language, I think it’s also important to acknowledge the shortcomings of the ecosystem such as unreliable autocomplete, cryptic build failures in deeply buried C libraries and slow build times.

I’m not saying that Kotlin is perfect, but I definitely feel that switching to a different technology was the right decision for me, at a time when I was trying to work on something fun.

[^1] I’m a little more on the fence nowadays. [^2] Which Java now supports as well. [^3] Don’t get me started on mocking. I will ruin your afternoon and we’ll both end up crying. The short story is that I hate mocking, but it wasn’t up to me. [^4] Especially when rendering templates with kotlinx.html