11 May 2018
So, since probably late November or early December 2017, I’ve been working on my first ever mobile app project!
It’s very exciting and it wasn’t even that big of a leap (I still don’t know Android Java or Swift) because the project is using React Native. Now, I had heard of React Native before as one of the big reasons to pick React as a frontend framework, because it can also help you generate a mobile application. As I’ve built up experience using it over the past few months and also used some of the common ecosystem tools, I’ve felt that I want to share some of my experiences and the “gotchas” I’ve worked to overcome. Obviously, this development space is rapidly changing, so consider all of this information immediately out of date!
React Navigation is great.
Let’s just start there.
Without it, I would certainly not want to have to built all the components necessary to have the Stack and Tab navigators that they provide. They all have fairly reasonable defaults and provide decent customization options.
However, I have found that the documentation can be severly lacking or non-existent for non-trivial customizations. For example, we wanted the app to have a more consistent look across both Android and iOS platforms (more on than below), and headers for stack navigators come with some OS-specific defaults to provide a native feel. Specifically, on iOS, header titles are center aligned, appearing in the middle of the screen. On Android, they are left aligned. We wanted all of the headers to be left aligned. This simple change is either completely obscured and totally undocumented, or near impossible short of simply supplying your own header component.
So, luckily, React Navigation lets us supply a custom header component to which it’ll pass the same props that it’d normally pass to it’s own header component. However, here again, the passed props are completely undocumented, many with similar or vague names and many levels of indirection. I wanted to be able to get the current screen’s title so I could display it in the header (with the proper left-alignment), but even after reading through the component code for hours, couldn’t figure out exactly how to get it. Instead, all I did was hardcode the title based on information I knew about the navigation and the app itself. Anyone who’s talked to me about code probably knows how much I hate this lack of isolation, but it is what it is.
Quick aside: My colleague and I even tried to fork the library so we could add an option to force a left-aligned header, even in iOS. After a few hours of walking through the code to process and merge options with defaults and calculate the left and right margins based on the OS, we still couldn’t get it to work.
This is great if you want your app to really feel like a natively built app. I’m sure that they wanted to err on the side of being too native-like when building React Native because they didn’t want to be seen as just another hybrid app solution.
Unfortunately, outside of literally customizing almost all of the styles for every single one of these components you use, there’s no easy way to have consistent styling across both platforms of your app. In response, the community has built a number of component libraries (Native Base, React Native Elements, etc.) that do exactly this and provide a consistent experience, so a big thank you to the React Native community for helping bridge this gap.
And speaking of styles, if you like CSS, you’re going to miss it. React Native has something like CSS in it’s StyleSheet API, but support can be limited for any library components you use and the library components themselves can just throw your styles away. If the library doesn’t provide a way to style one if it’s children (like React Navigation) then you’re left with just considering that a bug and trying to fork/PR it yourself or make a custom implementation. This leaves the modular nature of React components feeling a lot less modular.
And this is technically true, 100%, but it’s a little bit more than difficult. Native and Web components just have different APIs, and while you can provide abstractions and Native components can techincally render HTML, a lot of the paradigms just aren’t the same. Sorry!
When I first started looking at toolsets to build a React Native app, I read a lot about Expo and how it could shorten the developer feedback cycle. It gives you a releasable bundle and app that your beta testers can look at while you’re developing. I also read a lot about how all this greatness comes with a price: you can’t just add native code to your app to do anything you want (like interact with native APIs: Calendars, Photos, Maps, etc). This kept some developers away who needed to integrate with native API X and, thus, couldn’t use Expo.
We decided to take the dive and use it anyway, even though we’d need the Calendar API and that API wasn’t available when we first started (we saw it was on their roadmap, though). It brought tremendous value to the client, who could get regular, fast (sometimes even daily) updates. It also brought tremendous value to us as the developers because then the client stays engaged and can take an active part in finding bugs and usability issues. And trust me, the client did just that! Altogether, I wholly believe using Expo as really sped up our development cycle and even shortened the initial development period of the project (I’ll know it a few months when it launches)!
Oh, and Expo already came out with the Calendar API, just in time for me to add it to our app.
Even though I’ve encountered some issues in developing native apps using React Native, I still really love it. Honestly, if it weren’t for React Native and if I were to learn Android Java and Swift to build mobile apps with in the future, it would take awhile for me to get there. With this toolset, I feel so much more empowered and I feel really prepared, not just to talk about mobile apps with my clients, but to also respond to their needs for them without having to refer them to someone else just because “I don’t do mobile stuff”.
I think all of the above issues I’ve encountered are really just growing pains in this ecosystem. They can all be solved and made easier, some just by being more considerate of the developer experience when using the library or API. I know developer experience and customizability isn’t always the top priority, especially when you need to have something that works consistently first. I’ve really enjoyed building mobile apps and I’m probably going to be coming up with some pet projects just so I can have my name on a mobile app. :D