What I learned from developing iOS and Android apps with React Native
I recently developed 2 iOS and 2 Android apps from scratch with React Native and TypeScript and published them on AppStore and Google Play. Today, I’d like to share what I learned along the way.
This isn’t meant to be a complete list of best practices in any shape or form. Comments, suggestions and corrections are always welcome! :)
- iOS and Android platforms have distinct UI and UX design patterns. iOS Human Interface Guidelines and Android Design & Quality Guidelines are great resources to start studying them and correlating them with each other.
- React Native allows us to utilize a single codebase to build apps for both iOS and Android platforms. But that doesn’t necessarily provide an intuitive experience for both if you don’t design the UI and UX for the platforms you’re targeting. If your product managers, UI/UX designers, developers and testers are primarily iOS users or vice versa, they may not be able to create and maintain optimal and intuitive experiences for the other platform. Your team must treat both platforms as first-class citizens for design, development and testing.
- iOS and Android platforms may have different touch gestures and navigational patterns. E.g., on iOS, you may swipe from left to right through the edge of the screen to go to the previous screen. But on Android, you achieve that with clicking on the hardware/software back button of the device.
- You may create a user interface element from scratch with JSX and RN view components. You will have a consistent look on iOS and Android with this method. But iOS and Android users may have clear expectations for the UI/UX and your universal view component may not meet those expectations. As an alternative, you may use UI components provided by the underlying platform. In that case, your component will look and function natively on both platforms but may not necessarily integrate seamlessly into your UI design (color palette, styles, design characteristics, etc.) and may oddly standout.
- You need to create and bundle your image assets targeting different pixel densities (1x, 2x, 3x, etc.) to make them look sharp and crisp on all devices.
- You need to design and develop your app for different screen sizes in a similar way you would design a responsive web page. Flexbox comes to your help on tackling with this requirement.
- It’s a good practice to create interactive prototypes for UX testing before you actually start coding the app. It allows you to spot UX problems early on during the development process and avoid them before writing a single line of code. This saves you a lot of time and money in the long run.
- There are many easy to use tools out there you could utilize. Marvel, Framer are just two good examples and they integrate nicely with the Sketch app.
- Start with an MVP.
- Keep it simple.
- Release early, release often.
- Get feedback from users, validate your assumptions, learn from your mistakes, improve and iterate.
- Nearly all of the best practices that apply to React development also applies to React Native development since React Native is based on React.
- SOLID principles and almost all of the “twelve-factor app” principles can be and, in my opinion, should be applied to RN development.
Hardware and Performance
- Mobile devices have less powerful hardware than computers, and they are battery powered devices. You need to utilize those resources conservatively. E.g., you should be careful not to update your entire component tree, use shouldComponentUpdate and PureComponent extensively, use Reselect library or similar to compute derived data only when necessary, use immutable data, delay computations with InteractionManager during animations, screen transitions, etc.
- iOS and Android have different ways of handling user permissions for accessing certain kinds of user data on their devices. E.g. On iOS, you don’t need to ask for user’s permission to use storage but on Android you have to. For sending push notifications to iOS devices, you need to get user’s consent first. On earlier versions of Android you don’t need to ask for Location permission explicitly but on later versions of Android, you need to, just like you do on iOS.
- Unlike desktop computers, mobile devices don’t have high bandwidth, low latency connections for most of the time. Users of your app might be in densely populated urban environments with congestion or rural in areas with spotty data connections. You need to design and architecture your app to offer a reliable experience even on unreliable internet connections.
- You may develop your JS code app in any text editor or IDE you prefer.
- You may need to use Xcode, Android Studio and their command-line tools for creating app bundles and uploading them to the stores, debugging, profiling.
- XCode and Android Studio have different paradigms for a project, build, bundle and simulator configurations, and they provide different tools for instrumentation, performance monitoring, debugging, etc. RN developers need to get themselves familiar with both of them.
- A React Native developer may need to use native libraries and functions provided by the mobile platform, so learning some basic level of ObjC/Swift, Java would help them tremendously.
- Most dependencies could be shared between your web-based React app and React Native app, except for the View Components using HTML, CSS and interacting with DOM and libraries that rely on Node APIs. Having a multi-repo package/module pattern may help greatly.
- You’ll need to have distinct configurations for different deployments (development, testing, staging, production). It’s a good practice not to store your configuration in your code as constants. You may separate them from your code and store in environment variables. DotEnv files are very convenient for this purpose. But you need to configure XCode and Android Studio projects to utilize those DotEnv based configurations.
- You may debug your RN app with Chrome Developer Tools or React Developer Tools. Ordinarily, the JS code is executed within the mobile device itself. But when you connect it to Chrome Dev Tools for debugging, the JS code gets executed by the Chrome browser.
- Jest, Enzyme, and Detox are excellent tools for unit, component and e2e testing.
- You may need to conduct E2E testing on multiple versions of iOS and Android simulators/devices and also on different screen sizes to make sure that your app behaves consistently on all of them.
- Codepush is a great tool to deliver over-the-air updates to devices. It provides great flexibility to update your JS code bundle and static media asset bundle (images, sounds, etc.) without submitting a new version to the stores and going through the lengthy app review processes.
- There are multiple ways of handling dependencies. You handle JS dependencies with npm or yarn, iOS dependencies with CocoaPods and Android dependencies with Gradle. When you’re importing and using pure JS React components, using npm/yarn should be just fine. But if you’re importing an RN component that calls native ObjC, Swift, Java functions, then you’ll have to use CocoaPods and Gradle to import those dependencies into your project as well.
- 3rd party RN components are not necessarily following semantic versioning practices. Some of them do, some of them don’t. It becomes somewhat a challenge to figure out and track what release of a 3rd party component is compatible with what version of React Native. Some package maintainers are very responsive to the issues reported on GitHub and some of them aren’t. You may have to fork some components, do fixes, add features and maintain them on your own until your pull request gets merged into the upstream repo.
- React Native has a monthly release cycle. It is pre-1.0, so that means it’s API is not stable. You may encounter breaking changes between releases. Keeping the RN library in your project up-to-date is important. E.g. It’s relatively easier to upgrade from v0.52 to v0.55 as opposed to upgrading from v0.42 to v0.55 because there wouldn’t be as many breaking changes between subsequent releases.
- I find it safer and reliable to stay one or two releases behind the most recent RN release to avoid running into major or critical bugs. RN team usually releases PATCH versions with bug fixes following each MINOR release. It’s better to stick with an earlier MINOR version’s latest PATCH release. They tend to be more stable than the latest MINOR release.
Libraries & Tools
- Redux Thunk and Redux Saga are good middleware libraries for dispatching asynchronous actions to your Redux store.
- Firebase Crashlytics and Sentry are invaluable tools for logging errors and monitoring crashes.
Thank you for reading! Comments and claps are highly appreciated ;)
Feel free to connect with me on LinkedIn.