Background
About one year and a half ago, MOXY began its journey in the domain of mobile apps engineering. Even before that, as professionals that build digital solutions for the web and continuously monitor industry trends, we were conscious the web is not always the most adequate target platform to deliver certain kind of products and experiences. At the time, new business opportunities in the mobile market started to present themselves when some of our clients approached us to develop apps for them. We then realized it was time to realign our strategy and decided to seriously invest in exploring the mobile technology scene and train our team to develop and design apps for handheld devices targeting iOS and Android.
If we were to invest resources in learning how to develop to mobile platforms, we were to do it the way right way.
Above all, our premise was that the commitment towards what our clients entrusted us to build and the quality bar we set upon ourselves would have to remain the same: to deliver impactful experiences, visual appealing and polished apps which make us proud and our clients happy.
As such, the mobile development framework of choice would have to ideally comply with the following criteria:
- Cross-platform: share a single code base across iOS and Android so we could ship faster and at a lower cost.
- Open-source: as an open-source driven company, the technology would have to be backed and maintained by an experienced and massive developer community.
- Maturity & stability: the technology would have already reached a stage where it has become reliable to be used in production without major hassles.
- Developer experience: the development and debugging tools would have to be easy and pleasant to use. Using the same tools, libraries and languages we already use daily to develop websites would be a major plus as well.
- Native capabilities: unlimited access to the native APIs which enable us to develop more specialized and sophisticated apps.
- Native-like performance: reasonable load times and smooth animations and transitions.
Why React Native?
React Native is an open-source cross-platform development framework created by Facebook and it was first released to the public in 2015. Armed with the possibility to build native apps using React, a UI library also created by Facebook, React Native’s popularity among web developers grew quickly, which led to the establishment of a thriving community and the proliferation of countless open-source libraries and learning material. Moreover, in the last five years, besides Facebook, several tech giants such as Uber, Microsoft and Shopify adopted React Native and continue to place their bets on it.
Naturally, it also deserved our attention.
As with everything in technology, our choices are motivated by trade-offs and risk is always involved. We ended up choosing React Native as it ticked the most boxes in our list of goals we set fourth above. The decisive factors behind our decision were:
- The dominance of JavaScript from the frontend to the backend in our tech stack.
- The long standing usage of React for our web development projects and how much we love it!
- The possibility to utilize tools and libraries we were already familiar with such as ESLint, Babel, Jest, Redux and React Intl, to name a few.
- Our proficiency in JavaScript and React as a whole.
Cross-platform development frameworks are very complex and none of them is perfect. React Native is no exception, but it continues to evolve with fast release cycles and has been getting better and better in the last couple of years. Furthermore, a major architectural shift, several performance improvements and optimizations are expected to land throughout this year. You can stay tuned by keeping an eye on the official blog for announcements and on the discussions and proposals repository to follow ongoing discussions and upcoming developments. Moreover, if you’re interested in having an outlook of the current state of React Native, feel free to check the article by LogRocket to learn more.
Beyond React Native, we did have a look at other contenders in the cross-platform space for native apps, namely Flutter and NativeScript, but, without disregarding their potential, both did not meet our criteria. Had we chosen to use Flutter, we would have to learn a new programming language, Dart, for core development and we determined such investment was not worth for us, at least for the time being. Spending time to learn, master and keep up with React Native, Android and iOS ecosystems was and still is enough of a challenge of its own. Thus, we put Flutter aside.
NativeScript, on the other hand, could have been a more interesting candidate as native APIs can be accessed from JavaScript seamlessly. Unlike React Native, where we have to write code in three different languages to bridge the JavaScript and the native realms, writing code in one language is a major upside from productivity and maintenance standpoints. Unfortunately, NativeScript lacked React support and still does to this day (only Angular and Vue are supported). React has been our library of choice for quite a while now, in which we heavily invested our team to consolidate its expertise; for this reason alone we discarded NativeScript as well, as it would not be convenient to switch to another stack.
React Native CLI vs. Expo
Upon settling on React Native, our next task, most recently, was to evaluate and assess existing toolkits and frameworks that:
- Allowed us to easily kickstart new apps.
- Offered APIs and tools out-of-the box to speed up development and delivery.
This was essential as in our first React Native projects we spent a lot of time (and money) over and over again to setup the basics before starting the actual development. We had to put an end to this approach and seek a more efficient and effective solution.
In our hunt, the Expo project caught our eye. Expo is open-source, free and one of the most complete solutions we found as it offers a SDK with dozens of APIs, a CLI which builds production-ready iOS and Android binaries and deploys them to the app stores, and much more. To build apps with Expo, developers have two workflows at their disposal: managed and bare. However, from our experience, all the apps we developed required us to have complete control over the native projects and we anticipate this scenario is not likely to change in the near future, so the managed approach does not work for us. Regardless, it was important for us to understand the several limitations of managed projects and lost benefits in bare projects, the most critical to us being listed below:
- The build service provided by the Expo CLI is not yet available to bare projects which, in our opinion, is a huge downside and the biggest. Building and deploying iOS and Android apps automatically to the app stores requires knowledge of how app signing works, native development toolchains and APIs or even third-party tools, such as Fastlane. In managed projects, that process is simplified by hiding away all the complexity involved in the process.
- There are many native APIs yet to be supported in the managed workflow.
- In the managed workflow, Expo bundles APIs and dependencies we may not even use which leads to an increase of the app binary’s size.
- Third-party modules which require native access can only be used in the bare workflow.
- If we want to use a push notification service other than Expo’s, it can only be done in the bare workflow.
- In the bare workflow, common configurations such as setting up a splash screen or app icons are not so straightforward. It is necessary to perform configurations in the native projects of each platform and use native tooling such Xcode’s Layout Editor, Android’s Image Asset Studio, etc.
One last aspect worth noting about the managed workflow is that each version of the Expo SDK is tightly coupled to a specific React Native version and is subject to a time-based release cycle. This can be problematic for three reasons. First, to ensure compatibility and stability, React Native can’t be updated before Expo itself is and, as a result, we might miss on the latest fixes and new features in between. Second, if we’re tied to a React Native version we might not be able to support the latest Android or iOS major versions if an upgrade is required. Third and last, as each new version of the SDK is released on a quarterly timeframe, we might have to wait a while for bug fixes to be addressed. If there are delays during this process, things might get complicated.
To change things for the better, the Expo team humbly took one step back and has been taking several measures gradually:
- Last year, introduced Unimodules which allows us to selectively import modules from the Expo SDKs in any React Native app, be it a bare Expo project or not! This outstanding addition results from a change of direction driven by developer feedback and the long-term commitment made by the Expo team to unify the development experience across both workflows.
- Building upon the above, the roadmap for 2020 looks promising as it plans to address many of the pressing pain points we outlined here towards the end of the year.
The ideal workflow for us would be the possibility to enjoy the perks offered by the managed workflow combined with those offered by the bare one. In light of the current situation, as we weighed in the pros and cons, we concluded we wouldn’t gain much from using the bare workflow. We eventually decided to remove Expo from the equation for the time being and spin up a barebones React Native project enhanced with foundational features, popular libraries, tooling and conventions. To that end, we created a skeleton app with React Native’s CLI, which is more or less equivalent to an ejected Expo project but without web support (which we don’t need).
We like to keep our options open and, as such, we’re not shutting the door on Expo permanently. On the contrary, judging by the trajectory the project is being taken, there’s a high likelihood of us embracing it sometime in the future. We’ll be closely monitoring how things roll out in the coming months.
A new boilerplate emerges
As a company that advocates for open-source, our vision was to design a boilerplate that would serve our own needs but, at the same time, could be consumed by the open-source community and leverage its efforts.
Enter React Native with MOXY.
moxystudio/react-native-with-moxy
MOXY's boilerplate to accelerate the setup of new React Native based mobile applications.
Following the rationale and principles behind our boilerplate for web projects, there are a few things included by default but some of them can be easily removed, adapted or even ignored as it is not our intention to lock anyone in. Developers are free to use as little or as much features as they like.
Below you can find the base feature set included:
-
Internationalization (i18n): Multi-language support is added from the start, even if the project doesn’t initially require it. Powered by React Intl and
[@react-native-community/react-native-localize](https://github.com/react-native-community/react-native-localize)
. - Accessibility (a11y): Automatic checks are in place to enforce good practices and correctness on accessibility.
- Code linting: Usage of ESLint to ensure code is consistent across the codebase.
- Unit testing: Automated tests to validate correctness and to provide confidence upon code changes with Jest and Native Testing Library.
-
Streamlined SVG support: Import your SVG files just like any other module and use them as a React component. Powered by
[@react-native-community/react-native-svg](https://github.com/react-native-community/react-native-svg)
and[react-native-svg-transformer](https://github.com/kristerkari/react-native-svg-transformer)
. - Sane and scalable directory layout: Guidelines optimized for clear organization and separation of concerns.
- Screen navigator: Straightforward, JavaScript-based solution to manage of your app’s routes and screen transitions with React Navigation v5.
-
Environment variables: Twelve-factor your app’s config with
.env
files and access your variables from both JavaScript and native realms. Enabled by[react-native-config](https://github.com/luggit/react-native-config)
. -
Splash screen: Provide your user’s a pleasant startup experience by displaying a welcome screen while JavaScript is loading or asynchronous operations run to completion. Powered by
[react-native-bootsplash](https://github.com/zoontek/react-native-bootsplash)
. - Changelog generation: A changelog file adhering to the Conventional Commits specification is automatically generated and kept up to date on each release.
- Documentation: A webpage with key documentation, including instructions on how to setup a new app.
We have baked in all these features and best practices into our boilerplate and many more are planned to come. Moreover, we have an optional set of recipes that can be followed to progressively add other features. One of such recipes is a walkthrough on how to integrate Redux, a popular global state manager.
What’s next?
Our endeavor is still in its infancy but there is so much more in store! Here’s a quick peek of what’s on the short-term roadmap:
- Error tracking & monitoring: Explore the market of error logging solutions such as BugSnag, Sentry and Firebase Crashlytics and develop integrations for a select few in the form of an abstraction layer to enable us to easily switch between implementations. Unhandled native and JS errors will be reported automatically.
- Analytics: Explore the market of solutions to measure and analyze user behaviors by keeping track of custom events, such as Firebase Analytics. We’re planning to integrate one such solution in the boilerplate by default and create a set of instructions on how to configure it.
- Handle crashes gracefully: Users are never happy when an app crashes unexpectedly and closes abruptly without any feedback. In order to deliver a more professional experience, we have to intercept native and JS errors and display a proper screen to inform something went wrong.
- Update to React Native 0.63: The latest version of React Native has been around for about one month now so it’s time to upgrade from 0.61.
- Enable Flipper debugger integration: Flipper is now enabled by default on React Native 0.62 and onwards. React Native Debugger is a great project has been serving us well, but Flipper offers a much superior user interface and developer experience. Furthermore, Flipper is extensible via its plugin API.
- iOS and Android versioning strategy: We have been exploring industry practices on how to handle the versioning of Android and iOS apps at scale and how it can be fit together with our Git workflows. Inspired by such practices, we will design and document our approach and employ it in the release management process.
- Automatic deployment: Design a CI/CD pipeline in GitHub Actions to test, build and deliver iOS and Android apps to TestFlight and Google Play, respectively. We’re looking into using Fastlane to achieve this. We will provide detailed documentation on how to setup everything.
-
Add support for Expo’s Unimodules: Thanks to the efforts of the Expo team, we can leverage
[react-native-unimodules](https://docs.expo.io/bare/installing-unimodules)
infrastructure to cherry-pick SDK modules as we go instead of bringing in the whole thing and seamlessly integrate each one in the boilerplate.
Stay tuned! 💪
Meanwhile, you may visit the project’s website at https://react-native-with.moxy.tech/. Feel free to use it, give feedback and contribute! 🙏
Wrapping up
With React Native with MOXY, our team has an efficient way to kickstart apps that meet our business offerings and clients’ requirements.
Now, the developer experience is consistent across projects and our engineers are able to onboard new or ongoing projects with minimal friction. We’re now able to iterate and ship faster with a higher degree of polishing, two things highly appreciated by our clients.
Check out the most recent app we built with React Native with MOXY:
Enables quick and easy configuration and analysis of Tridonic’s LED drivers.
At MOXY, we build websites and applications that our customers can feel proud of, instead of being embarrassed and frustrated. Our Engineered Design process helps our customers have a deeper understanding of their challenges, and provides a clear path for solving them through careful design and implementation.
Looking for help with a project? Tell us a bit more.
Written by André Costa Lima
Top comments (0)