[00:00.000 --> 00:10.120] Hello, everyone. Hello again. So we are going to resume. If you are in the back, please come [00:10.120 --> 00:16.280] in. We have seats in the front. And please make sure to make yourself comfortable for [00:16.280 --> 00:21.200] this next talk, where I have the pleasure to introduce Russell. So Russell is coming [00:21.200 --> 00:26.040] all the way from the USA to be with us today. He works at Touch Labs, knows everything about [00:26.040 --> 00:30.680] Kotlin multi-platform. And today he will tell us about from Hello World to the real world. [00:30.680 --> 00:40.200] Yeah, thanks a lot. Yeah, I'm here from Boston, where it's actually like negative 24 degrees [00:40.200 --> 00:48.600] Celsius today. So I'm all right with the rain. And yeah, my name is Russell Wolf. I'm a Google [00:48.600 --> 00:54.800] developer expert for Kotlin. And I work at Touch Lab, where we do all Kotlin multi-platform all [00:54.800 --> 01:02.000] the time. So I'm going to talk a little bit about taking a Kotlin multi-platform from like the [01:02.000 --> 01:05.960] basic kind of Hello World example that you might start out with to the sorts of things that you [01:05.960 --> 01:14.600] might need for a production hyalab. And I've been part of work at Touch Lab. I've been doing [01:14.600 --> 01:19.320] Kotlin multi-platform things since pretty much day one. In a couple weeks, it's going to be five [01:19.320 --> 01:24.520] years since I wrote my first Kotlin multi-platform code, which is kind of cool. I don't know. It [01:24.520 --> 01:27.280] hopefully means I know something about what I'm talking about, but you can let me know. [01:27.280 --> 01:40.520] So let's get started. So quick introduction to Kotlin multi-platform. And I'm kind of breaking [01:40.520 --> 01:47.560] up my talk, breaking up the talk by the sections of the title. So started with Kotlin multi-platform. [01:47.560 --> 01:59.880] So Kotlin grew up initially as a JVM language, but it also has backends on JavaScript and [01:59.880 --> 02:06.400] native. And all of these are actually kind of families of platforms where JVM includes Android, [02:06.400 --> 02:14.320] native includes all sorts of different targets. And Kotlin multi-platform adds not just the [02:14.320 --> 02:20.360] targets themselves, but the ability to share code between them. So you have platform code [02:20.360 --> 02:28.600] that runs on a particular target, and then you have common code that runs on all of them, [02:28.600 --> 02:37.320] or runs on combinations of them. And what this enables is a really nice kind of flexible interop [02:37.320 --> 02:45.000] where you can share the post of your code that makes sense to share, but you have the ability [02:45.000 --> 02:49.800] to drop into platform specific code for things that you don't want to share. And it lets you [02:49.800 --> 02:56.040] treat your shared code as basically just another library. So you can be writing what would [02:56.040 --> 03:06.400] like an otherwise fully native app and share just a piece of it. Say a couple words on [03:06.400 --> 03:12.720] KMP versus KMM. So KMP is Kotlin multi-platform is the kind of whole universe of all the different [03:12.720 --> 03:19.760] things that call it in the target. KMM is Kotlin multi-platform mobile, which is the mobile part [03:19.760 --> 03:25.920] of that story, which is the first piece that JetBrains is stabilizing. So that's the thing [03:25.920 --> 03:31.120] that they announced is in beta. There's not really a hard technical line between them, [03:31.120 --> 03:37.960] because KMM in the end is just like parts of KMP. It's just kind of working on the same technology [03:37.960 --> 03:45.680] stack. But in terms of what they're focused on for the developer experience, KMM is kind of [03:45.680 --> 03:52.800] the piece that's coming first. And yeah, as I mentioned before, it's recently moved into beta. [03:52.800 --> 04:01.720] It's planned to go stable this year. So it's a really good time to get into it, start using it, [04:01.720 --> 04:10.640] if you haven't yet. And what beta means to JetBrains can be a little bit different than you [04:10.640 --> 04:16.800] might be used to from other projects. They're very slow about designated things as stable. [04:16.800 --> 04:20.520] They want to be absolutely sure of every little detail. But even by calling it beta, [04:20.520 --> 04:25.880] they're very strongly committed to keeping things working. They're just saying there might be some [04:25.880 --> 04:33.760] breaking changes in the future. And to kind of break down how call-on-mobile platform code works, [04:33.760 --> 04:40.440] I like to use this kind of Venn diagram and focus on the mobile use case. So we're talking about [04:40.440 --> 04:47.000] Android and iOS. So if you're an Android developer, you're used to kind of this type of [04:47.000 --> 04:53.920] diagram. You have access to kind of all the Kotlin APIs you're used to. You have access to [04:53.920 --> 05:02.600] JVM and Android APIs. And there's a subset of that, just the kind of pure Kotlin stuff, [05:02.600 --> 05:08.680] that in principle you can run on any platform. Which then means you can take that over to the [05:08.680 --> 05:22.280] iOS side, also add some iOS platform specific code. And then, so you have kind of your shared bits [05:22.280 --> 05:30.480] and your platform bits. And the KMP tool chain brings all of that together. So that essentially [05:30.480 --> 05:35.840] each of these different colors on the diagram are just a different source directory. And the [05:35.840 --> 05:40.120] tool chain knows how to kind of put the right parts together so that you get the right code for [05:40.120 --> 05:49.440] your platform. And again, there's more to KMP than just KMM, but the 8-way Venn diagram of [05:49.440 --> 05:56.640] everything is a lot more complicated to draw. So what does it look like when you're writing your [05:56.640 --> 06:06.480] first Hello Worlds in Kotlin Multiclapform? And one way to get that is to start with the [06:06.480 --> 06:15.240] Kotlin Multiclapform mobile plugin for Android Studio. So you can do a lot of the stuff. I tend [06:15.240 --> 06:22.760] to use IntelliJ IDEA more than Android Studio when I'm doing my KMP development. But the new [06:22.760 --> 06:28.080] project template in Android Studio is a little bit easier to get started with. They have these [06:28.080 --> 06:38.240] Kotlin Multiclapform application, or Kotlin Multiclapform library. And what they give you is [06:38.240 --> 06:42.240] some code that looks kind of like this. And don't worry about kind of every little detail of it. [06:42.240 --> 06:52.280] But this is kind of like the Hello World template that it generates for you. So there's a platform [06:52.280 --> 06:56.080] interface in the common code. The common code is in the center here. The Android is on the left. [06:56.080 --> 07:03.400] The iOS is on the right. So there's a platform interface that's implemented on each platform [07:03.400 --> 07:11.160] as Android platform and iOS platform. There's a expect function. So expect an actual or two [07:11.160 --> 07:18.360] keywords that Kotlin Multiclapform adds to the language that essentially let you declare something [07:18.360 --> 07:26.520] in your common code, but implement it in your platform code. Essentially kind of like a header. [07:26.520 --> 07:33.880] Actually, they use the header keyword for it before Kotlin Multiclapform was released in 2017, [07:33.880 --> 07:44.000] 2018-ish. So there's an expect function that you kind of get a default platform that has [07:44.000 --> 07:48.800] actual implementations on each platform. And then there's a greeting class that just kind of brings [07:48.800 --> 07:53.800] it all together and prints the name of the platform that you're on. And this gives you a [07:53.800 --> 08:02.880] little playground to start messing around with Kotlin Multiclapform code. And I actually really [08:02.880 --> 08:09.720] like there's, I like the way that they use expect actual in here. It's very easy when you have this [08:09.720 --> 08:13.840] new tool starting out with Kotlin Multiclapform to kind of overuse it. I mean, you start making [08:13.840 --> 08:21.200] all these expect classes and things like that. I tend to find it's really nice to also use, [08:21.200 --> 08:26.760] like, hold on to interfaces as well. So when you define an interface platform rather than an expect [08:26.760 --> 08:37.640] class platform, you can substitute other implementations a lot more easily. And so this is kind [08:37.640 --> 08:44.240] of like a rough sense of the code structure that you get from this template. So the code that I [08:44.240 --> 08:52.520] showed you is kind of this bottom three boxes. So there's common code in the middle, the orange, [08:52.520 --> 08:59.680] there's Android sources in that that you then compile to an Android library. There's iOS sources [08:59.680 --> 09:07.600] that you then compile to a iOS framework file. And then if you use one of the application templates, [09:07.600 --> 09:17.680] it'll look at the app layer that consumes that. There's multiple ways that you can configure the [09:17.680 --> 09:22.960] iOS app to consume it. So you can, which essentially there's different dependency managers that you [09:22.960 --> 09:33.520] can use in iOS. There's a default that's just kind of manually include the framework. You kind of [09:33.520 --> 09:42.040] add a custom build step into Xcode that will call into Kotlin and do that. There's also a plug-in [09:42.040 --> 09:50.920] that's part of the Kotlin tool chain that uses Cocopods. So Cocopods is kind of, has historically [09:50.920 --> 09:57.640] been a commonly used dependency manager on iOS. It's these days starting to be replaced by Swift [09:57.640 --> 10:03.360] package manager, but the Kotlin tool chain doesn't have as good of integration into SPM yet. [10:03.360 --> 10:18.000] And then I'll also just call out, we at TouchLab have a sample called CampKit that can also be a [10:18.000 --> 10:25.280] nice kind of place to start out if you're playing this stuff for the first time. It's a somewhat [10:25.280 --> 10:31.960] more complicated sample than that Hello World. It has a bit more kind of architecture to it and [10:31.960 --> 10:38.400] shows kind of some of our standard architecture and library practices. And also has a bunch of [10:38.400 --> 10:44.360] documentation kind of explaining why we make some of the choices that we do. So check that out [10:44.360 --> 10:52.080] also if you're interested. So what are kind of some common themes around these sorts of starter [10:52.080 --> 10:59.320] projects? And there's a lot more than just those two, I should say also. There's lots of people [10:59.320 --> 11:04.520] that have kind of put together interesting multiple from samples that you can use when you're [11:04.520 --> 11:12.200] first learning. And something that comes up often in a lot of them is they tend to aim at [11:12.200 --> 11:22.360] maximizing shared code, which like in an ideal world is really nice. In the real world, oftentimes [11:22.360 --> 11:26.800] you're starting from two separate native apps and you want to incrementally move towards more [11:26.800 --> 11:32.520] shared. And you don't always get a good sense of what that looks like from any of the standard [11:32.520 --> 11:43.200] samples. Things also tend to be mono repos when you're looking at starter samples. So what if I [11:43.200 --> 11:46.440] already have existing apps? They live in different places, but I want to start sharing code [11:46.440 --> 11:54.280] between them. What does that look like? A big piece of a lot of them is there will be some [11:54.280 --> 12:01.240] step in your build process where in your build process in iOS where Xcode has to manually call [12:01.240 --> 12:08.320] into Gradle to build your Kotlin. But if you're on a larger team, you might not want to have to do [12:08.320 --> 12:15.960] that every time. Your iOS team might not even have a JDK set up if they're not used to using [12:15.960 --> 12:28.200] that. So what do you do in that case? And they also tend to be single module when you're looking [12:28.200 --> 12:39.280] at sample projects. But what happens when things get bigger? So that brings us to what does it [12:39.280 --> 12:43.280] look like when you take all of these sorts of things and start scaling it up to real-world [12:43.280 --> 12:52.360] projects? And I'm going to talk about some of the ways that we tend to think about this at [12:52.360 --> 12:59.960] touch lab as well as some tools and things that we've put out into the community to help out with [12:59.960 --> 13:07.440] some of these things. And the first thing I want to talk about is team structure. This is [13:07.440 --> 13:11.840] something we've been talking about a lot internally at touch lab recently and kind of [13:11.840 --> 13:18.880] building out this sort of taxonomy of different ways that different teams approach the way that [13:18.880 --> 13:28.880] they handle their shared code. And a common core piece of that is being thoughtful about the [13:28.880 --> 13:33.000] ways that the structure of your team impacts the way that you want to organize your code. [13:33.000 --> 13:45.200] Because lots of teams are very different. So the distinction I'll highlight here and it [13:45.200 --> 13:50.400] kind of works across a couple of different dimensions. I tend to think of it as kind of [13:50.400 --> 13:57.440] small teams versus large teams. But it's also sometimes teams that work kind of as one unit [13:57.440 --> 14:06.480] versus teams that work as multiple units. And a key piece of that is often is the group that [14:06.480 --> 14:10.560] is writing the shared code the same group as the people who are consuming the shared code. [14:10.560 --> 14:18.320] So when you're a smaller team or if you're one unit you tend to have kind of fewer worries [14:18.320 --> 14:24.320] about who's owning what parts of the code. You're more kind of unified in what your developer [14:24.320 --> 14:30.000] setup looks like. And you're more likely to be in a situation where you're kind of sharing [14:30.000 --> 14:35.360] a higher percentage of things and just kind of wrapping a thing UI around it. And you're [14:35.360 --> 14:39.760] more likely to be doing all of your feature development kind of at once for both platforms. [14:39.760 --> 14:47.680] On the other hand, when teams get larger, things get a little bit messier. You're more [14:47.680 --> 14:53.920] likely to have iOS specialists who don't want to kind of deal with the Kotlin directly. [14:56.400 --> 15:03.120] And your Kotlin code, you're more likely to have a larger iOS app than just what the Kotlin is. [15:03.120 --> 15:10.000] And so you might, like your Kotlin is just kind of one more thing in a sea of other native libraries [15:10.000 --> 15:16.880] that your iOS app is using. And you tend to want to minimize the impact of your Kotlin [15:17.920 --> 15:26.000] on the rest of the iOS code. And what the topic means in practice is you want to kind of rather [15:26.000 --> 15:37.520] than linking your XO build to your Kotlin directly, you want to kind of publish it as an external [15:37.520 --> 15:45.280] library. And so the diagram that's why I showed you that kind of diagram on the left earlier, [15:46.080 --> 15:50.160] the way it can look like in a larger team is rather than directly consuming things, [15:50.160 --> 15:57.360] your shared code is being published to some sort of artifact repository, and then your apps are [15:57.360 --> 16:01.760] pulling that artifact down. And there's kind of more of a two-step process to making updates, [16:02.960 --> 16:05.760] but it lets you kind of work in separate streams more easily. [16:10.560 --> 16:19.760] And we put out a tool to help with this in the fall. We touch lab. It's called KMM bridge, [16:19.760 --> 16:26.720] and it's a Gradle plugin that can essentially manage the publishing of your iOS framework [16:26.720 --> 16:30.880] in a couple different ways. So it gives you a Gradle task to publish a new version when you've [16:30.880 --> 16:35.600] made changes. It has options around how you implement that version and things like that, [16:36.240 --> 16:39.600] options for where you want to host that binary and the ability to plug in your own. [16:41.920 --> 16:48.320] And then some helpers, if you're using a package manager for making that local development flow [16:48.320 --> 16:56.480] a little bit easier. So sometimes you want to be able to toggle between using the binary [16:56.480 --> 17:01.200] that you pulled down versus building it directly when you're trying to write new code or debug it. [17:02.960 --> 17:07.200] So we have some helpers to make that flow a little bit easier. There's a bunch of little [17:07.200 --> 17:11.280] things that are still kind of a work in progress on here. If you're a team that's [17:12.640 --> 17:15.600] interested in using it, we'd love to talk to you and get some feedback. [17:15.600 --> 17:20.080] So feel free to find me and let me know if you want to learn more about that. [17:22.640 --> 17:29.520] Another problem that comes up at scale is modularization. So when you write a hello world, [17:29.520 --> 17:34.400] it tends to be one module. But when you're writing bigger things, you might want to have more than [17:34.400 --> 17:40.480] one. And Kotlin native, it turns out, makes us a little bit complicated. So when you have multiple [17:40.480 --> 17:48.720] Kotlin native modules and you export them to iOS, they're essentially their own kind of [17:48.720 --> 17:54.560] separate worlds. And so each of these modules has its own copy of any internal dependencies, [17:54.560 --> 17:58.960] their own copy of the standard library, their own copy of any third module that you might have [17:58.960 --> 18:02.320] underneath them that you're trying to share between them. And they can't kind of talk between [18:02.320 --> 18:10.880] each other very easily. And this can be okay if they're doing very distinct things. So maybe one [18:10.880 --> 18:16.160] of them is making analyst calls and one of them is running your database and they don't really need [18:16.160 --> 18:24.320] to interact with each other. And then having them separate can be okay. But often you end up wanting [18:24.320 --> 18:30.720] to kind of write this umbrella module on top of them so that in your Kotlin layer, you can have [18:30.720 --> 18:35.600] them talk to each other more easily. And then you have sort of a shared module on top that you [18:35.600 --> 18:46.720] export as your iOS framework. And that lets you more easily have that more typical kind of modular [18:46.720 --> 18:53.600] structure while working with the Kotlin native limitations. There's still some messiness to [18:53.600 --> 19:01.920] this because your umbrella framework will have, you and I have kind of namespace clashes where [19:01.920 --> 19:06.880] all of your declarations in here are essentially in one giant global namespace. [19:08.320 --> 19:14.640] And there's romantic that will improve this. But right now it can be a little messy when you have [19:14.640 --> 19:27.920] a lot of code in there. Another thing that comes up in real-world projects is your binary size. [19:29.040 --> 19:34.320] So hello world tends to be small, real apps tend to be larger. And real apps have consequences [19:34.320 --> 19:41.840] when things are too large. Where things like the app store will throttle your downloading [19:41.840 --> 19:45.520] or force you to do it on Wi-Fi rather than on mobile if your app gets too big. And this [19:45.520 --> 19:52.480] can be a significant impact to the amount of downloads that you get. And it turns out [19:53.360 --> 19:58.800] one of the biggest contributors to this is the Object2C interface that Kotlin native uses [19:59.600 --> 20:08.320] to export your code to iOS. And the kind of trick to use here is you want to limit the amount of [20:08.320 --> 20:15.440] public decorations that you have in your Kotlin code. And that will shrink that Object2C interface [20:15.440 --> 20:20.960] because it only needs to be generated for public decorations. And that hitter from Object2C [20:21.920 --> 20:28.080] annotation that Marco mentioned earlier can also be a way to do that. Or there's kind of [20:28.080 --> 20:39.200] different monitoring structures you can sometimes use. So I'll mention quickly a couple other tools [20:39.200 --> 20:44.560] that Tesla puts out that can be helpful when you're running loads of apps. So by default, [20:44.560 --> 20:52.320] the crash reporting that you get out of Kotlin native doesn't kind of export to Swift very well. [20:52.320 --> 20:55.600] So we have this tool called Crash Kios that will essentially [20:55.600 --> 21:03.200] symbolicate your stack traces better. We have some updates to that in-flight that will kind of [21:03.200 --> 21:06.560] clean up different pieces of that story, but I'm not going to go into detail there because I'm [21:06.560 --> 21:14.400] getting kind of low on time. And we also have a Xcode debugger that lets you debug your Kotlin [21:14.400 --> 21:19.840] code from Xcode, which can be a nicer environment for your iOS developers when you're kind of [21:19.840 --> 21:28.880] introducing that. That recently to be a CLI-based interface, which makes it much easier to update [21:28.880 --> 21:32.640] and install. So if you've tried it out in the past, feel free to give it another look. [21:36.080 --> 21:43.520] One of the things I want to talk about is kind of the shape of your API service. So [21:43.520 --> 21:49.120] Hello World apps tend to be small, but as your app gets bigger, you start to care more and more [21:49.120 --> 21:56.960] about what, sort of, how idiomatic is your API? And Swift and Kotlin tend to want to [21:56.960 --> 22:05.360] eat different things with that. So I just want to point out, like, don't be afraid to kind of [22:06.080 --> 22:10.240] need a bit of translation layer between your shared code and your platform code. [22:10.240 --> 22:16.160] And we have some tooling that we're working on to make some of that easier, but it's not in [22:16.160 --> 22:22.480] the open yet, so I'm not going to go into detail there. I'll skip the example because I'm running [22:22.480 --> 22:31.200] out of time. But kind of the overall lesson that I want to highlight is different teams and have [22:31.200 --> 22:38.960] different structures and want slightly different things. Kotlin is all about adaptability. And so [22:38.960 --> 22:43.520] if you're a team that wants to share a lot of code, you can do that. If you're a team that [22:43.520 --> 22:47.680] wants to minimize the impact of the Kotlin on the rest of your domain team, you can do that. [22:49.680 --> 22:54.400] And you have the flexibility to kind of choose the way that you want to approach all of that. [22:56.720 --> 23:01.360] So thanks. I think I'm probably out of time for questions, but I'm definitely happy to [23:01.360 --> 23:05.680] answer stuff in person. Feel free to tap me on the shoulder, find me whatever later today. [23:05.680 --> 23:11.040] If I'm sharing out my laptop, it's not important because it's Saturday, so I might love to chat. [23:11.040 --> 23:36.400] Thanks.