Lessons Learned From Building React Native Apps
Native apps build cross platform apps for both iOS and Android. My talk will be interesting for those who are venturing into this mobile space either from the web development background, or other mobile development experience like native development. The talk will help them see an overview of all the options they have to build scalable React Native apps. I have given React Native talks at several conferences and have been involved actively in the RN community. I have also authored the course React Native : The Big Picture on Pluralsight. I have been building RN apps for the last 4 years, and quite interested and involved in the React Native space.
Transcript
Adhithi Ravichandran: [0:00] Hello, everyone. Welcome to my talk on "Lessons Learned From Building React Native Apps." Thank you egghead for organizing this. Especially during a pandemic, having this kind of virtual talks are really nice to stay in touch with technology and with a bunch of folks. I really like this idea and I thank egghead for doing all of this.
[0:20] Who am I? I am Adhithi Ravichandran. I am a software consultant based in Kansas City. I primarily work on React, React Native, and GraphQL. You can follow me on Twitter to stay in touch.
[0:33] All right. What is React Native? React Native is basically a framework, which is used for building native cross-platform apps using JavaScript and React.
[0:44] I started working on React Native apps four years ago, when it was a completely different situation. React Native was still a brand-new concept and there were a lot of challenges we faced while building React Native apps with a brand-new library.
[0:59] Four years fast-forward today, React Native is a more mature framework, which has a comprehensive solution for you to build both iOS and Android applications, and you may not face many of the challenges that we faced four years ago while building React Native apps.
[1:18] In this talk, I have a few lessons that I'm going to talk about that may be useful to you, if you're starting out with React Native and going to start building application cross-platform.
[1:31] The first lesson is about iOS versus Android. The primary concept of React Native is that it is used to build cross-platform native apps. We don't use React Native to just build iOS apps or just build Android apps. If we were to do that, we would rather use the native languages like Swift or Java for Android or Kotlin, to do those.
[1:57] This is something we need to keep in mind because as developers, it does drift away from our head multiple times, while we're working on React Native.
[2:08] From my experience, about 85 to 90 percent of the code is shared between iOS and Android. This is a huge benefit of using React Native, because you don't need to maintain multiple codebases that cater to both iOS and Android.
[2:24] Instead, you have this one single large codebase, which you code in JavaScript and React, and it translates into native iOS and native Android. You do know that there's about 15 to 10 percent overhead, where you may still need to do a bunch of custom coding.
[2:44] Let's look at what's different.
[2:46] The first thing that you would notice when you do React Native is the tools that you use are different for iOS and Android. With iOS, you have to use what's called the XCode with your MacBook, and trust me, the only way this is going to work is using a MacBook.
[3:02] This would not work with a Windows machine, or it would be really hard to make it work with a Windows machine. You'll have to know some nitty-gritty details of XCode, and tips and tricks while you're working with it.
[3:14] With Android, you have a completely different toolset, where you'll be dealing with what's called the Android Studio. You might be familiar with that if you're a native Android developer.
[3:24] The toolset differences is a challenge, because when you're coding in React Native, and if you want to build and link libraries, and do other things, you have to deal with these tools. Unless you're using Expo, which adds another layer of abstraction on top of React Native, so you don't have to deal with these.
[3:45] Keep in mind that you have different tool sets for both iOS and Android and that is a part of the challenge.
[3:53] The next thing you would notice is that React Native basically translates your JavaScript code into native iOS and native Android components, and some of these components may look completely different on iOS and Android. For example, let's look at a date picker with iOS.
[4:12] This is the date picker that you would see which is basically a calendar and you can pick dates, and that's the iOS version. Whereas the Android version looks completely different, where you see a more of a calendar view and you pick the date from that.
[4:27] What's interesting here is that as a customer when you're using both iOS and Android, you will have to explain to your customers that these differences are because natively these components look different on iOS and Android.
[4:43] It's very hard to mimic the same calendar component that you see on iOS with Android, or mimic the same components for Android or iOS. We would never recommend doing something like that. Instead, you still have to use what comes out of the box, and realize that when they compile and when they're displayed, the components look different across multiple platforms.
[5:06] Your testing strategies and other UI strategies have to differ based on how they look on iOS versus Android. This is just an example. There are plenty of other components that would look completely different across the different platforms.
[5:21] The release process is also different for iOS and Android. With iOS, you have to deal with the Apple Store and you have to go through their release process and review their guidelines. This is way harder than the Google Play release process that you do for Android.
[5:39] Releasing your app on Google Play is a lot more straightforward, easier, and quicker. When you deal with Apple, it is kind of more stringent and takes time, so you might have to plan accordingly for your releases, because you can't sometimes release your applications within a day.
[5:57] Sometimes the review process takes a couple days with the App Store and for it to get passed. You could go back and forth with Apple support and so on. That's another thing to keep in mind, that although you're building a cross-platform app, there are differences when you end up releasing your application.
[6:16] When you get into the code details, React Native does offer some platform-specific code. The first thing we're looking at right now is the platform-specific styling. We have something called the Platform API that comes with React Native out of the box. You can use that for several things.
[6:33] You can use that to detect the version of your iOS, the version of your Android. You can also use that for specific style changes. For example, we have a stylesheet here, and you can notice that I have a container style, and I've given a flex property of one. Then, I'm using the Platform select API to determine if it's an iOS or an Android.
[6:57] If it is an iOS device, I'm going to give it a background color of red. If it is an Android device, I'm going to give it a background color of green. Things like that can be achieved where you do have some specific styling requirement, based on whether it's an iOS or Android.
[7:13] These are very common in React Native. If you look at a React Native codebase, you will see tons of platform-specific styling going on. That is basically to cater to the needs of both iOS and Android.
[7:26] If you do notice that you have tons of platform-specific custom code, you can always separate them out with different extensions. Here, for example, let's say you have a component called "MyComponent." This component is drastically different for iOS and Android. Then, it would be advised to separate them out as two different files with two different extensions.
[7:49] Your iOS file will have an extension .ios.js, and the Android component will have the extension .android.js. The good news here is that React Native will be able to detect the extensions and know which component to render, based on the device that the user is using.
[8:09] This is something I have used as well, but not as frequently as you may think. It's more of a very unusual component that needs to look completely customized on Android and different on iOS. If there were needs for that I did use these extensions. That way our code is clean and React Native can detect which one to render, based on the user's device.
[8:32] So far, we saw the differences that you'll encounter when you're coding in React Native between cross platform, iOS and Android. Some of them include the tools, the operating system, the platform-specific code, styling, native components, and so on. There are other challenges that you will face in addition to that.
[8:51] The first thing is that we as developers are inclined to test on our personal devices. That is actually a big problem, because as we code, let's say, we deploy, or we connect our device to the debugger, and we test real quick. Depending on what device that is, our code is catered towards that device, subconsciously.
[9:14] Let's say I have an iPhone. I'm quickly testing all of my changes on the iPhone. I may overlook how these look on the Android device and if there are other problems that I may encounter when I deploy on the Android device.
[9:27] The other challenge you'll see is that developers tend to run only one emulator at a time. I might be just used to running an iOS emulator, because it's usually faster and forget all about the Android emulator.
[9:40] In such cases too, there may be issues and bugs that go uncaught during the development stage, and you may encounter only after you deploy them, and somebody is physically testing this on a device.
[9:52] Some of the strategies that you can use to overcome these challenges would be develop with both iOS and Android emulators. It may be hard to say that, because the Android emulators usually slow down your machine, so maybe you can alternate.
[10:07] If you have a few developers on your team, you can have a rotation where someone works on the Android emulator and the other person works with the iOS, and then you switch a week later. Strategies like that do help uncover bugs and issues, before they go get deployed, and you're testing them.
[10:25] The other thing that I highly recommend would be automating both Android and iOS releases. If you're just going to automate only iOS releases, then it's going to be a favor for the tester, where they're just going to pick iOS and they're not going to test much on Android, because they have to manually install an APK.
[10:44] Make sure that your automation is done for both releases. That way your testers can swiftly test both iOS and Android without any issues.
[10:55] The other strategy is to have as many devices as you can get ahold of. Make sure you have devices of both iOS and Android. Make sure you have different screen sizes, you have tablets, iPads, Android tablets, other smaller phones, just to make sure that your application looks and works correctly across different devices.
[11:19] Finally, the major strategy is test early and often. I like to catch bugs and issues at the development stage. That way we don't reach a deployment issue or issues when you're actually testing it with beta testers. Make sure you test early and often across multiple devices. That's going to be the key when you're developing cross-platform applications.
[11:42] The next topic that we're going to touch is about organizing your styles. React Native is heavy on styling, because it is basically building your UI for your mobile application. Making sure that your styles are organized and closer to the component is very crucial.
[11:59] Let's take a look at this styling strategy, which we call as inline styling. You notice that we have a component called MyComponent, and we have some views and text, and view tags. You can think of the view tags like divs in React.
[12:16] Take a look here that the styling is given inline right there. I have styling for each and every element specified right there. If I were to develop all my components with this strategy of inline styling, it's going to look super messy and confusing.
[12:34] I prefer keeping the styles outside of these elements away from this component in its own stylesheet. That way, it's easier to read and not as messy as how this component looks.
[12:46] The strategy to organize your styles is, on the left side, you can see that we've created a stylesheet, and it's on its own separate file. I like to put all the styles for a specific component within its own stylesheet file.
[13:01] Then, you can take a look at the component here on the right side. It basically links to the styles from the stylesheets. We imported our styles from the AppStyle stylesheet and we've given each element a style which has been defined in the stylesheet.
[13:17] Now, our component looks a lot cleaner. You can refer to these styles anytime you want from the stylesheet, so the styling and the component are separated out. Let's look at an example to see how the folder structure looks when you organize the styles.
[13:35] In this example, you can notice that we have my app.js file here, and the style is imported from the AppStyle file. Notice that my styles are defined very close to the components, so it's easier to find, and I have my stylesheet defined right here.
[13:53] I've created a export for it, so we can export it outside in the app.js file. I also have a component folder, which is going to have all my components in there. I have a header component and I'm using the same strategy of defining the styles separately in a stylesheet.
[14:11] In this component, it is using the header styles. That's available within the styles folder here within the component, and it has a header style. This is how on all my projects I have my components organized and the styles organized. I have a separate folder for the styles and I keep them close to the component, so I can always refer to it and update it.
[14:35] Now, if I wanted to change something here, let's say I want to change my app style. I don't want a flex direction of row, I can always go back in there, change it to column, and it's going to reflect that pretty quickly here.
[14:49] I like to keep it clean and consistent, and separate out the styles, because we're going to have so many components and so many stylesheets, and we don't want to mix them up and have inline styling.
[15:01] To summarize some of the perks and organizing styles is you can reuse your styles across multiple components. You end up with maintainable code that is also easier to test.
[15:19] The next thing we're going to talk about is Hooks. Hooks is the recent addition to React and React Native. These days, I've been writing all my React Native components as functional components, which use hooks.
[15:33] This has been really great, because your components are more concise and you can utilize state within a functional component using the hooks. You don't need to worry about some of those lifecycle methods anymore, if you were going to go with this approach. Again, this is optional. You could use functional components or class-based components based on your preference.
[15:55] I'm not going to go into the details of writing functional components with Hooks. Instead, I'm going to talk about some of the custom hooks that I use in React Native, which is super handy in functional components.
[16:07] The first hook that we're going to look into is the useKeyboard hook. It's available as a part of the React Native community repo. This is super useful, and I use it a lot. It basically determines if your keyboard is shown. It also figures out the height of your keyboard and the coordinates.
[16:25] If you want to do this without this custom hook, the code that goes into it includes event handlers, waiting to see if the change happened where your keyboard is being displayed. This is super useful if you were to do some kind of calculations or changes to your UI, whenever the keyboard is pulled up by your user.
[16:45] The other hook that is useful when you're doing styling is the useDimensions hook. This one is used to get you the windows height and width, with which you can style your components.
[16:58] Another hook which is also useful for me is the useAppState hook. This one is used to tell you whether your app is active, or inactive or backgrounded by the user. You might have certain things that can happen to your app like logging them out, etc., based on whether it is active or backgrounded.
[17:17] Again, you don't need to use any event handlers to detect the change in the state. The hook is going to be telling you that. All you need to do is plug this hook in.
[17:29] The other hook is the useColorScheme hook. If your app responds to the light mode and the dark mode changes that your user has in their phone, you can use this hook to get the color scheme of the user's phone, and your app can respond accordingly.
[17:45] I also use the useAccessibilityInfo hook. That's super useful in telling us whether the user's screen reader is enabled, the reduce motion is enabled, and it detects the changes as well.
[17:58] You might also like the useDeviceOrientation hook if your app responds to landscape versus portrait mode. Sometimes we develop apps where it works only in the landscape mode or only the portrait mode. If your app needs to respond to that, you can use this hook, which will tell you every time the user changes the mode, which is really nice.
[18:20] There are also other hooks you can find in this React Native community hooks repo. You can always create your own custom hooks when it makes sense within your project.
[18:29] The next lesson we're going to talk about is don't reinvent the wheel. You're going to get requirements to add several features to your mobile app. Some of these could be your own custom code, your own new components, but there are times when there are common problems that you may encounter, which have already been solved for you.
[18:48] The best place to go start looking at these would be in the React Native community repo to see if there are existing libraries that you can utilize for your project, instead of trying to recreate and reinvent the wheel.
[19:01] Let's take a look at some of the libraries that are available in the React Native community repo. The React Native community has over 80 repositories, and all of these are vetted by React Native experts in the community. This has been a huge blessing, because we don't need to reinvent the wheel if there are already solutions available to us that we can use.
[19:22] Some of the repositories that I have used personally and find really useful are the React Native Camera, to have camera integration with the new mobile app, the React Native Picker. I've also used the hooks that we just saw in the previous slides. I also use the React Native Video to have video streaming within your application.
[19:42] There's also other cool repositories here, like the React Native Web View, which is super popular to build React Native cross platform on the Web. There are plenty of other repositories as well that you can check out, when you start working on a project, to see if you can use them.
[20:01] Another thing to keep in mind is if you do end up creating something cool, make sure to open source your work to help the community in return. This is a huge help for anybody getting into React Native, and that's what keeps it going, which is the open source nature of it and the community involvement.
[20:19] The next topic that I want to touch is to build accessible applications. Accessibility is a moral responsibility of developers like us. We need to try our level best to build applications that are accessible to a larger set of people.
[20:35] There are people with different types of disability and if we can help them use our application, and if we can implement some accessibility features, so our Applications are easier to use for even people with certain disabilities, then it is a huge win for us in terms of providing an accessible app for all.
[20:54] Let's take a look at what React Native has to offer in terms of accessibility. First things first, if you do have an iOS device, it comes with a screen reader built into it, that you can use for the purpose of accessibility. If you go into the Settings and click on Accessibility, you would see something called the VoiceOver.
[21:16] The VoiceOver is basically the screen reader that speaks items that it sees on your screen. You can tap once to select an item, double tap to activate and so on. Make sure that you turn this on to have the screen reader turned on on the device, so that we can leverage that within our React Native application.
[21:38] If you're using an Android screen reader, Android has built in screen readers as well. If you go back into the Settings and go into the Accessibility section, you can notice that there's something called TalkBack. That's something if you don't see that comes with your phone, you can install it from Google Play. TalkBack is the screen reader version that is available for Android.
[22:01] Within React Native, we're going to now look into how we can detect the state of your device. We want to make sure that the screen readers are enabled, so that our code can function accordingly. We will be using the useAccessibilityInfo hook that we talked about previously, to detect the state of the device.
[22:20] If you look into that, it has an API for finding out if the screen reader is enabled, and also if the reduce motion is enabled. All you have to do is use that hook and within your component, you can use the API to find that out and see if it is enabled or disabled. Based off that your accessibility features can be either turned on or turned off.
[22:43] This is an example of an accessible component in React Native. In this component, it's quite simple. If you take a look at our element touchable opacity, we have a flag that says "Accessible," and we've set that to true.
[22:58] By default, the touchable elements are accessible, you can add it to the view elements as well. By doing that, you're going to have all the children of your view element also being accessible. That's the first thing you do towards building your accessible component, which tells React Native that, that is a accessible element.
[23:18] The next thing we do is I've added something called the accessibility label. We can add labels to our elements. Here, I've added a label that says "Go back," so when the user's screen reader's turned on, it's going to read to them the word "Go back."
[23:35] You can in addition to the label also add what's called the accessibility hint. That's going to give them more information in addition to the label. It's going to tell them "Go back," and then read into them that says, "Going back really means navigating to the previous screen."
[23:50] These are some things you can do to build accessible components. Notice within our view element, we also have another prop that says accessibility role. That tells the user the purpose of that component or elements, so it tells the user "This is a button."
[24:09] Let's take a look at some of the other API's that are available with React Native, to make your component accessible. This is the official React Native documentation on accessibility. Here, you can see several APIs that we have available.
[24:23] We just saw the accessible property, we saw the accessibility label, accessibility hint, and also the accessibility role. There's also accessibility ignore inward colors.
[24:35] There are other accessibility properties that are very specific to either iOS or Android as well. We have something called accessibility state. That's going to tell you the current state of the component. Then, there's one called accessibility value, which I'm guessing represents the current value of that specific component.
[24:55] It's quite useful when you start developing components with keeping accessibility in mind and using these properties. Your application's going to be wonderfully accessible to somebody who's using a screen reader. Keep that in mind and check out all of the accessibility APIs and options that are available with React Native, so you can leverage them within your application.
[25:19] The next topic that we're going to talk about is my favorite, and it's about testing. There is a famous quote by Henry Ford that says, "Quality means doing it right, even when no one is looking." This quote is applicable to software development as well.
[25:39] Testing is a crucial piece of software development and as developers, we need to embrace testing with the much more open mind. There are different types of testing, which you know from the testing pyramid like unit testing, component-level testing, and end-to-end testing.
[25:56] I'm not going to be talking about unit testing and component testing. In React Native unit tests and component tests are pretty much done the same way as you would test a react component. I'm going to talk specifically about end-to-end test using Detox. Detox tests your mobile application while it's running in a real device or a simulator, and interacts with a just like a real user.
[26:22] You can compare Detox in the mobile platform to something like Cypress on the Web platform. If you use Cypress test to write end-to-end test for your Web application, Detox does pretty much the same thing for your mobile application.
[26:38] I've used Detox for my React Native applications and it gives a sense of satisfaction that our product is well-tested. We have quite a few end-to-end tests to prove that. QAs also love end-to-end test, because they can literally see this happening visually.
[26:55] They can either see this happen on their emulators or on the personal devices, where they can see that the automated test is running and it literally looks like a user is interacting with it.
[27:08] Some basics of Detox testing is that Detox emulates a user testing experience. It is essentially a gray box test, because it monitors your app from inside of it. It also helps automate all of your manual QA process. That way, saving you tons of time, and also making sure that there is a higher quality. It's simple to write and execute, because it's all written in basic JavaScript.
[27:38] Let's take a look at a sample Detox test, just to get a feel of what detox testing looks like. In here, in our test, you can see that we're using the common testing terminologies like "describe," "before each," "it." If you've used testing tools like Mocha, you may have encountered these.
"[27:57] Describe" tells us what the test suite is going to do. In our case, we've given it a name "example explicit." "Before each" is going to execute before each test. In our case, I'm going to have an async await function that says reload React Native. This is going to run before each test.
"[28:20] It" is basically every single test that is separated out, so you can give it a title. Our first test is should have a Welcome screen. Our second test is should show Hello screen after tap. Our third test is wait for should be exported.
[28:36] You can notice here that the code looks very simple and easy to understand. Within our "it" function, we have an async await. We're expecting an element and we're expecting the element by ID "Welcome" to be visible. This is a simple test that is just testing to see if an ID of "Welcome," an element with that ID is visible.
[29:00] In our second test, we're also going to do the next step, we're going to tap on the Hello button, we're going to use a function called "tap" to do that. Once that is done, we'll have an expectation that an element by text "Hello" is going to be visible.
[29:17] As you read a Detox test, it almost seems like you're reading some kind of simple English or an algorithm. Detox tests are also easy for QA engineers and other testers to write, but I prefer writing Detox tests as a developer as well.
[29:34] Make sure to check Detox testing for your React Native application. If that's not something you've done, I highly recommend that. Ultimately, testing makes your ship code with confidence, and that's what we all want.
[29:49] The final topic that I'm going to touch during this talk is about Continuous Delivery. Continuous Delivery with Web applications are quite easy, but when it comes to mobile applications, it could be challenging.
[30:06] I use Fastlane for automating a lot of my beta deployments and release processes. It's super awesome and takes a lot of pressure off the developer. Some of the things that Fastlane can do for you include generating screenshots, code signing, releasing your application.
[30:24] If you're a mobile developer in the past, you know that when you're releasing your application, there is a bunch of things that you need to do, and all of these are quite monotonous and painful. Fastlane can take some of this burden out of you and automate these things for you.
[30:40] For beta deployments, I use fastlane. On iOS, we use what's called TestFlight, and deploy our beta product to TestFlight. TestFlight can have a huge suite of testers who can test your product on beta. This is hugely helpful when we have a product which is still in dev and we want beta testers to first test it out, before it goes into prod.
[31:03] We use TestFlight for iOS. It's super easy where the beta testers just need to install TestFlight, and they can update to the latest versions as and when we release them.
[31:15] Similarly, on Android, we use Google Play for beta deployments. Our beta testers can get on Google Play and test the beta version from there.
[31:23] Make sure you have this set up in place for your deployments as well, because it's helpful when you have a whole bunch of testers testing your app, and you have these all set up and automated.
[31:35] This is how TestFlight looks. You can see here, that you can get information about your bills, the version numbers. You can include testers, you can add beta testers, external testers, and so on.
[31:47] With respect to the tester's device, they can see that they get an invitation to use this TestFlight for that app. Once they accept that invitation, they become a part of that beta testing. It is quite easy to set up, and it helps with thoroughly testing your applications.
[32:06] I want to conclude with saying that it is a great time to be working on React Native. It's come a long way. It has been quite a journey for me working on React Native, and I look forward to what is in store for future of React Native.
[32:21] If you have any questions, feel free to ask me now on egghead chat, or leave me a message on Twitter @AdhithiRavi. I would be happy to engage in any conversation related to React Native.
[32:32] Thank you egghead for having me here today, and giving me an opportunity to talk about React Native. I enjoyed being part of the eggheadTalk series, and I also look forward to the other talks as well. Thank you.