Philips is a leading health technology company focused on improving people’s health and well-being, and enabling better outcomes across the health continuum – from healthy living and prevention, to diagnosis, treatment and home care. Philips is pursuing the goal of improving 2.5 billion lives per year by 2030.
Philips has a wide range of connected products for smart home care and personal health that are supported with smart applications for Android and iOS. Typically these products are connected locally via Bluetooth Low Energy or Wi-Fi, and they are also connected to the Philips cloud for remote control or data insights. For these purposes, the Philips Innovation Services organizational unit develops a connectivity platform that comprises embedded hardware, firmware, protocols, and an SDK for creating mobile apps. This connectivity SDK offers components for discovering, connecting to, and interacting with connected products. The SDK targets Android and iOS, with embedded Linux as the native target also becoming more important.
Installed base of mobile apps for Philips’ connected products
The mobile apps for Philips’ connected products have a combined installation count of well over 1 million on the App Store and Google Play. These apps are being installed and used all around the world, with the USA, Europe, and Asia being the biggest markets.
How is Kotlin Multiplatform Mobile used in your product?
One of our SDK components is a client library for Philips’ cloud solution: HealthSuite Digital Platform, or HSDP for short. It enables an app developer to interact easily with the cloud infrastructure without having to write HTTP requests with intricate headers and complex payloads. Instead, developers can call high-level functions, such as
getBlob() to download a binary file,
sendCommand() to control a cloud-connected device remotely, or
createDataItem() to upload telemetry data.
Visit the Kotlin Multiplatform Mobile portal to create your first cross-platfrom application with Kotlin!
Boilerplate code generation with Kotlin
All HSDP service endpoints are documented in the OpenAPI (Swagger) YAML format. Using these definitions, we generate a lot of boilerplate code during a build, which allows us to be ‘in sync’ with the cloud services in production. Our team has created the Kotlin codegen module for the popular OpenAPI Generator, which we are contributing to on Github. The code we generate with this includes all the necessary data transfer objects and classes that wrap the HTTP request/response handling using ktor. This means that we can focus on writing the business logic that builds on top of the generated code, instead of spending extra time working on the plumbing. The moment a production service is upgraded to a new version, we re-generate the code for it using the updated
YAML file and we can immediately spot the changes we need to make.
Designing an API for Java, Swift, and Kotlin
An interesting aspect of writing a Kotlin Multiplatform SDK component is thinking about your API. Each platform language has its own paradigms. For instance, asynchronous operations are handled slightly differently in each case. In Java it is natural to invoke an asynchronous method with a callback argument to handle the result, for example:
In Swift, we are used to using completion handlers in this type of scenario. For example:
And finally, for Kotlin users we’d like to offer an API with the suspend keyword, so they can make use of it using coroutines. One problem that quickly arose with this was that defining multiple method signatures on the API would generate more than is needed on each platform. Suspending functions are for instance converted for Java, but especially on Android SDK levels below 24 (which don’t have support for all Java 8 language features), the result generates quite a verbose signature that is hard to work with. To address this situation for all target languages, we added functions with specialized signatures for Java, Kotlin, and Swift on the external API. These signatures wrap the
suspend variant using a custom
runAsync wrapper function that arranges the correct
Dispatcher for executing suspending functions. The
@JvmSynthetic annotation was key in hiding the incompatible Java variants.
This enables us to offer the API function signatures that feel most natural on each native platform while keeping the business logic concentrated under one implementation.
Why did your team decide to use Kotlin Multiplatform?
We had been busy building SDK components for Android and iOS for a while, and cross-platform code reuse was not really happening apart from a few bits and pieces in C, such as the implementation of the SSDP protocol. However, because of the difficulty of bridging between Java, Objective-C, and C, we actually ended up reimplementing this protocol natively on each platform.
This meant that the development team was also split in two when we were implementing new features. The advent of Kotlin Multiplatform provided a serious opportunity to not only become faster at implementing new features, but also to get more interaction in the team between Android and iOS developers.
We knew that Kotlin Multiplatform would still evolve a lot, but the benefits of being able to write once, test once, and deploy more as a res
ult, were compelling enough.
Some of the things that we learned from this experience were:
The integration of the Kotlin Multiplatform plugin in the IDE (IntelliJ IDEA/Android Studio) has not always been ideal, but it has definitely improved with each release.
There is always a trade-off between code reuse and writing stuff natively, but this holds true for every cross-platform solution. You have to think hard about which types of business logic can be converged and which should remain native.
Adopting Kotlin/Native on iOS is definitely harder than using it on Android or JVM, especially when it comes to IDE and debugging support.
With Kotlin Multiplatform, our development team is able to push out new features much faster, the codebase is easier to maintain
The Kotlin language itself helps us write better code with less effort.
We have seen an increase in interaction and knowledge sharing between the Android and iOS developers in the team.
Leaving the ecosystem to the community allows JetBrains to focus on the essentials and move faster.
We are planning to target Linux natively as well, so we will have lots of opportunities to see whether the shared codebase pays off. For anyone thinking about using Kotlin Multiplatform, my suggestion would be to look at that portion of your codebase which does not directly interact with platform-native APIs or interfaces that are specific to one platform. Focus on the business logic that has a clear overlap between platforms, since that is where you can actually gain the most.
Jeroen Brosens, Mobile Connectivity Architect, Philips Innovation Services
Visit the Kotlin Multiplatform Mobile portal to find more about cross platform development!