09 Feb 2020 - 6 minute read Updated: 11 Feb 2020
Hello! I’m Ben. I’m an iOS engineer at Autodesk where I work on the PlanGrid app. PlanGrid is a construction productivity tool that works on iOS, Android, and even Windows 😱. The iOS version is written in Swift (some Obj-C too because it’s older), but now most of the code I write day-to-day for the iOS app is in Kotlin.
We started exploring how to share code between our native mobile platforms at the end of 2018, and we wrote a bit about that in a recent post here. After a year of learning how all of the various parts of Kotlin multiplatform fit together to make that project work, I felt motivated to make this blog to write about it.
To get started, I’m going to write a bit about Kotlin and Kotlin multiplatform from the perspective of someone (me) who came at this from iOS development. I’ll start with Kotlin and work my way through multiplatform.
From Swift to Kotlin
If I were starting an app from scratch today, it’s hard to say what language I would write it in. Swift is an impressive language with lots of useful modern features. But, if I also want to write this app for Android and am limited in the number of engineers (just me in this scenario), why would I set myself up to write the same app in two languages? JetBrains announced at KotlinConf 2019 that you’ll be able to build and iterate on an iOS and Android app simultaneously in Kotlin using Android Studio. If nothing else, it highlights what you can build on a foundation of great tooling. In this case, that foundation is Kotlin multiplatform.
A year ago, I hadn’t written any Kotlin. When Kotlin multiplatform became a possibility for us, I got my start with Kotlin Koans. This got me enough Kotlin knowledge to where I could write working code and test things out with iOS. Let’s walk through a quick comparative example between Kotlin and Swift.
Let’s say I want to represent a dog. In Swift, you might represent it with a
struct like this:
In Kotlin, the tool we reach for here is a
data class, which looks like this:
In one line, we’ve explicitly defined the type, its properties, and its constructor. With a
struct, you get this too (though such a one-liner is discouraged in Swift), but the constructor is implicit (generated by the compiler). With a
hashCode are generated for you as well. To get this with a
struct, you make sure to declare it as
Equatable (also gets you
Let’s expand on this with a
In Kotlin, this is an
Let’s implement it. First, in Swift:
And in Kotlin:
In Kotlin, we’re still at one line. I don’t know how others feel about ability to do this in one line, but as I’ve been writing more Kotlin, I’ve found it to be practical and concise. It’s one of the “Kotlin-y” things I’ve come to enjoy most, and I’ve found more and more of these “little things” as I’ve explored the language.
Coming from Swift
At this point, you may have noticed some similarities between the two languages, and there are many that you can lean on to help get started writing some Kotlin:
varfor declaring variables
protocolfor defining methods and properties that a type must implement
fun foo(arg: String)and
func foo(arg: String)for defining a function called “foo”
Coming from Swift, there were also tools I wanted to reach for when writing Kotlin that didn’t have such an exact match in Kotlin. For me, these all fell into the category of working with statics. The first thing to know is that there is no
static keyword like in Swift. Think about all of the places you might use
static in Swift. How do we do those things in Kotlin?
In Kotlin, singletons are declared using the
object keyword, and all members declared within are accessed on the type directly.
In this example, I’ve declared an
Platform. This acts as a singleton, and we access its sole property via
Platform.name and call its sole function via
Static Class Members
In Swift, we’re used to using
static func and
static let for type-scoped members. In Kotlin, we do this by extending the
object concept and declaring one within the
class using the
companion 🤝 keyword.
When I first saw this, my mind went to the C++
friend keyword 🙈, but all it does is provide us an area to define
static-level members for the
Dog class. In this case, we can now access
Dog.isGood, which will always return
Kotlin/Native and Objects
One thing to note about
objects when working with them in multiplatform contexts is that they behave in slightly different ways on native platforms like iOS (i.e. built using Kotlin/Native) compared to the JVM. On native platforms, globals (including
objects) are initialized during Kotlin/Native runtime initialization, which is typically when you launch your application. On the JVM, they’re initialized on access (lazily).
Top level/global variables of non-primitive types are by default accessible in the main thread (i.e., the thread which initialized Kotlin/Native runtime first) only.
I’ll discuss this a bit more in a future post.
I hope that if you’re coming from Swift, this gives you a sense of Kotlin as you’re getting started. Next, I’ll get into Kotlin multiplatform and how these Kotlin concepts bridge back to Swift/Obj-C.