Hello, everyone. Hello again. So we are going to resume. If you are in the back, please come
in. We have seats in the front. And please make sure to make yourself comfortable for
this next talk, where I have the pleasure to introduce Russell. So Russell is coming
all the way from the USA to be with us today. He works at Touch Labs, knows everything about
Kotlin multi-platform. And today he will tell us about from Hello World to the real world.
Yeah, thanks a lot. Yeah, I'm here from Boston, where it's actually like negative 24 degrees
Celsius today. So I'm all right with the rain. And yeah, my name is Russell Wolf. I'm a Google
developer expert for Kotlin. And I work at Touch Lab, where we do all Kotlin multi-platform all
the time. So I'm going to talk a little bit about taking a Kotlin multi-platform from like the
basic kind of Hello World example that you might start out with to the sorts of things that you
might need for a production hyalab. And I've been part of work at Touch Lab. I've been doing
Kotlin multi-platform things since pretty much day one. In a couple weeks, it's going to be five
years since I wrote my first Kotlin multi-platform code, which is kind of cool. I don't know. It
hopefully means I know something about what I'm talking about, but you can let me know.
So let's get started. So quick introduction to Kotlin multi-platform. And I'm kind of breaking
up my talk, breaking up the talk by the sections of the title. So started with Kotlin multi-platform.
So Kotlin grew up initially as a JVM language, but it also has backends on JavaScript and
native. And all of these are actually kind of families of platforms where JVM includes Android,
native includes all sorts of different targets. And Kotlin multi-platform adds not just the
targets themselves, but the ability to share code between them. So you have platform code
that runs on a particular target, and then you have common code that runs on all of them,
or runs on combinations of them. And what this enables is a really nice kind of flexible interop
where you can share the post of your code that makes sense to share, but you have the ability
to drop into platform specific code for things that you don't want to share. And it lets you
treat your shared code as basically just another library. So you can be writing what would
like an otherwise fully native app and share just a piece of it. Say a couple words on
KMP versus KMM. So KMP is Kotlin multi-platform is the kind of whole universe of all the different
things that call it in the target. KMM is Kotlin multi-platform mobile, which is the mobile part
of that story, which is the first piece that JetBrains is stabilizing. So that's the thing
that they announced is in beta. There's not really a hard technical line between them,
because KMM in the end is just like parts of KMP. It's just kind of working on the same technology
stack. But in terms of what they're focused on for the developer experience, KMM is kind of
the piece that's coming first. And yeah, as I mentioned before, it's recently moved into beta.
It's planned to go stable this year. So it's a really good time to get into it, start using it,
if you haven't yet. And what beta means to JetBrains can be a little bit different than you
might be used to from other projects. They're very slow about designated things as stable.
They want to be absolutely sure of every little detail. But even by calling it beta,
they're very strongly committed to keeping things working. They're just saying there might be some
breaking changes in the future. And to kind of break down how call-on-mobile platform code works,
I like to use this kind of Venn diagram and focus on the mobile use case. So we're talking about
Android and iOS. So if you're an Android developer, you're used to kind of this type of
diagram. You have access to kind of all the Kotlin APIs you're used to. You have access to
JVM and Android APIs. And there's a subset of that, just the kind of pure Kotlin stuff,
that in principle you can run on any platform. Which then means you can take that over to the
iOS side, also add some iOS platform specific code. And then, so you have kind of your shared bits
and your platform bits. And the KMP tool chain brings all of that together. So that essentially
each of these different colors on the diagram are just a different source directory. And the
tool chain knows how to kind of put the right parts together so that you get the right code for
your platform. And again, there's more to KMP than just KMM, but the 8-way Venn diagram of
everything is a lot more complicated to draw. So what does it look like when you're writing your
first Hello Worlds in Kotlin Multiclapform? And one way to get that is to start with the
Kotlin Multiclapform mobile plugin for Android Studio. So you can do a lot of the stuff. I tend
to use IntelliJ IDEA more than Android Studio when I'm doing my KMP development. But the new
project template in Android Studio is a little bit easier to get started with. They have these
Kotlin Multiclapform application, or Kotlin Multiclapform library. And what they give you is
some code that looks kind of like this. And don't worry about kind of every little detail of it.
But this is kind of like the Hello World template that it generates for you. So there's a platform
interface in the common code. The common code is in the center here. The Android is on the left.
The iOS is on the right. So there's a platform interface that's implemented on each platform
as Android platform and iOS platform. There's a expect function. So expect an actual or two
keywords that Kotlin Multiclapform adds to the language that essentially let you declare something
in your common code, but implement it in your platform code. Essentially kind of like a header.
Actually, they use the header keyword for it before Kotlin Multiclapform was released in 2017,
2018-ish. So there's an expect function that you kind of get a default platform that has
actual implementations on each platform. And then there's a greeting class that just kind of brings
it all together and prints the name of the platform that you're on. And this gives you a
little playground to start messing around with Kotlin Multiclapform code. And I actually really
like there's, I like the way that they use expect actual in here. It's very easy when you have this
new tool starting out with Kotlin Multiclapform to kind of overuse it. I mean, you start making
all these expect classes and things like that. I tend to find it's really nice to also use,
like, hold on to interfaces as well. So when you define an interface platform rather than an expect
class platform, you can substitute other implementations a lot more easily. And so this is kind
of like a rough sense of the code structure that you get from this template. So the code that I
showed you is kind of this bottom three boxes. So there's common code in the middle, the orange,
there's Android sources in that that you then compile to an Android library. There's iOS sources
that you then compile to a iOS framework file. And then if you use one of the application templates,
it'll look at the app layer that consumes that. There's multiple ways that you can configure the
iOS app to consume it. So you can, which essentially there's different dependency managers that you
can use in iOS. There's a default that's just kind of manually include the framework. You kind of
add a custom build step into Xcode that will call into Kotlin and do that. There's also a plug-in
that's part of the Kotlin tool chain that uses Cocopods. So Cocopods is kind of, has historically
been a commonly used dependency manager on iOS. It's these days starting to be replaced by Swift
package manager, but the Kotlin tool chain doesn't have as good of integration into SPM yet.
And then I'll also just call out, we at TouchLab have a sample called CampKit that can also be a
nice kind of place to start out if you're playing this stuff for the first time. It's a somewhat
more complicated sample than that Hello World. It has a bit more kind of architecture to it and
shows kind of some of our standard architecture and library practices. And also has a bunch of
documentation kind of explaining why we make some of the choices that we do. So check that out
also if you're interested. So what are kind of some common themes around these sorts of starter
projects? And there's a lot more than just those two, I should say also. There's lots of people
that have kind of put together interesting multiple from samples that you can use when you're
first learning. And something that comes up often in a lot of them is they tend to aim at
maximizing shared code, which like in an ideal world is really nice. In the real world, oftentimes
you're starting from two separate native apps and you want to incrementally move towards more
shared. And you don't always get a good sense of what that looks like from any of the standard
samples. Things also tend to be mono repos when you're looking at starter samples. So what if I
already have existing apps? They live in different places, but I want to start sharing code
between them. What does that look like? A big piece of a lot of them is there will be some
step in your build process where in your build process in iOS where Xcode has to manually call
into Gradle to build your Kotlin. But if you're on a larger team, you might not want to have to do
that every time. Your iOS team might not even have a JDK set up if they're not used to using
that. So what do you do in that case? And they also tend to be single module when you're looking
at sample projects. But what happens when things get bigger? So that brings us to what does it
look like when you take all of these sorts of things and start scaling it up to real-world
projects? And I'm going to talk about some of the ways that we tend to think about this at
touch lab as well as some tools and things that we've put out into the community to help out with
some of these things. And the first thing I want to talk about is team structure. This is
something we've been talking about a lot internally at touch lab recently and kind of
building out this sort of taxonomy of different ways that different teams approach the way that
they handle their shared code. And a common core piece of that is being thoughtful about the
ways that the structure of your team impacts the way that you want to organize your code.
Because lots of teams are very different. So the distinction I'll highlight here and it
kind of works across a couple of different dimensions. I tend to think of it as kind of
small teams versus large teams. But it's also sometimes teams that work kind of as one unit
versus teams that work as multiple units. And a key piece of that is often is the group that
is writing the shared code the same group as the people who are consuming the shared code.
So when you're a smaller team or if you're one unit you tend to have kind of fewer worries
about who's owning what parts of the code. You're more kind of unified in what your developer
setup looks like. And you're more likely to be in a situation where you're kind of sharing
a higher percentage of things and just kind of wrapping a thing UI around it. And you're
more likely to be doing all of your feature development kind of at once for both platforms.
On the other hand, when teams get larger, things get a little bit messier. You're more
likely to have iOS specialists who don't want to kind of deal with the Kotlin directly.
And your Kotlin code, you're more likely to have a larger iOS app than just what the Kotlin is.
And so you might, like your Kotlin is just kind of one more thing in a sea of other native libraries
that your iOS app is using. And you tend to want to minimize the impact of your Kotlin
on the rest of the iOS code. And what the topic means in practice is you want to kind of rather
than linking your XO build to your Kotlin directly, you want to kind of publish it as an external
library. And so the diagram that's why I showed you that kind of diagram on the left earlier,
the way it can look like in a larger team is rather than directly consuming things,
your shared code is being published to some sort of artifact repository, and then your apps are
pulling that artifact down. And there's kind of more of a two-step process to making updates,
but it lets you kind of work in separate streams more easily.
And we put out a tool to help with this in the fall. We touch lab. It's called KMM bridge,
and it's a Gradle plugin that can essentially manage the publishing of your iOS framework
in a couple different ways. So it gives you a Gradle task to publish a new version when you've
made changes. It has options around how you implement that version and things like that,
options for where you want to host that binary and the ability to plug in your own.
And then some helpers, if you're using a package manager for making that local development flow
a little bit easier. So sometimes you want to be able to toggle between using the binary
that you pulled down versus building it directly when you're trying to write new code or debug it.
So we have some helpers to make that flow a little bit easier. There's a bunch of little
things that are still kind of a work in progress on here. If you're a team that's
interested in using it, we'd love to talk to you and get some feedback.
So feel free to find me and let me know if you want to learn more about that.
Another problem that comes up at scale is modularization. So when you write a hello world,
it tends to be one module. But when you're writing bigger things, you might want to have more than
one. And Kotlin native, it turns out, makes us a little bit complicated. So when you have multiple
Kotlin native modules and you export them to iOS, they're essentially their own kind of
separate worlds. And so each of these modules has its own copy of any internal dependencies,
their own copy of the standard library, their own copy of any third module that you might have
underneath them that you're trying to share between them. And they can't kind of talk between
each other very easily. And this can be okay if they're doing very distinct things. So maybe one
of them is making analyst calls and one of them is running your database and they don't really need
to interact with each other. And then having them separate can be okay. But often you end up wanting
to kind of write this umbrella module on top of them so that in your Kotlin layer, you can have
them talk to each other more easily. And then you have sort of a shared module on top that you
export as your iOS framework. And that lets you more easily have that more typical kind of modular
structure while working with the Kotlin native limitations. There's still some messiness to
this because your umbrella framework will have, you and I have kind of namespace clashes where
all of your declarations in here are essentially in one giant global namespace.
And there's romantic that will improve this. But right now it can be a little messy when you have
a lot of code in there. Another thing that comes up in real-world projects is your binary size.
So hello world tends to be small, real apps tend to be larger. And real apps have consequences
when things are too large. Where things like the app store will throttle your downloading
or force you to do it on Wi-Fi rather than on mobile if your app gets too big. And this
can be a significant impact to the amount of downloads that you get. And it turns out
one of the biggest contributors to this is the Object2C interface that Kotlin native uses
to export your code to iOS. And the kind of trick to use here is you want to limit the amount of
public decorations that you have in your Kotlin code. And that will shrink that Object2C interface
because it only needs to be generated for public decorations. And that hitter from Object2C
annotation that Marco mentioned earlier can also be a way to do that. Or there's kind of
different monitoring structures you can sometimes use. So I'll mention quickly a couple other tools
that Tesla puts out that can be helpful when you're running loads of apps. So by default,
the crash reporting that you get out of Kotlin native doesn't kind of export to Swift very well.
So we have this tool called Crash Kios that will essentially
symbolicate your stack traces better. We have some updates to that in-flight that will kind of
clean up different pieces of that story, but I'm not going to go into detail there because I'm
getting kind of low on time. And we also have a Xcode debugger that lets you debug your Kotlin
code from Xcode, which can be a nicer environment for your iOS developers when you're kind of
introducing that. That recently to be a CLI-based interface, which makes it much easier to update
and install. So if you've tried it out in the past, feel free to give it another look.
One of the things I want to talk about is kind of the shape of your API service. So
Hello World apps tend to be small, but as your app gets bigger, you start to care more and more
about what, sort of, how idiomatic is your API? And Swift and Kotlin tend to want to
eat different things with that. So I just want to point out, like, don't be afraid to kind of
need a bit of translation layer between your shared code and your platform code.
And we have some tooling that we're working on to make some of that easier, but it's not in
the open yet, so I'm not going to go into detail there. I'll skip the example because I'm running
out of time. But kind of the overall lesson that I want to highlight is different teams and have
different structures and want slightly different things. Kotlin is all about adaptability. And so
if you're a team that wants to share a lot of code, you can do that. If you're a team that
wants to minimize the impact of the Kotlin on the rest of your domain team, you can do that.
And you have the flexibility to kind of choose the way that you want to approach all of that.
So thanks. I think I'm probably out of time for questions, but I'm definitely happy to
answer stuff in person. Feel free to tap me on the shoulder, find me whatever later today.
If I'm sharing out my laptop, it's not important because it's Saturday, so I might love to chat.
Thanks.