My first attempt at writing a mobile app happened more than 5 years ago.  I was in between jobs at the time, and decided to use the time to write a mobile app to get rich.  At the time, I had two main problems: I didn't have any ideas about how to write a mobile app, and I didn't have any ideas about what the app would do.  I think it was an uphill battle the whole way, from installing the Android Toolkit to laying out the UI.  Ultimately, I made something very small and basic, that ran on my Motorola Defy, abandoned my project, and found a job.

This time, after my trip to Europe, I had a very clear picture of the mobile app I wanted to create.  I wanted to create an app that would switch between two languages quickly, so two people speaking different languages could quickly pass the phone back and forth and be able to hold a conversation.  There were many times in Europe when I felt that it would've been very useful.

There were two sides to this project, both of which were mostly uncharted territory for me: whether I could somehow use Google Translate to perform the translations and the current landscape of mobile development.

Some digging led me to Google's Cloud Platform, which was their cloud offering, similar to Amazon's AWS.  I was no stranger to Google's Cloud offering.  From what I remembered, it was clunky, and extremely difficult to navigate.  In my past experience, I had ultimately cancelled my trial, and deleted the app I had running there, because it was so difficult to use.  Due to a recent Amazon AWS training I participated in, I felt that I was now better equipped at taking a fresh look at Google's offering.  Either they revamped the user experience severely, or the training really changed my perspective, but this time, I did not find it at all difficult to navigate through Google's Cloud Platform.  I was able to enable the Google Translate API, look at their pricing structure and documentation, and determine that it was possible to use their Translation API for my mobile app.  It was also very generous of them to provide $300 in funds as a trial for me to start with.

I then proceeded to research what development tools were available to me.  In my mind at the time, I had one framework in mind: PhoneGap.  The reason was because this framework allowed me to write the app in JS, CSS, and HTML, once, and be able to generate both iOS and Android apps from the same code.  As I researched it, I realized that while it would deliver as promised, it came with its own compromises - mainly in the realm of performance.  Essentially, PhoneGap would be using a web browser (WebView) to render the application.  I imagined that performance would be roughly equivalent to browsing mobile websites on a smartphone.  Ultimately, what caused me to seek alternatives was that I found articles where there was discussion that apps implemented using PhoneGap may lag from the simple action of scrolling, depending on the UI.  It seemed ridiculous to me that something as simple as scrolling could be unperformant, and would require some tweaking or optimizations.

However, the concept of writing code once, and being able to deploy on both Apple and Android appealed to me.  Thus, I wished to continue researching, to see if there's more performant alternatives.  Along the way, I found RhoMobile Rhodes (which I found the documentation to be lacking, and the tool relied on the business with many of the more advanced utilities being only available to the paid tiers), Famo.us (which had changed directions from being open-source to focusing on commercial products), OnsenUI, Ionic (which is based on Angular, which is very opinionated on how things should be done, on top of being big and heavy to start off with), and Electron.  Unfortunately, they all had the same basic architecture as PhoneGap, and so also suffered from the same performance issues.  I did not wish to compromise on performance, as I had read years ago that Facebook regretted using something like PhoneGap to write their app, as the performance discouraged people from using their app.

That was when I stumbled upon React Native.  React Native was similar to PhoneGap in that you write the application once, and then are able to deploy it to both Apple and Android.  However, a crucial difference existed that set React Native apart from the "hybrid" mobile app frameworks listed before: the code that was written was actually compiled into native code.  This was possible because React Native utilizes a bridge to talk to the actual architecture.  They have written many APIs that expose the native functions, so that they can be used.  App developers only needed to write their application in Typescript (or Javascript) the same way they would've written it if it were a React web application, which is a little different than CSS and HTML.  After compilation, there would be no weird performance problems with basic functionality.  For more information about the differences between PhoneGap and React Native, this webpage and this webpage really helped.

Because I had RiotJS experience from work, which uses very similar concepts as React, I was able to jump in and start developing, with not much of a learning curve.  While most of the time, the documentation for React Native was good, and I eventually solved every problem I encountered (except one), there were a few notable challenges I encountered throughout my time developing my app:
  • TypeScript was new to me, and there were some gotchas that I fell into when using it, as opposed to Javascript.  It took a while for me to figure out why my method calls would keep failing with a weird error message saying that "this" was undefined.  Turns out, I had two options: I had to either bind(this) to the method call, or use another syntax "() => {...}" to call the method so that I knows what "this" was.  This was particularly frustrating when I was trying to set event handlers for button clicks.
  • Some code examples for React Native were in TypeScript while others were in Javascript.  Since I was learning TypeScript on the fly, it was sometimes difficult to figure out what the equivalent TypeScript code was for the Javascript example I was reading.
  • There are three ways to test the application on Android: via Expo on a real device, via a Virtual Device, and via loaded onto a real device.  They each have their pros and cons.  The Android Keyboard, Drawer and StatusBar would not render correctly when using Expo or a Virtual Device.  To fix this, I ultimately had to just use an actual Android device to test, which made debugging harder, since I would not be able to see the console outputs.  But, at least there would be no question about how the app would look on a real device.
  • ListView is deprecated and should not be used.  FlatList should be used.  Many times, there would be links to old versions of the React Native documentation, where ListView was being used.  ListView is very unintuitive to use.
  • Many node packages/modules were available that provided the various React Native functionality I was looking for, so I never had to write any native Java code.  However, the packages themselves varied with respect to quality, reliability, and documentation.  Many times, I would find myself trying one package, only to get frustrated, uninstall it, and switch to another package.  In one particular case, I had to patch the package manually.
  • Javascript asynchronous execution was confusing enough.  TypeScript made it even more confusing.  Earlier on, I found myself not knowing when to use "await", "done()", "then", and "async".
  • AsyncStorage, the mechanism for persisting data in a React Native application, was also very non-intuitive.  The documentation was also lacking, and I had to go through lots of trial and error to figure it out.
  • While it is possible to change state variables via "this.state.xxx = yyy", by using "this.setState({xxx: yyy})", it triggers the UI to update any components that would be affected by the new value of the state variable.  This was an a-ha moment.
  • Adding a button to the navigation involved jumping through hoops and setting the onclick event handler to a state variable for it to be visible and accessible to the navigation.  This was very unwieldy.  Going back in the navigation was similarly unwieldy.
  • Showing and hiding components involves some creativity.
  • Reading files was also ridiculously hard.
  • Sometimes, running "react-native run-android" would fail, but running it again would succeed.
  • React Native developer(s) were closing bugs simply because they had been around for awhile with no updates (and no fixes, neither).
  • From start to finish, React Native needed various pieces of software: Nodejs, npm, Expo, Chocolatey, Android Studio (which the default installer was corrupted), 
  • There is no way to change the keyboard to a different language in Android.  This was the one problem I could not solve.  Understandably, it was done as a security measure, so a mobile app cannot change the input method to a keylogger.  The only way to solve this problem would be to write my own virtual keyboards for each language I wish to support.  This may be a long-term goal.
  • Use "npm install <package_name> --save" to install React Native node packages.
  • Use "npm update" to update the package.json.lock file.
  • Use "react-native link" to link packages that need to be linked.
  • Use "react-native run-android" to install the app onto an Android device (that is connected and detected by adb devices).
  • Use "cd android && .\gradlew assembleRelease" to build the production release APK.
  • Use "cd android && .\gradlew installRelease" to install the release APK onto a connected device.
  • Use "cd android && .\gradlew clean" to clear out the files that were generated to build the production release APK.
Having finally written the app, I now needed to deploy it onto the Google Play Store and the Apple App Store.  Those also involve a few lessons:
  • Google charges $25 for the privilege of publishing apps on the Google Play Store.
  • Apple charges $99 per year for the privilege of publishing apps on the Apple Store.  Because of that, I haven't published the iPhone version of the app.  I'm waiting to see how the app does in the Play Store before proceeding.
  • For Android apps using React Native, by default, it requests the READ_PHONE_STATE permission, which is a big deal, and makes a privacy policy mandatory.  That permission, if unneeded, as was my case, can be removed by adding "<uses-permission android:name='android.permission.READ_PHONE_STATE' tools:node='remove'/>" to android/app/src/main/AndroidManifest.xml.
  • RECORD_AUDIO is another permission that will cause the app to require a privacy policy.  Unfortunately, I had to have this permission, so I got a privacy policy from here.
  • Every time a new version of the APK is uploaded, it requires the android:versionCode to be incremented.  This is also found in android/app/src/main/AndroidManifest.xml.
  • Changing the App's name is not straightforward.  Similarly, changing the App's package name is a little more difficult.
  • Add one big icon for the app, and let Android do the scaling.
  • The release APK is larger than the debug APK.  This is normal.
Throughout this experience, I would say that React Native mostly made sense for me.  There were a few problems I encountered that I felt shouldn't be there, but for the most part, the combination of good documentation and a bearable development process means that I consider the cost I had to pay worth it for being able to write the code once and being able to deploy it into two mobile ecosystems.  For anyone who is considering React Native, they should also read this article, which covers some other points that may be a big deal.

Along the way, I also did some auxiliary research, such as researching whether names were taken or trademarked for the name I was going to name my app, whether there were any apps that already had the functionality I was seeking, which I did find one in the Play Store, but I think mine is better.

Ultimately, I accomplished my mission, which I had wanted to do for a few years now.  And, next time I travel, I will be better equipped so I don't find myself being laughed at by a frenchman sitting behind the counter of a tourist office.

Written on October 1, 2017
Updated on April 21, 2020. © Copyright 2022 David Chang. All Rights Reserved. Log in | Visitors