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 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
:
In Kotlin, this is an interface
:
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:
val
/var
andlet
/var
for declaring variablesinterface
andprotocol
for defining methods and properties that a type must implementfun foo(arg: String)
andfunc foo(arg: String)
for defining a function called “foo”class
andclass
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.
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.
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 object
s 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 object
s) 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 mastodon.social.
Find a mistake or inaccuracy? Please open an issue or PR on github.