Earlier than I began migrating, I had a number of targets in thoughts:
- Make my app cross-platform with out an excessive amount of effort.
- Defer processing of iOS-specific or Swift code for so long as potential and supply mockups as an alternative
- Totally migrated my Android app to Kotlin Multiplatform and Multiplatform Compose, ensuring it runs easily on Android.
- Present precise implementations and resolve any late stage iOS compatibility points.
Since this can be a small undertaking, you possibly can deal with iOS compatibility later. For bigger tasks, I’d intention for incremental iOS leads to the migration for smoother transitions.
See the picture beneath for a visible comparability of the applying structure earlier than and after the migration.
For the reason that undertaking is developed utilizing modularization, I can migrate every module individually. My principal strategy is to create an empty shared module and regularly migrate every submodule to this shared module. When migrating every module, I deal with fixing totally different challenges which are particular to every module. Right here is an easy overview of the steps I adopted:
- Core module: I began with the core module, impartial of the UI. This helped me perceive the fundamentals of Cross-platform Kotlin growth, comparable to preliminary setup, dependencies, and using anticipated/precise declarations in .
- File Characteristic module: Subsequent I moved the profile function module which has a static view and does not require ViewModels or fetch knowledge. This allowed me to find how JetBrains Compose cross-platform function with out the complexities of useful resource sharing.
- consumer interface module: This part includes specializing in sharing sources, together with fonts, themes, and different UI associated components.
- Quote Characteristic module: Extra advanced quotation function module, involving ViewModel And information. I settled on this module after getting a greater understanding of the cross-platform setup.
Now, let’s dive into the technical particulars and focus on particularly the problems I encountered throughout the migration, together with the options I applied to repair them.
First step – Arrange your atmosphere, together with putting in Xcode and the Kotlin Multiplatform plugin. For detailed directions, you possibly can discuss with the official handbook https://kotlinlang.org/docs/multiplatform-mobile-setup.html#install-the-necessary-tools
Second step (MAIN STEP) — Add these to yours gradle.properties doc
kotlin.mpp.enableCInteropCommonization=true
kotlin.mpp.androidSourceSetLayoutVersion=2
kotlin.mpp.stability.nowarn=true
org.jetbrains.compose.experimental.uikit.enabled=true
kotlin.native.cacheKind=none
Throughout the migration, I encountered many various kinds of issues that have been troublesome to resolve by way of a web-based search as a result of the know-how was nonetheless new. Fortunately, the JetBrains staff on the Kotlin Slack channel was fast to supply useful options, and more often than not it concerned gradle.properties. So be sure to add these in gradle.properties file and you’ll save a variety of time.
third step – Create drums share cross-platform library module
By default, the .gitignore the file isn’t routinely generated by the plugin. You may create your individual .gitignore file and embody the next:
/construct
.gradle
Then every module can be created inside this shared module.
Fourth step – Transfer modules
Core The module is principally composed of pure Kotlin code, besides Community handler class, checked the web connection. Apart from that since frequent core the module does not have any android dependencies, I believe copying and pasting the recordsdata in generalMain supply positioned in :shared:frequent:core module will suffice to finish the migration. Nevertheless, I bumped into a number of issues:
1) The primary downside is said to Legionthe unavailability of Coordinator.IO within the frequent energy provide. Nevertheless, I used to be capable of resolve this downside by updating the coroutine model 1.7.1makes Dispatcher.IO obtainable for a number of platforms within the newest model.
2) The second downside I confronted was associated to the logging library, wooden. Since Timber isn’t cross-platform suitable I needed to search for another cross-platform logging library (Napier logging library). The problem is to switch all variations of Timber with a brand new library implementation all through the codebase. This expertise taught me an essential lesson: each time I take advantage of a brand new library, it is best to create a boundary, comparable to an interface, in order that my utility’s enterprise logic stays the identical. impartial of any explicit library implementation. This strategy results in cleaner code and makes it simpler to alter libraries sooner or later. I’ve shared extra detailed details about this in my weblog, you will discover it at https://medium.com/@mirzemehdi/create-boundaries-over-libraries-before-it-is-too-late-2ff2a3eb60b5
3) The following problem I confronted was with Community layer, which includes checking the web connection. Since this perform is platform-specific, I used wanting ahead And truly idea in cross-platform growth. And with the assistance of Koin, I used to be capable of present the required platform-specific implementation. Once more, I’ve supplied extra particulars on this in my publish. https://medium.com/proandroiddev/achieving-platform-specific-implementations-with-koin-in-kmm-5cb029ba4f3b
4) One other downside I had was associated to the highest stage file within the frequent module. File with the identical title (Core module) in each Android and iOS modules, inflicting an error (Copy JVM class title).
There may be additionally an open situation associated to this: https://youtrack.jetbrains.com/situation/KT-21186.
To get round this, I merely renamed the recordsdata utilizing a naming conference. I added a prefix indicating the platform, comparable to AndroidCore . module And IosCoreModuleand this resolved the battle.
After making these modifications, the core module grew to become a whole KMM module. Then I can take away the outdated core module as it’s not wanted. To make sure progress, I examined the primary utility utilizing shared core module recordsdata. If all the pieces works as anticipated, that reveals that I am heading in the right direction. Eliminating the “outdated” core module is a superb feeling 🙂
Effectively, you will discover my favourite quote on the best facet 😀
The migration of profile function module surprisingly easy as a result of I solved cross-platform issues. For the standpoint I took actually use the identical Jetpack Compose as earlier than. Lastly, I eliminated the outdated profile function module however saved the navigation in androidMain energy provide to verify the Android app nonetheless works. I might say the profile module made me much less of a headache throughout the migration.
inside frequent consumer interface module, the primary problem I confronted was useful resource sharing. To repair this, I used “moko sources” library. This library helps to handle and share sources throughout totally different platforms.
In relation to function module quoteThe principle challenges I face revolve round information And ViewModel carry out. These two features must be rigorously thought-about and studied to make sure they work in a cross-platform atmosphere.
Throughout the migration, I examined the implementation of various tasks from a modularization and cross-platform perspective in KMM. For the ViewModel side, I discovered KampKitthe best and most handy strategy to implementation.
And for navigation, I selected this library that’s simple to deploy and matches properly with the prevailing structure of the applying. (https://github.com/chRyNaN/navigation).
Then I found the Cross-platform Journey navigation library ( ), seems promising. Though I didn’t implement it throughout this migration, I’m contemplating utilizing it sooner or later for higher navigation.
Ultimately I created a local module that features all the mandatory modules and gives a principal native view for various platforms which simplifies the combination course of particularly for iOS, the place a single body is required. In relation to the iOS half, there’s a limitation that’s solely Coconut shell framework can be utilized. Nevertheless, this isn’t an issue, as you possibly can merely copy and paste the framework from the undertaking root listing construct.gradlDigital file. Additionally, for the iOS undertaking, I used ios apps folder from the official Jetbrains template and made some minor modifications to go well with my undertaking’s wants.
https://github.com/JetBrains/compose-multiplatform-ios-android-template#readme
And ultimately, that is the one a part of the Swift code that ChatGpt wrote for me.
These are the challenges I confronted throughout the migration. You may take a look at the most recent code from the grasp department: