Sharing Mobile Application Code Across Platforms with Kotlin Multiplatform Mobile for GraphQL Queries

Sharing Mobile Application Code Across Platforms with Kotlin Multiplatform Mobile for GraphQL Queries

Since the release of the first smartphone, mobile teams have faced some common challenges:

  • Maintaining parity across platforms – iOS, Android, web, etc.
  • Keeping bugs to a minimum
  • Reducing development time

These challenges are often related — differing implementations across clients means two or three times as much code needs to be written, leading to double or more the chances for bugs to arise, compared to. developing on a single platform. This difference in platforms also leads to more time spent coding, as developers create similar code against each client for every change. Furthermore, developing for multiple platforms requires specialized knowledge about different languages and technologies.

Unfortunately, the Premier Agent mobile team has not been immune to these challenges. In this blog post we’ll discuss our journey into sharing code across platforms using Kotlin Multiplatform Mobile (KMM), the difficulties we encountered, the lessons we learned along the way, and the steps we’ll take toward streamlining our development process.

For those unfamiliar with KMM, it enables the sharing of code logic across multiple platforms — including Android, iOS, and more — in the Kotlin language. By enabling the creation of platform-agnostic modules, KMM allows developers to write and maintain a single codebase, typically used in the data and network layers, as well as for business logic. This reduces redundancy, eases maintenance, promotes code reusability, accelerates development, and ensures consistent functionality across diverse platforms. Just the thing we’re looking for!

Once we identified KMM as a tool we could use to share code across platforms and tackle the problems mentioned earlier, our next step was to identify a use case. We considered where in our code bases we had code that could most readily be reused, and where doing so would address bugs we’d encountered on one or the other platform due to implementation differences.

This shined a light in the direction of our GraphQL queries, which are typically recreated across iOS and Android. If we could transition these GraphQL queries to KMM, we’d avoid having to write nearly identical queries on both platforms, reduce platform-specific boilerplate, and avoid bugs we’d recently bonked our heads on due to differing queries or expectations of those queries, that our mock server exposed when creating UI tests.

Our workflow

For context, we’ll summarize our architecture & workflow at a high level:

  1. With the move to KMM, we created a separate repo for our KMM code, referenced by our iOS and Android application repos as an external dependency. Developers working on changes to shared code in the KMM repo will do so in their local development branch.
  2. When changes are ready to take effect for testing (before being merged or potentially before being pushed) they will run a manual publish step in order to publish artifacts to a local repository. This step will create build artifacts from that repo based on the commit hash; this version is temporary and separate from the normal semantic version.
  3. Then the developer will be able to update their main app repo to point to this temporary version, validating the changes or performing development on the app repo that depends on the KMM repo changes.
  4. Once the KMM changes are merged into main, the actual semantic version can be incremented, and the KMM changes will be deployed as artifacts to Artifactory. This new semantic version will include their changes and the app repo will be updated to point to this new semantic version.

Here are a few of our observations and experiences from implementing new and existing GraphQL queries in KMM:

KMM’s Apollo Client’s is well-documented

  • Huzzah! Getting the plugin up and running was straightforward.

Module Makeover

  • For context, prior to the move towards KMM, most of the Premier Agent app was broken out into separate modules for each feature. However, this separation did not include our queries, which all resided in a shared GraphQL module.
  • The transition to KMM provided the perfect opportunity to refactor our GraphQL queries into feature-specific modules, with our schema residing in the core module.
  • This division of GraphQL content into multiple modules was more involved. We encountered several issues along the way and had to undergo some trial and error to get the new setup working.

Reusing Build Logic

  • We crafted a custom Gradle plugin to apply the Apollo plugin and configure it for separate modules within the KMM repository.

Apollo Client in KMM

  • Along with GraphQL queries, we also moved our Apollo client to KMM. We adapted an existing client from the app repository and tailored it to KMM’s requirements, which was straightforward.

?We encountered a number of issues that weren’t apparent at the outset of this project:?

Merge Mishaps

  • One issue happened in the following scenario:
  • A developer would make changes to the KMM repo to support changes to the main development branch of the app repo.
  • Separately, updates would be made to an active release branch of the app repo, along with associated KMM repo updates.
  • These changes would result in an outdated schema in the KMM repo, which would be a problem when those changes were watered to the main development branch.

To avoid similar issues, moving forward we will:

  • Avoid having parallel work on the same GraphQL content, e.g. if there’s concurrent work on a release branch, wait for that to be merged, then take it into account. Make associated app updates immediately? (after merging KMM repo changes).

Version Heck

  • A second issue emerged from our first attempt at a versioning methodology for KMM, without considering the full impact of the workflow for developers.First, we designed a versioning method that would create a version identifier based on the most recent commit hash to a developer’s local development branch of the KMM repo (same as in the workflow described above).
  • When we were ready for a release, we bumped up the semantic version number (this part is different – we now bump the semantic version on merge to main, as opposed to on release). We did this? so we could get the in-progress version of the KMM repo up on the cloud and ready for immediate use, ensuring that continued development developers wouldn’t be blocked. However, we quickly realized that this was confusing during development.
  • Imagine that developer ‘A’ merged something into the KMM repo and then updated the app repo’s KMM dependency version with the hash version they had just generated. Meanwhile, developer ‘B’ merges something into the KMM repo and updates the app repo’s KMM dependency version again, this time to the hash that they just created
  • Since these versions are not sequential, it’s unclear which hash is correct or which changes are included in each hash.To
  • Address this issue, while still enabling developers to work with their in-progress KMM changes, we started auto-incrementing the KMM repo’s semantic version on any merge into main, rather than on release of a new version. This ensured that all changes were sequential and eliminated? confusion about which version was most recent when multiple developers were making changes simultaneously.

Since switching to KMM for GraphQL queries, we’ve encountered no bugs due to differing GraphQL implementations across platforms, and we have saved the need for dozens of merge requests. Previously, we would need to develop merge requests? for both iOS and Android platforms. Now we can? develop against just one shared KMM repo. At this point in our KMM adoption, we’ve overcome growing pains that accompany working with a separate KMM repo, we’ve found success in the areas we set out to improve, and we have our sights set on pursuing new use cases for KMM.

As we continue with KMM we will explore increasing the amount of shared code beyond the network layer, potentially as far as moving our entire domain layer and the business logic contained therein, to KMM. On the extreme end of things, we could adopt a UI framework like Jetpack Compose Multiplatform, so almost the entire code base could be in the shared repository. This is an experimental framework, so taking such a drastic step could lead to unforeseen, and undesired, consequences (e.g. What if Apple changed something within their ecosystem to make such an approach impossible? We would be stuck re-writing the whole app in swift). Which is why we’re taking small,? incremental steps toward this possibility, and will continue to light our future path with KMM.

By- Himanshu Garg


要查看或添加评论,请登录

Engineer's Planet的更多文章

社区洞察

其他会员也浏览了