 So yeah, I'm Russell. You can find me at Russ H. Wolf on Twitter, Slack, and I'm going to be starting next week at Touch Lab doing multi-platform stuff. So, we've just had a couple of talks on multi-platform, so I'm just going to do kind of a quick intro with some of my thoughts. But in brief, the idea of multi-platform is to take common code and compile it to multiple targets, so that being things like JVM, JavaScript, Android, et cetera, et cetera, et cetera. And in addition to that, shared common code use platform-specific code to talk to any of the, any of the individual platform APIs. And the general idea here is making it easy to do the custom things when you have to but share the things that don't need to be customized per platform. So the tool is to let you do that. The first one, er, sorry, yeah. So what does common code look like? So it looks pretty much like any Kotlin code you write. So from common, you have access to most of the standard library that you use to minus some platform-specific things. You can create classes, functions. You have things like the collections API. And when common isn't quite good enough, there's various strategies for platform-specific code to be able to kind of create whichever implementation you need to interact with that platform. So you have things like expecting an actual. So you can say, I expect some string that's gonna be the platform, which you might call Android on Android and you might call iOS on iOS. It's a very useful tool. You don't have to use it. So you could also just define an interface in your common and have implementations on it for it on each of the platforms. And a neat advantage to this is your implementations, like your implementations don't necessarily have to live in Kotlin. So maybe on the iOS side, for example, I want to have a Swift implementation of something that's defined in common. If it's an interface, you can still do that. And you have the ability to have multiple implementations. So expect an actual, the compiler enforces that they are one-to-one per platform. So there's exactly one actual declaration for every expect declaration. If you wanted to be able to do things like substitute mocks for testing, interfaces are a lot more flexible. So that's kind of a brief overview of multi-platform. How did I get into this stuff? So I did a project in React Native a while ago, about three years ago. So my day job, I mobile consultant, I build apps for different clients and usually do a native Android, but we wanted to try out React Native and see how we felt about it. I wasn't a very big fan, but it got me thinking a lot about what would I like in a multi-platform framework, or in a cross-platform framework. And around that time, that was around when the very, very first version of Kotlin Native came out. So that was before you could share anything to it, but you could kind of see what JetBrains was starting to build towards. So I was watching that out of the corner of my eye for the next kind of year or so. And one day this happened. So in version 0.6 of Kotlin Native, they added multi-platform support to it. So this was finally the moment that if you'd been kind of using Kotlin on the GM for a while, you could start to experiment with sharing some of that code to the native platforms. So I started as I went out a little bit. I did a couple of little example apps. And a couple of months later, the next Kotlin version came out. Had a bunch of updates around the native threading model and stuff that I won't go into in too much detail. But there was this neat note deep in the change log. And sorry, you can't read it, but exactly what I'm saying is use the Gradle native dependency model to be able to publish on K-Libs as native artifacts. So to translate that, it's saying Kotlin native now had the ability to use the same kind of native dependency framework that you're used to on JVM Kotlin for native stuff, which means if you want to do something like write a library that runs on native, you have to be able to do that now. So I did. So I took some of the code that I'd been playing with in one of the sample projects that was interacting with the shared preferences API on Android. And I extracted out this little subject library called multi-platform settings. So what it does is key value storage based on native platform APIs. So initially that was just Android and iOS. I've since added a couple of other platforms. And in addition to the kind of like raw wrapping of the platform APIs, there's also some nice Kotlin syntax helpers on top of it. So there's some operators and property delegates that let you get this, that kind of nicer enumatic Kotlin syntax if that's the style you prefer. So it's available there on GitHub if you're interested in looking at it. And let's take a quick walkthrough kind of like the restructure of what it is. So the core of it is this interface called settings that I'm just kind of using. One, not an example, but it has a bunch of getters and setters for different data types. And it has different implementations on each of the platforms. So there's a Android settings which wraps the typical Android key value API which is called shared preferences. There's an Apple settings which wraps the user defaults API which is essentially the equivalent on iOS. Over time I've added more platforms. So there's a JS settings which wraps local storage and JavaScript. And there's other pure JVM implementations. So this one as an example wraps the Java preferences API. And I was talking a little bit about mock things earlier so I also include a mock settings. So that means if you're kind of writing application code that interacts with this library and you wanna be able to write tests around it, you can use this mock settings implementation which just has a in-memory map and you don't have to worry about serializing your actual data to disk in your tests. And then yeah, there's some other operators and delegates and stuff to add that measures into X. So that's kind of the brief rundown of what the library looks like. So it didn't all start that way. So I'll tell you a couple of stories of kind of things that I've learned along the way. So it's been about a year and a half since I first put that out a little longer now. And yeah, I've learned a couple of things. So one of the big lessons that I think is applicable to a lot of people is kind of first start in multi-platform is the use of expecting actual versus the use of interfaces. So when you get a brand new tool, like you get a brand new hammer and everything looks like a nail. And the new tool in multi-platform is expecting actual. So the first version of settings settings was an expect class. Cause like that seems like what you do in multi-platform, right? And that was fine. Like it works perfectly well. But you like expecting actual is one to one. And so there's kind of no ability for consumers of that to be able to kind of supply an alternate limitation for things like testing or for other platforms or stuff like that. So eventually I kind of pulled out a interface on top of it to do that. But I still left the expect actual in there. I don't remember exactly why I did that. It's not kind of messy. There wasn't really any point to it. So it actually got to the point where it is now where it's just interface. You don't really need any expect actual there. For a little while there was actually zero usage of expect actual in the non-test code at all. I've since added a couple more new ones which I'll talk about later. But the overall lesson is just cause you have that functionality there to use you don't necessarily need it. And it's often the case that you can do things kind of a little bit more flexibly if you do it the old fashioned way. So one of the first change requests or sorry one of the first feature requests that I got after I put the library out was this question about what if you added change listeners. So the initial APIs were all just like getters and setters but both the Android and iOS APIs provide also right away to listen for updates. So I started putting that together or started kind of looking at what was there. So an Android shared preferences has this listener API on trade preferences change listener. And it's basically a haulback that it passes you kind of every time something updates it passes you function that says this was the key that changed. But it does this funky thing where it says it might get called again if there's repeated updates. So that the documentation is kind of weirdly and they use about that I don't actually know why I think it's maybe behavior that kind of changed some from early versions of Android. But anyway, iOS exposes a very different looking API where they have the NS notification center which is essentially a centralized event bus that lots of different system updates go through. So when a user defaults update happens you can subscribe to that and you get this user defaults to change notification but it doesn't tell you anything at all about the change. All it tells you is that a change happened. So I have these kind of like two very different APIs and behaviors that I'm trying to like write a shared interface around. And what I came up with looks like this. I'm emitting a bunch of the kind of glue code but the core of it is I keep a cache of what the kind of previous value at that key was in all my callbacks I check if that has changed and only update the user-supplied callback if it did. So it behaves a little bit differently than either of the platforms but it does so in a way that I could synchronize the behavior of both platforms. So I put that out and that was already for a little bit and then I added that JavaScript support and then I had the same problem all over again. And it turns out so the local storage APIs do provide ways to listen for updates but they're basically built for talking to other processes and other windows and I haven't yet found a way to kind of make it work in the same way that the intro and out of some of the additions do so I'm kind of just in trouble. So where I ended up with that was I split the interface. So I have a observable settings interface that holds all of the listener APIs and like Android and iOS implement that and the base settings interface like the sort of JavaScript just implements the base settings interface. So you can in your common code check whether you're just the settings or you're in observable settings and only interact with listeners when you are observable. So another fun developer story was around JVM limitations. So early on I put out a call saying what are other platforms that I should wrap around for this and someone suggested properties on the JVM. So that's even nice. So properties are the API that reads things like the great old properties file that you might know of from if you're used to using great old projects. That's just kind of like a standardized file format that the API can read. So I put an implementation out that wrapped that and then I got this pull request that pointed out something I'd missed which is the Java properties API doesn't actually do any work for you to serialize updates to disk when you make changes. So this protocol is essentially adding a callback inside of each of the change calls so that you would get the same behavior on the properties implementation that you do on the other platforms. So just as I was about to merge that pull request I got to a comment that said, hey by the way there's this other API called preferences that basically behaves the same way as the internet I also want to do and does exactly what you want. And plus the properties didn't have update listeners so this one does. So what I ended up with is now I have two different limitations that I'm maintaining on the JVM which means like if you're using this in a project that has a JVM target which ever API you want to interact with you can. And one of the lessons I came up with that is like there's a lot of complexity to having lots of different platforms that I don't have as much experience with but another thing I thought about is it is actually important to have that flexibility. So someone kind of introducing this library like introducing multi-platform into their project that has a JVM target might be using lots of different things to do their existing GVM storage and the more things that my library can interact with the easier it will be for them to use this library to kind of add multi-platform support to their code. So another fun thing that I did recently was adding to the CI support to the library. So see me. So right now I have a setup that's using Azure Pipelines to be able to build on Mac, Linux and Windows hosts which you need to be able to target every single one of the native platforms. And one of the neat things that this enabled is building the common code so things like the settings interface to every platform. So previously I was building everything locally and I was just building the platforms that had limitations which means if you as a consumer want to be able to use it on a different platform I hadn't thought of you don't have any opportunity to do that. So now the interface exists everywhere you still have to supply the implementation if you want to use it on a new platform but you have the flexibility to do that. And there's a funny little trick in the Gradle code to make that happen where the common Gradle plugin gives you this list of native presets for each target and you can just kind of iterate through them and say I want to build a target for each one of these because there's not a built in that just turns it on for everything on its own. And one of the couple things that came out of that is there's a bunch of new Apple targets that have been added in the most recent development version so originally I just had support for ARM64 and X64 which is the 64 bit device and simulator and eventually added desktop support and 32 bit iOS but now there's all of these new targets that would have been in pain to, sorry what I was saying, that yeah there's kind of like a lot more to manage and it's nice to kind of have some CI in place and make sure that it's all working the way expected to. And it's a good thing I had that because I actually learned that my initial 32 bit implementation was not doing what I thought it was. So the user defaults API that these things are running under the hood has this said integer call which is using the integer size that is native to the platform that it's running on. So it might be 32 bit or it might be 64 bit. And initially I was just using this convert function that Kotlin native provides to kind of try to convert between those. But it turns out that will let you essentially cast between 32 and 64 bit things so between Int and Long in Kotlin but that doesn't allow a system API that's only storing things as 32 bit integers to store anything larger so the long APIs on 32 bit systems were not actually working and I didn't realize that until I had all that CI in place and was running the test on a 32 bit simulator which I didn't have until watchOS came along. So where that's ended up is now I've reintroduced some expect actual to the library. I have this like set long extension and there's kind of like there's a set long, get long setting, get int that has different limitations per platform. So on 64 bit platforms it can just put both ints and longs into integers or sorry into IOS integers which is 64 bit which is essentially the same as Kotlin long and then on 32 bit stuff I have to stringify it so it's not super elegant but it does the trick and it can still serialize every possible value correctly. So some other notes from other things I've been or yeah from other kind of like things I've learned along the way. An important thing for library development is that Kotlin native currently because it's still in beta has no version compatibility guarantees. So there is, excuse me, so it's like there's this kind of ratchet that happens every time a new Kotlin version comes out where you're no longer compatible with when you update your Kotlin version your libraries need to update at the same time. So a lot of the library use system will kind of try to update quickly so that you're not blocking all of your users. Another good note is like the Gradle setup for a lot of this stuff can get kind of complicated. There's a good multi-platform Gradle reference here that's been like for a long time a lot of the multi-platform documentation has not always been kept up to date very well and this page has always been consistently pretty accurate. And a good just kind of reminder so like I've talked about a lot of like difficulties and issues and things but things have gotten a lot better over the last year and they will continue to over time as the ecosystem ensures. So what are some other things I'm working on? I always like to shame myself into I need to do a Maven Central deploy right now it's just deployed on J Center and I just hate dealing with publishing config and stuff but it's been on my two lists for a while and it's still there. I have portal class up right now to do co-routines flow and connex serialization integrations. So probably we'll have that in the next version but it's up on GitHub now which are interested in looking at them. I'd like to get a setup for on-device unit tests so that I can test some of the platforms that aren't currently being tested in CI and then also like add smart implementations so like there's a portal class right now that's a draft trying to add Windows registry support for Windows implementation and I'm interested in getting feedback on like what a useful API to use on Linux would be because I'm not aware of one right now. So what other stuff is out there? So not going into much detail on these but like JetBrains has a bunch of libraries that they've been working on. These three big ones are like co-routines serialization and the Ktor client essentially give you like your defaults HDD stack in multiple platforms is pretty useful and there's some of the things that they've been working on and then the community isn't putting out some libraries so one of the notable ones is SQL Delight which gives you SQLite access in your shared code and there's a couple other community libraries there as well and maybe at some point yours so one of the things I wanna do in giving talks like this is try to inspire more people to get into this ecosystem and think about what that would look like so one of the kind of strategy you can use to try to do this is wrapping around platform APIs which is what the kind of core of all these platforms settings does so like there's an application that's already there and you're just gonna create a shared interface into it so like a lot of the hard work is done but you have to do the kind of extra work of taking whatever different platform or whatever different platform implications there are and find the shared interface around them or you can go the other way you could write something that's pure Kotlin maybe it's like NAT Utilities or something like that the work there is creating something brand new but once you have, you get this great payout where now you can access everything like once that common columnization is there there's no work to add more platforms to it but either way like now is a really great time to think about doing this so things have been maturing over the last couple years but it's still a very young ecosystem there's a lot of opportunity if you want to start contributing to open source to be the first person to build a thing on this platform that was one of the things that inspired me to get into this stuff to begin with and if that's interesting to you I recommend you think about it so thanks, here's some links if you want to refer back to them later so the link to the library code is there that great old documentation page and this repository at the bottom that has a good list of libraries that are already out there that might give you some inspiration and then as I said earlier I'm joining the team at TouchLab who are kind of taking a pretty big leading role in building a lot of like building multiple maps for clients and kind of defining best practices and things so if you are interested in that or you like want help kind of getting started with stuff let me know so thanks a lot