Improved commuting with Expo and React Native

Scott Pritchard
Exposition
Published in
16 min readFeb 4, 2019

--

My name is Scott Pritchard, and Iā€™m a Javascript developer in my late 20ā€™s based in the north of England. Iā€™ve been coding since around 2006 in my own time, creating projects that range from setting up and adapting a CMS and forum, building a desktop hardware monitor for Windows, building a live chat platform for a major outsourcing company and working on sportsbooks for some big names in the industry.

Today, Iā€™m going to tell you how I built my own commuting app and API to optimize my daily commute to and from work. First Iā€™ll cover how I discovered Expo, my inspiration for the project and my first attempt at the project. Then, Iā€™ll cover some key lessons I learned about user behaviour and UX.

The project Iā€™ll be discussing today is called Panther Commute (available on the Google Play Store for Android, and the Apple App Store for iOS).(At the time of writing, the app only supports mainland UK train stations and times)

Discovering Expo

Day to day, I work as a full stack developer but primarily focus on front-end projects including websites, web apps, and mobile development. It was June 2018 when I first heard about Expo.

Iā€™d mentioned to one of my colleagues that Iā€™d been trying to get started with React Native for my own projects but had trouble setting up react-native-maps due to the native parts of it. It was at this point that he mentioned Expo, briefly explaining some key facts about it. That evening, I had my task ā€” learn more about Expo.

Why Expo?

In addition to the native parts of adding libraries, I discovered that Expo had more to it than just libraries. Sure, the libraries were a big part of it, but the workflow it allowed made things easier. I didnā€™t need to compile the app on my system, I just had to write JS, let expo handle the bundle and compilation.

On top of this, I didnā€™t need any native libraries that werenā€™t included with Expo. It gave me all the libraries I felt I needed. Even to this day, Iā€™ve not found any native library that I absolutely need thatā€™s not included with Expo. Your mileage may vary, but Iā€™m a strong advocate for the platform because it simplifies the development process allowing me to focus on the key part ā€” developing my projects.

Iā€™d worked with mobile a little in the past through the Delphi Firemonkey framework. However, now that I work in the world of Javascript, that was no longer an option, and the costs for Delphi are exorbitant. If you think buying Apple hardware and maintaining developer licenses is expensive, that pales in comparison to the cost of even a single year Delphi architect license at over Ā£4000 GBP (even their lowest level professional license costs in excess of Ā£1200). In addition to this, the community around it has receded into near-hiding, and is even more disliked and dreaded than PHP.

Inspiration now arriving at Platform 1!

Inspiration isnā€™t endless. Unlike this train. Probably running on time too.

The project Iā€™d been wanting to work on was a train times app. The options that were out there didnā€™t quite offer things in the way I wanted them. I wanted information quickly, with minimal navigation needed.

It was also a perfect project for learning React Native ā€” lots of data, doing my own styling, navigation, all the goodies. Commuting apps do exist of course, but I found that there was always something that took more steps than it needed or were less than optimal for 1-handed usage.

My end intention was to provide users with the ability to view nearby train stations, view departures and arrivals at stations, bookmark stations and services, plan journeys, and view where a service was in as few taps as possible and without having to hand-juggle their device.

I had more than a few reasons for doing this. Firstly, I commute using a high powered electric scooter for the ā€˜last mileā€™ parts of my journey (a minimum of 3.6 miles per day, but often in excess of 9 miles) and take this on the train for the bulk of the journey. I need to know which platform to be at to ensure I can get a spot on the train and to prevent me from having to go in and out of lifts more than necessary. It weighs almost 30KG so stairs arenā€™t an option.

Secondly, knowing if my train is delayed and the reason why lets me prepare for the rest of the evening, and even to let relevant parties know if Iā€™m going to be late. After all, ā€œIā€™m going to be lateā€ looks bad, moreso if you canā€™t provide a reason.

Finally, it allows me to know if I can stay in bed a few minutes more in the morning. If my train is going to be late, no sense leaving the house early and sitting in the cold for 20 minutes more than needed.

Now that I had a project, plenty of reasons, and a framework to build with, I could begin. To cut a long story short, within 6 weeks, Iā€™d released my first React Native app, called ā€˜Pantherā€™, to the Play store. It made use of TransportAPI as itā€™s data source. Iā€™ve glossed over this part ā€” this is because it was the first version of the app, and thatā€™s not the primary focus of this article.

The goggles - they do nothing!

Considering Panther was my first project, I thought Iā€™d made a good impression. I was amazed that Iā€™d produced something that run on mobile without workarounds or wrapping a webapp in a webview. Looking at it now, itā€™s amazing to me that I like this UI and thought it was easy to use.

Everybody loves gradients!

Believe me, the bottom navbar was meant to be blue like the header. Before you ask, you are seeing double navigation of tab navigator with tabs inside. I think at one point in development, I had another level of tabs inside one of the screens.

Remember, this was after 6 weeks of learning a framework where I had only a little bit of previous experience (due to a project Iā€™d been assigned) and having only learned React ~3 months prior. I didnā€™t even use maps in this version of the project in the end so Expo might not have been needed.

With my hand on my heart, I can say without a doubt that the UI is a retina-searing mess of almost-neon.

It didnā€™t last long

It was only around 2 weeks after releasing this that I decided to rebuild. Iā€™ve waited for trains that have been delayed longer than that!

Iā€™d learned enough about RN and had enough willingness to rebuild the app avoiding the mistakes Iā€™d made the first time around. Around the same time, I moved from Android (Huawei Mate 10 Pro) to iOS (iPhone XS ā€” which had only just been released) for day to day usage.

The project had bugs. The ā€˜splash screenā€™ was scaled incorrectly (I put that in quotes because it wasnā€™t technically a splash screen in the way youā€™re meant to make them), scaling was way off, and of course there were the notch-issues. It had buttons and icons which did things but didnā€™t provide user feedback. Plus, it was bright purple. It worked, but it wasnā€™t what I had envisioned at all. You could say it was a project of exploring what was possible.

I could have updated the project to work with iOS, but I wanted to do things differently.

We can rebuild him. We have the technology.

Thus, in the middle of September 2018, it was decided. It would be rebuilt from the ground up. Iā€™d go with proper theming, using the brand-colours approach with primary, secondary and tertiary colours rather than ā€œwell, letā€™s use gradients everywhereā€. Iā€™d limit colours to ensure consistency, and make sure that my icons were all consistently styled.

Within a few days, Iā€™d created a basic rebuild that would lead me on a 6 month journey:

2 weeks of work on the next version of the app

Black and Yellow. Not the colours I ever expected Iā€™d put together for a UI but it turned out to work perfectly. I realized that this provided the dark UI that was much easier on the eyes and using a contrast colour which was less likely to interfere with sleep.

It had support for bus stops and train stations. It had 3 tabs, a hero unit (of sorts) to display information about a stop, and a header to allow displaying of the list icon. The layout works and it remained this way for a very long time. For the next few months, right up until December 2018, I kept this design.

Did I change it? I had to. Why did I change it? Usability and UX. The top right corner isnā€™t accessible, but itā€™s how you access a list of stations instead of the map view. The map itself is on the edge of the thumb stretch zone. Thereā€™s also information that really isnā€™t relevant to the user such as the stop number and geographical coordinates. Sure, they might want this information, but mostly it was just present without the majority of users caring about it.

Lesson #1 ā€” More data in your UI is not always better.

By the end of November, more features had been added, but the UI was still similar;

Those buttons still took up lots of space so theyā€™re touchable, but that icon in the top left was still inaccessible. The tabs had text and icons on them. Itā€™s good, but thereā€™s still so much more that can be done.

Day to day, I work with a truly world-class team of developers and designers, and Iā€™d picked up a few key design tips. Slowly these were materializing, but I hadnā€™t realized it.

The epiphany

The christmas holidays began, and I had plenty of time to actually sit down and design something better. I realized something ā€” Iā€™d been assuming that the app users wouldnā€™t understand what an icon did.

Thereā€™s psychology behind this ā€” people donā€™t naturally read first before taking an ā€˜automaticā€™ action. Take a look at the problem we have with doors and door handles. Even if a door says ā€˜pushā€™, if it has a handle on the same side as the sign, people will instinctively try to pull the door open. Their reflex action is much quicker than their brain processing the action of reading a sign, understanding it, and adapting their bodies action in response.

Labels are overrated and overused. An icon can represent your intention just as well while reducing space usage. There are well established icons which represent common tasks, such as geolocation, bookmarks, information, etc. Minimizing the processing the brain has to do allows users to easily navigate through your app.

Lesson #2 ā€” Icons and labels can both serve the same end result, but do so in vastly different levels of effectiveness.

Those huge buttons had to go. They may fit the theme, but they didnā€™t fit the usability criteria I had in my mind. The button in the top right was a mess on other devices, being out of alignment and even completely broken on some of them. The map needed to be the primary focus, and I should make use of the areas surrounding the notch.

The final lesson ā€” Make the primary purpose of a screen easily evident. If itā€™s a full screen feature like a map, adapt your UI around that to make it as easy to use as possible.

Thus, I set about refactoring the UI, and eventually ended with what is the current design;

The selected station name is clearly presented at the top. The header area has an overlay near it so that the clock and status icons are readable. The icons in the area above the tab bar represent what they do (timetable, bookmark toggle, station information). The timetable icon could be represented with several other options, but this represents that itā€™s a searchable list.

The tab buttons are just icons now, which represent what they do. There was room for UX improvement even here, not just by removing the labels. For example, if you press the left-most tab icon when itā€™s active, itā€™ll scroll the map to your current position.

Youā€™ll notice that the focused tab has a different colour and an ā€˜overlineā€™. I feel that it is imperative that users with visual disabilities are able to access the app as easily as possible. If a user is colourblind, they can still identify the active tab from the overline. At one point in development, I also adjusted the size of the focused icon to make it more prominent, but decided against this as it would break the UI on some devices.

The second tab along is the list of stations. If you press that when itā€™s the active tab, itā€™ll focus the search bar thatā€™s in that screen. This prevents the need to stretch to the top of the screen. These 2 icons also change when theyā€™re the active tab;

This is to indicate that the functionality has changed. To reinforce this, is an available behaviour, youā€™re presented with an overlay the first time you visit the search tab telling you that you can press the tab again to focus the search input box.

All of this was done with Expo, without detaching, and without any trickery. It started in Expo SDK 29, but was upgraded to SDK32 the same evening it became available (primarily in preparation for future plans).

I didnā€™t need to do anything complicated to make the app work. I didnā€™t need to deal with native linking, or cocoapods (trust me, Iā€™ve dealt with them in projects Iā€™ve been assigned, and Iā€™m not a fan due to the spectacular way things can fail and the incompatibilities between native libs).

Data now arriving at platform 3

What about train services though? So far, weā€™ve covered the first thing users see, but the app has more to it than that. Letā€™s take a look at the services screen;

The first thing that stands out is the layout. Expanding on the rule of not assuming users are stupid, consistency is key. The left side always contains times, the right side always contains platform, and the center always contains the station and other key information. If a train platform is yet to be confirmed, the platform will display as ā€˜TBCā€™.

You can see that it shows some departed services. What I found is that it was more difficult for me to identify if Iā€™d missed a train if it had simply disappeared from the list, so I opted to show a small selection of recently departed services. This will be split into itā€™s own section in the list in a future release.

We can see that each service has a lot of information. You can see the name of the destination of the train, the list of stops (which is a marquee), the platform, the scheduled departure time, and in case of delay, also the expected departure time. There is a lot of logic involved in all of this, ranging from the colours of each item to the list of stops to display, to the platform, and the times.

The next thing you might notice is the search bar. Itā€™s at the top, and thus not 1-handed accessible. This is an issue that I couldnā€™t quite solve in time for release, but Iā€™m aiming to fix in a future release.

Hereā€™s another example of that screen with different data;

As you can see, thereā€™s a lot of delays and cancellations here due to an incident but the information layout is consistent and easy to read. ā€˜Cancelledā€™ is reiterated but thatā€™s because I consider it a key piece of information that users absolutely need to know about.

Since taking this screenshot, the delayed services also show which stations the service will stop at. You see, even though I know my regular train services by heart, I realized I might want to get on a different train that stops at a specific station before the destination. Also, if one of my trains departs from a different platform to itā€™s usual one, then I can identify it much more easily even if itā€™s cancelled.

To put perspective on the logic of this screen;

  • Is the service delayed or cancelled?
  • Is the estimated time the same as the scheduled time?
  • Is the platform TBC or known?
  • Has the service already departed?
  • Does the service have a reason for being delayed or cancelled?
  • Is the service going to stop at only a selection of stations or will it stop at all of them along itā€™s route?

All of this logic has to work together seamlessly, and one piece of data or logic should not have a direct effect on any other ā€” they all have to be calculated independently to ensure accurate display of information.

Designing this page and the logic took around 1 week of casual work integrating the basics, and 3 solid days of work integrating the logic and a design that worked with all that data in a user-friendly way.

A bigger boat

Around the same time as Iā€™d been refactoring the UI, I discovered that Iā€™d been overusing the TransportAPI service, resulting in throttled service. This proved to be a big problem as it meant that not only was I potentially causing problems for other users of the service, but I wasnā€™t able to quickly load information in the app when I needed it the most. I hadnā€™t thought Iā€™d been overusing it, but the throttling was apparent and so I took it as a strong hint that I should look at another solution.

As I have a background in node.js development and had been itching to build a server-side project for a long time, I jumped at the opportunity to build my own rail API.

Thatā€™s right; I built my own API to go with this app, using the official services provided by National Rail (a few systems, one of which is known as Darwin). Firstly, by moving to their systems, Iā€™m given several million api hits every 4 weeks. This represents more than a 16000% increase compared to Transport API.

The downside to Darwin ā€” it uses SOAP and WSDL, something that doesnā€™t integrate nicely with Node (compared to JSON). Thankfully, I found that a library had been made only a short time before to handle this for me which allowed me to focus on parsing the data.

This also opened up many more possibilities. I could finally format data as I wanted it, do my own data correlations, provide support for incidents (e.g. maintenance work), etc.

Most importantly, I could optimize for speed, data, or different endpoints to choose between speed or data, and build the service as I grow.

Euston, we have a problem

Euston Station ā€” source: https://commons.wikimedia.org/wiki/File:Euston_station_MMB_78.jpg (Licensed under Creative Commons Attribution-Share Alike 2.0 Generic license).

I found myself with a data predicament still. See, when I was using TransportAPI, they provided accurate GPS locations for stations and a comprehensive list of stations within a radius. The data-set for these was impossible to find on itā€™s own, especially when combined with other information. I had to resort to using a mix of data in JSON, XML and CSV formats from 6 different sources and merging it together. Each data set had different fields. Compiling the information into a single consistent data-set was a huge task and took around 4 days. By this point, we were into 2019 and work schedule was about to resume.

By completing this step, I could share this data with both my app and my API. This allowed Panther to have offline-first support for station information. I could cross-reference data in both places to make both projects better. I still found that some stations were missing. It was at this point I adapted my API to request missing information from the official data sources. If a user viewed a list of departures at a station, and a train service was passing through a station that wasnā€™t recognised, it would request data for that station and update the API data source. After a few days, it had gathered information for around 60 more stations than Iā€™d compiled.

Building the app for myself

One interesting development in recent times is that the expo team released Turtle CLI. This tool allow me to build the apps on my mac mini. Combined with the ability to export the bundle, Iā€™ve been able to setup my own mini CI process using only NPM commands and webhooks. You could use a true CI tool of course, but I have no need for anything that complex right now.

By building it on my own machine, I can go from starting bundle compilation through to being uploaded to Testflight in around 8 minutes. Add 4 more minutes and I also have an Android build ready to go.

Conclusions and future plans

This project taught me how to refine my development process and truly understand what UX is really about. Despite taking 6 months to release, Iā€™ve got a much better app than I would have if Iā€™d released in October like Iā€™d planned to do so originally. It taught me some of the key pillars of UX, allowed me to build an API to overcome some serious limitations, and took me through many of the pages in the expo documentation.

Panther is available already, but the development doesnā€™t stop. My next plan is to add support for TFL (Transport For London) to allow London Underground users to see timetables and stations too. This will require new UI elements and some data wizardry. Fixing up the UI issues surrounding search, and reinforcing the one-handed support, are also on the agenda.

Loved this? Catch our thoughtful guest-blogger, Scott, on Github and Twitter!

--

--

Node Nerd. React Native Graduate. Full stack JS dev. I build APIā€™s and appā€™s for those APIā€™s. Creator of Panther Commute app.