Getting Started with Kotlin on iOS, Part 1

09 Feb 2020 - 6 minute read Updated: 11 Feb 2019

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:

struct Dog: Equatable {
  let name: String
}

In Kotlin, the tool we reach for here is a data class, which looks like this:

data class Dog(val name: String)

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 data class, equals and hashCode are generated for you as well. To get this with a struct, you make sure to declare it as Equatable (also gets you Hashable).

Let’s expand on this with a protocol:

protocol Pet {
  var ownerName: String { get }
}

In Kotlin, this is an interface:

interface Pet {
  val ownerName: String
}

Let’s implement it. First, in Swift:

struct Dog: Equatable, Pet {
  let name: String
  let ownerName: String
}

And in Kotlin:

data class Dog(val name: String, override val ownerName: String): Pet

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:

  • val/var and let/var for declaring variables
  • interface and protocol for defining methods and properties that a type must implement
  • fun foo(arg: String) and func foo(arg: String) for defining a function called “foo”
  • class and class

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?

Singletons

In Kotlin, singletons are declared using the object keyword, and all members declared within are accessed on the type directly.

object Platform {
   val name = "iOS"

   fun printName() = println(name)
}

In this example, I’ve declared an object called Platform. This acts as a singleton, and we access its sole property via Platform.name and call its sole function via Platform.printName().

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.

data class Dog(val name: String) {
    companion object {
        // they're all good dogs
        val isGood = true
    }
}

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 true 🐶.

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).

Their behavior here turns out to be the same 🎉. I made a mistake understanding the concurrency rules and reading too much into this line in the immutability docs:

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.

Multiplatform

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.

Questions or comments? Find me on twitter.

Find a mistake or inaccuracy? Please open an issue or PR on github.