I built an KMP + CMP app targeting Android and iOS...
# multiplatform
a
I built an KMP + CMP app targeting Android and iOS. Here is my experience, I hope it helps anyone who decides to go on this path:
🧵 13
🐕 6
K 4
Background ( I am an indie dev ): I had an iOS swiftUI and Android Jetpack compose app native apps on the App stores. Post release and after collecting some user feedback, I decided to pivot leading to making some major changes(basically rewrite the App) in May 2025, overwhelmed by the changes needed to do at both clients I decided to explore KMP. Side note: I explored creating a KMP app back in Nov 2023, I felt it may not be meeting my criteria so I went ahead and created a native SwiftUI app instead, Current state of the App: the App is live on the Google and Apple app store. My skills prior to developing this KMP app; • 4 years in Android development ( 3 years with Jetpack compose ) • 1 year experience in swiftUI development The development experience: Given my background in Jetpack compose, the experience felt like I am developing a Android jetpack compose app that works on iOS too so this was great. It would be accurate to say that building a KMP that shares UI on Android and iOS feels almost exactly like building a native Android App so Android devs will feel right at home. Notable Libraries i used were ( More notable libraries mentioned further down this message ) : • Compose Multiplatform for UI ( of-course) • Koin for DI • Compose Navigation 2.9 • https://klibs.io/project/tamimattafi/krop for cropping images • Compottie for lottie animations ( noticed some stutter, seems to be resolved in the latest update though but havent tried that yet ) • Room and Data store for local data persistence • Firebase for Auth, Storage, analytics • Ktor client for network calls From dev point of view, this app has the following: • Authentication to use the App • Upload photos to your profile • Make network calls and persist data locally to make it usable when no internet connection available • User generated content • Interact with self and other user generated content ( no chat functionality ) So not a complex app but not too simple either. (FYI but Not very relevant) The server was built using the Ktor server framework. What I loved: 1. 80% of app was built using Kotlin so my prior experience and love for Kotlin made the process really pleasant. 2. Plugins like Augment, Claude Code, Cascade worked really well here and also helped with the native iOS code 3. I don’t recall any notable differences in behavior for common code on Android and iOS so I was able to mostly get away without having to manually test the common code logic in iOS. ( not suggesting you ignore testing but as an indie dev the fact that there was parity helped save time and be less anxious) 4. Compose Multiplatform is basically Jetpack compose so I already had KMP skills even before I started playing with KMP. Here are my Gotchas: 1. Having SwiftUI skills will help immensely ( and might also be mandatory since some UI componenets will need swiftUI or UIKit implementation - date pickers for example ) to meet Apple’s guidelines. Furthermore having native iOS skills will make you an excellent KMP dev since using native views smartly will make the user experience top notch specially for iOS users. 2. When you implement platform specific code, you need to be super careful and need extensive testing to ensure it works as expected (expect + actual + swift code ( with multiple functions at times ) ). 3. The KMP library i used for image cropping did not work well on iOS, it was janky and led to crashes so this one : https://klibs.io/project/tamimattafi/krop as of June 2025 did not work reliably on iOS so I used https://github.com/TimOliver/TOCropViewController for iOS so this needed platform specific implementation and was a handful to ensure the iOS library interacts with Kotlin code. 4. AI tools like Claude code were surprisingly helpful with KMP and CMP given CMP especially is still new. Claude Code was smart enough to map jetpack compose with CMP and be aware of the differences despite the similarities. 5. I have a date picker in the App, for Android i implemented a custom solution that mimics the swiftUI date picker(PFA) and for iOS I wanted to implement a swiftUI date picker but turns out we can use UIKit instead that can be written in 100% kotlin instead so that was great. ( Apple also has rules for date picker as per my knowledge so it needs to comply with their Human interface guidelines so using a UIKit view written in 100% kotlin basically met those guidelines with 0 additional effort ) 6. Pagination with local data persistence with Room: most apps will be fine with pagination over network downloaded data and do not need local persistence as long as internet connectivity is not an issue I guess but I wanted local persistence support with pagination specially because the native apps had them and the app needed to be 100% usable with no or low internet connectivity. So the Android Remote Mediator did not work as expected in a CMP environment, So I had to implement a custom Paging with local data persistence that also supports UI updates when local data changed so I wanted the logic : Network -> download -> persist to ROOM -> ViewModel queries paginated data from ROOM -> Composables show local data ( So a single source of truth ). This was the one and only major pain point as I look back while I was going back and forth with Android Remote Mediators vs a custom solution so finally with the help of Claude code I was able to create my own class that supports paging using Kotlin flows. ( I created a base class and other classes inherit from it ), In the end I’m proud of it, I might not even consider immediate switching when the Android team supports this via the jetpack libraries ( I’m not sure if they already support it now ) 7. The android apk size is 36MB and on iOS is 109MB. I am not the best judge here when it comes to this but I think these sizes are manageable. 8. When i updated the android and iOS apps that are already on the App store built using native ( jetpack compose and swiftUI ) and then migrated them to KMP and release them on the App stores, users were not able to just update them, they had to delete and reinstall, I suspect this is due to a complete architectural changes of the Apps. What I did not like 1. The KMP library https://klibs.io/project/tamimattafi/krop as of June 2025 did not work as expect on iOS, I thought i was doing something wrong but I guess the library needed more work so I used a iOS native library and needed custom implementation. It works fine on Android thou. 2. Pagination with local data persistence support - as i mentioned this already, I did expect the existing jetpack library to work ( at the time of June 2025 ) but it did not. 3. Implementing native swift UI views is straight forward but is a handful, I do this using interfaces and protocols on swift but with the release of Kotlin swift export, I expect this to get easier. However this would be 20% or less of your code depending on your project scope so its defiantly manageable specially with the AI tools once you get used to how it works. 4. Build times on iOS take a long time ( much longer than Android ), So I always make it a point to use Xcode to build and deploy as opposed to Android studio/IntelliJ to build parallely on both platforms, this can help ease the pain. A note on Firebase: Used this for Auth with MFA, storage, Remote Config, App Check, Push Notifications(Android client and server) and Analytics. I went ahead with the official native Android and iOS libraries despite some 3rd party KMP community libraries since I felt this library is just too important to reply on unofficial ones. Since this needed platform specific implementation there was a handful amount of code to ensure it worked as expected so this is one of the areas where my native iOS background really helped, I was able to take the native swift code and native Android code and basically just map them together. Claude code really helped here. If you have no experience with native ios and want to use Firebase and want to use official libraries then you will have some amount of learning here to do. A note on Liquid glass: I am looking forward to catching up on the current state of Kotlin swift export for 2 things: 1. Make it even more easier to add swiftUI views in CMP and map the UI interaction with Kotlin business logic code. 2. Liquid glass in iOS is becoming a thing, whether you personally like it or not iOS users expect it and I plan to implement even more native swiftUI views to have the liquid glass look for iOS users. A note on AI tools: • Primary tool used was Claude code and it did a great job despite Compose Multiplatform being new compared native swiftUI and jetpack compose. (Had to use the 100$ max plan and at 3 or 4 occassions reached the usage imits as well.) • I was able to allocate the boiler plate code to implement native swiftUI views to AI. • Claude code helped immensely in creating a custom Paging class that supports ROOM and has a single source of truth while working network + room data. • For AI code completion, I used Windsurf( codeium) and Augment. Haven’t tried Junie yet. Regarding submitting the App to the App Store: I did expect the App to be rejected on 1st submission and need further iterations to be approved by Apple but I kid you not it was approved in the 1st submission itself, this was probably the most surprising and delightful moment. I must disclose that my native SwiftUi app needed about 6 iterations before it was approved so I guess i learnt my lessons there already. Overall the experience was awesome and I do recommend this to android developer who is skilled in jetpack compose. If anyone has any questions regarding my experience from development to getting them approved on the App Store, I’m more than happy to answer them to the best of my knowledge.