Cross-Platform Mobile Development Options

At work, we are planning the development of a mobile app that will need both an iOS and Android application. So besides just making two native versions, we are also looking at options for cross-platform development.

Framework Options

  • React Native
  • Flutter
  • Microsoft MAUI (formally Xamarin.Forms)
  • Strada (part of Hotwire from 37signals)

Why are we not considering a progressive web app (PWA)?

We kind of already have that. When iOS 16.4 was released with web notification support, we added it. We also use Turbo (part of Hotwire) to make the interactions feel like a client-side app.

Users are still requesting a “proper” mobile app that installs from an app store. But we’re not begrudgingly planning this mobile app. I think it will be fun to build.

About the app

One of the web services we produce is a CRM used by sales reps at car dealerships. We are planning a texting companion mobile app for this web service. The mobile app should allow the rep to login to our service, list their active leads, have a standard texting UI, and receive push notifications when the lead replies to the sales rep.

Because the initial scope is limited, native development for iOS and Android could be a possibility. However, if we choose to expand the feature set to include other features of the web service, it would be nice to just work in one codebase. We also don’t think there will be any immediate features that a cross-platform framework wouldn’t be able to handle well.

React Native

React Native is the default choice for cross-platform development. It’s used by a lot of people and companies, and we do a decent amount of React development in other web applications.

My reason for considering React Native is just how widely it is used. Someone else has most likely run into issues we would run into.

My concern is that a fresh “Hello World” application has over 800MB of node modules and other dependencies. That seems like it will be difficult to maintain through the years as the app grows and React Native and its dependencies update.

Another concern is that React components are translated into native views. You aren’t directly writing what is presented on the screen.

Flutter

Flutter directly addresses the two main concerns I have with React Native and seems to be the biggest and strongest competitor to React Native.

Unlike React Native, it uses its own rendering system, so you are directly writing what is presented. And it requires almost no third-party dependencies for a simple application. Because of this, a Flutter project seems like it should be easier to maintain than a React Native project.

My concern is getting up to speed on Dart, the Flutter APIs, the ecosystem of packages and components, and development tools. At work, we have a lot of React knowledge, so getting up and running with React Native seems like it would be quicker than getting up and running with Flutter.

I’m also a little concerned about Google’s track record with supporting technology and products. They are well known for killing or abandoning things left and right. However, Flutter is used for mobile apps like Google Pay and has a very strong open-source community. If Google does abandon it, I bet Flutter will live on independently.

MAUI

While our CRM service is a Ruby on Rails application, we do a lot of ASP.NET Core development with C#. This makes MAUI, a C# cross-platform framework, a pretty attractive choice. We know the .NET API and standard library, as well as the development tools on Windows, pretty well.

I’m just not convinced of how well supported it is by Microsoft. MAUI seems more like a product they are making and less like a technology they would use themselves. For example, the Outlook and Teams mobile applications are a mix of platform native and React Native. Whereas Flutter is actively being used by Google for apps like Google Pay and as the UI for their Fuchsia operating system.

And the MAUI community just isn’t as active as either the React Native and Flutter communities, even when you bring in the Xamarin community. I like the idea of a large, active community behind a framework. Other people will know where the sharp edges are and, hopefully, will have noted them.

Strada

With our service being a Ruby on Rails project, it seemed natural to use Hotwire to sprinkle in dynamic behavior. Since we’re using Turbo and already support some PWA features like web push, Strada could be a viable option.

I have two big concerns. Strada is in beta right now, and we’re planning to start development soon. I don’t know how beta it is right now or if it will reach a final 1.0 before we begin. And two, the way Strada works would require some changes to the views and javascript components in our web application to support Strada. I like the cleaner separation that the other frameworks offer by just relying on our API for data fetching and actions instead of trying to integrate a hybrid native/web application.

The Showdown

The first framework we ruled out was Strada because of how intimately tied to the rest of our web application it would be. The point of Strada is to make your entire web app available on a mobile platform with a few native touches to make the experience nice for the user, but we weren’t willing to pepper the web application with data-bridge attributes and make some other changes to support this technology while still needing to write platform-specific code in Swift and in Kotlin.

We were looking for something that could be one codebase for a mobile companion app instead of changes to the web service and an additional codebase for each mobile platform.

Ruling out MAUI was a hard one. As primarily a C# shop, we really wanted MAUI to work for us. I tried a few tutorial projects, but I ran into a button state issue in a very simple demo app, and I also found Mac-based development tools like C# DevKit lacking for MAUI development.

I went into this showdown thinking React Native would be the framework we picked, but the dependency situation was the deal breaker.

I downloaded an open-sourced React Native project that was a simple “to-do” application. It was last touched in 2019, and I wanted to get it up and running in 2023. Apparently React Native used to ship with a Navigator component, but that’s no longer available, and the community seems to have migrated to React Navigation. The old version of React Native wouldn’t build using the current iOS SDK on my machine, and to upgrade to the newest React Native version required a migration of Navigator across all the screens to something else among updates to node modules and cocoapods.

Not that we would let a project sit for four years. But I wanted to see how bad things could get.

The Winner

Ultimately, we picked Flutter for cross-platform development. It seems well supported by both Google and an active community, and the developer experience seems to be equally good on Windows, Mac, and Linux with either VS Code and a browser or Android Studio.

Like with React Native, I picked an open-sourced project, this time from 2018, and tried to get it up and running in 2023. I had to make two changes to pubspec.yml and update the iOS workspace to the latest version of Swift. No other changes were needed for that app written using Flutter v1 to run in an iOS simulator today using the Flutter v3.2 SDK.

The goal of Flutter seems to be consistent behavior between platforms, and that’s what I’m seeing with my experiments, both with UI components and other things like push notifications.