How Life.Church Engineers Simplified & Streamlined Cross-Platform App Development
The Life.Church mobile app is the official mobile application of Life.Church, an evangelical multi-site church based in Edmond, Oklahoma, whose mission is to lead people to become fully devoted followers of Christ. The objective of the Life.Church Interactive team, who owns and maintains the app, is to leverage technology to help users to take their next steps on their journey with Christ.
The Past
The initial version of the Life.Church app was built completely with the native iOS and Android technologies. As you can image, this resulted in many platform-specific differences in the various layers of the application architecture, as seen in the diagram below. The only section of the application that had any semblance of shared code consistency between platforms was the use of ReactiveX (RxKotlin and RxSwift) for the View Model layer.
While this architecture had been working, our team noticed four main areas in which we wanted to improve: Consistency, Agility, Accessibility, and Testing.
Consistency
Between platforms, our app suffered from a few key areas of inconsistency. For example, our service time information was not identical between iOS and Android, which was the result of different parsing and formatting logic. We also found that the videos were visible with different sort ordering, leading not only to inconsistent display in the UI, but taking additional time and effort in finding and fixing them during the quality assurance (QA) process. We even found that making very small changes often took longer than we desired. Fixing all of these inconsistencies ultimately required a fair amount of effort, which equated to unnecessary time and resource allocation.?
Agility
We always want to release features on both platforms at the same time to ensure we have feature parity. After all, it is not ideal to have users of one platform lose out on the full features and value simply because it wasn’t updated at the same rate as the other. Ultimately, it was hard to achieve this using all native code from the backend to the frontend. Implementing a feature for both platforms took more than twice as much effort as doing it in just one place that could be leveraged by both platforms, making iterations and updates less of a burden.
Accessibility
Accessibility is a key area of focus for our app, including important considerations such as support for bigger font sizes to help increase legibility, different screen sizes for the myriad types of devices, and dark mode for less eye strain in low-light environments. However, we found that implementing these key accessibility features while also considering all of the edge cases was a big challenge, especially with our small team size and technical constraints.?
Testability
Unit tests and UI tests also proved to be quite a herculean effort. In addition to the obvious difference of having to write tests in platform-specific code, without having a declarative UI framework, testing different scenarios required more development time to implement and ensure adequate coverage, and more time to run in continuous integration.
The Transition
Considering these four key areas of improvement, our team sought out a solution, ultimately deciding to use Kotlin Multiplatform (KMP) as our shared code for the backend, whose 1.3 and 1.4 version releases had proven to be highly stable. This decision was also inspired by our sister team, YouVersion, who had been using it successfully at scale for some time in their free YouVersion Bible app.
In 2020, we set out and began by migrating the network portion of the app’s data layer. It was an easy decision, as after multiple years of development by a variety of engineers, we had many duplicated dependencies just to achieve the networking logic. This transition happened iteratively throughout the year as we had time and margin.
The next year, we decided to use SwiftUI (iOS) and Jetpack Compose (Android) for our UI layer. We were inspired by great minds like Arkadii Ivanov and Daniele Baroncelli, among others, and were excited about the power that each of these toolkits had to offer. At that time, both SwiftUI and Jetpack Compose were slowly but surely becoming the de facto UI framework for their respective platforms. Jetpack Compose was heading to the stable version in that year, while at the same time, Apple was on the verge of announcing their third iteration of SwiftUI. We felt confident that both were stable enough to be used for our production code.?
Rather than a complete overhaul to rewrite everything from scratch, we decided to keep the existing code and architecture as-is, and to introduce the new architecture incrementally as we developed new features. Since this decision, we have implemented a handful of new features with the new architecture, and as of July 2022, roughly two thirds of our features are now on the new architecture, which is now structured as seen below:
The Outcome
Using the combination of KMP with declarative UI frameworks has proven to be the best of both worlds, especially for a team of our size. We are now able to share core business logic to provide a consistent user experience between platforms. It also gives us the freedom to use native UI frameworks, which we believe is the best way of delivering the best user experience.
领英推荐
We analyzed two different features of our application to compare the benefits of the new architecture to that of our original one: Achievements and Locations. The Achievements feature, which was built with our original architecture, is a feature that enables us to celebrate with users when they achieve various milestones in their journey. The Locations feature, built in our new architecture, is a feature that adds valuable campus-specific information such as events, next steps, and service times. While the two features have their unique differences, they also provided us the closest comparison for the two architectures, as they also share many of the same needs such as data retrieval and storage, date and time considerations, sorting and ordering, and the like.
Upon implementing these features in our new architecture, we noticed improvements in all four key areas on which we had focused:
Consistency
The Achievements feature suffers from inconsistent behavior, such as the fact that each platform has different timing to fetch the list of achievements. This results in the Android app having issues with not having the data at the appropriate time, and is a major pain point for our developers as we need to take extra time and effort to ensure the inconsistencies do not fall through the cracks and make it to our end users.
The Locations feature now sees our API server providing both apps with the raw data, and the conversion to human readable text is now handled by KMP. One immediate benefit we noticed is that our development and QA efforts on both iOS and Android were much quicker and easier, in addition to the logic being much more consistent and stable. Compared to the issues we still face with the Achievements feature, this has been a key and noticeable benefit, which we are hoping to soon be able to work in as we replace the old architecture.
Agility
The development of the Achievements feature took multiple months, as there were many factors that had to be accounted for, such as external dependencies and engineering resources. Because of this, it is not entirely fair to directly compare with the Location feature, however, the total development for the individual platforms did take approximately 2.5x more effort than doing it on just one platform.
The Locations feature really took advantage of having the shared code base. Based on timeline needs, we had to develop the app and the API at the same time. By having a set data structure between two layers, we were able to create the mock data inside the KMP, allowing UI development to proceed during API development. Once the API was ready, we simply made the necessary code tweak to the view model layer with no need to change the UI layer.
Accessibility
The Achievements feature started to add support for bigger font sizes with limited scenarios, but was ultimately not ever fully covered. Additionally, dark mode support was excluded from the scope from the beginning due to the limited resources available for each platform.
The Locations feature offers full support for bigger font sizes, as well as different mobile and tablet screens, and both light and dark modes for all new screens. This was possible thanks to SwiftUI and Jetpack Compose, as they enable us to better support accessibility with less code. So even in the scenario of limited resource availability, we were still able to implement these in short order which has paid huge dividends. While we are not yet able to have these enabled app-wide due to our legacy code, we are much closer to full support now than ever before, having added them to this feature.
Testability
The Achievements feature really didn’t originally have a good way of testing all the different edge cases we encountered, and with differences such as having to set the UI state independently, writing test code proved to be time-intensive and expensive.
The Locations feature was able to have all code covered well by unit tests, since we could write one set of test code inside KMP and run all of the tests against iOS simulators and Android emulators. As you can imagine, this has proven to be a big win, and has given us much greater confidence that our end product is as secure and stable as it can be for our end users.
The Future
We are presently working to decrease the development turnaround for the changes inside KMP. As we share more code for the business logic, we recognize that even the smallest changes in KMP require three merge requests - one for iOS, one for Android, and one for the KMP. Additionally, we are working to decrease the wait time between KMP build and app development. There are a few additional areas we would like to improve as well, with one of the biggest being the need to keep both old and new dependencies for both architectures. Once the transition is complete, we can move forward in getting rid of our old dependencies and this will no longer be a concern.?
At present, we are living in a sort of messy middle state due to our architecture transition still being in progress. However, seeing where we are compared to where we were just over a year ago, we are highly optimistic and excited about full migration and the day when we will have everything in proper alignment.?
Stewarding our Life.Church app and ensuring the best possible user experience is our number one priority. The best way we can do this is by leveraging technology not only for our end users to
Dohyeon Kim, Sr. Software Engineer, Interactive
Anthony Hessler, Sr. Software Engineer, Interactive
SEO specialist
11 个月Really enjoyed reading about how Life.Church streamlined their cross-platform approach. Your insights on the process are spot on! If you're interested in diving deeper into building church apps, this guide has some great tips: https://www.cleveroad.com/blog/how-to-build-a-church-app/
Individual and family services
1 年great work with inspiring
Senior Android Developer
1 年This is the best post I have ever read about how to migrate the architecture in Android. I love using You Version App and I hope one day to work for you guys and to serve our God by tech development. Blessings brothers and sisters, I'll be praying for you! ??
Strategic Business Leader | Visionary Problem Solver | Operational Excellence
2 年Our church is full of Jesus loving geniuses