Charlie Luo

28 Apr 2020


PHSchedule

During the summer between my sophomore and junior years (2018-2019), my school completely changed the way classes were scheduled, from a system that had every class at the same time every day to a considerably more complicated system, found here:

Bell Schedule

As this schedule was basically uninterpretable to most students, the administration reached out to a teacher who was advising several app development and robotics clubs, who then reached out to a couple of students in those clubs, one of which included me. We were simply asked to develop a mobile app that could tell students what class they should be at any given time. At the time, I had experience developing iOS apps (primarily at hackathons), so I thought it would be a fun and simple project to work on over the summer with one of my friends. And, because we believed this would be such a simple project, we decided to use React Native, a framework that would allow us to write the app once and deploy to both iOS and Android at the same time.

We split up the work so that I built the frontend while my friend handled the backend – a full writeup of how he did it can be found here. I spent almost all my time learning how to use React Native, which ended up being the biggest challenge of the project. React Native is a great framework, especially if the developer has any React experience. However, the difficulty of installation, the mess of dependencies, the painful styling options, and the general difficulty of getting anything done made my work very slow.

How the final product looked (from my friend’s phone):

PHSchedule2

Issues

There were quite a few issues running through development – I’ll outline the highlights here.

API Calls

To display what classes a student had, we had to get the information from Powerschool, our school’s grading and class system. We talked to some administration and my friend built the API, so all I really had to do was call it.

The issue came up with callbacks and storage. If the user had called the API today for their schedule, I would rather not bombard Powerschool with more API calls. So, I wrote this, based off my login code:

_retrieveData = async () => {
    try {
      const response = await AsyncStorage.multiGet(['username', 'password', 'schedule', 'matrix', 'weekly', 'letterDay'])
        for (i = 0; i < 6; i++) {
          if (response[i][0] == 'username') { this.username = response[i][1]; }
          if (response[i][0] == 'password') { this.password = response[i][1]; }
          if (response[i][0] == 'schedule') { this.schedule = response[i][1]; }
          if (response[i][0] == 'matrix') { this.matrix = response[i][1]; }
          if (response[i][0] == 'weekly') { this.weekly = response[i][1]; }
          if (response[i][0] == 'letterDay') { this.letterDay = response[i][1]; }
        }
     } catch (error) {
       this.props.navigation.navigate('Login');
     }
  }

This hellish callback took so many hours to figure out, simply because there were so many different situations the user could be in. If the login information saved correctly, I should be using this callback. If it didn’t, then I would have to throw an error and go back to the login screen. If the login info wasn’t there but the data is still stored, then I could check the data in storage and use it to build the timeline. But if I had the login info but couldn’t connect, I should stay on the timeline screen with whatever data I had. This was complicated by strange rendering rules React has, making the async function calls so much more confusing. Though the API calls themselves were relatively simple, data management and storage became a major issue that I should have just written before starting, rather than on the fly.

Routing

Routing sucks and it’s not better in React Native. Maintaining state between screens became a major issue when dealing with our need for multiple screens:

PHSchedule3

If the app just had a login screen and the timeline, a StackNavigator could have passed username and password as props; though janky, it worked. However, we also decided on a settings/info page:

PHSchedule1

Routing necessitated a Navigator.js, where I put a StackNavigator and a TabNavigator together to create the flow I wanted. Passing props was the real issue. Upon reflection, I should have used a separate data saving method to maintain state throughout the app, as passing all the props needed for screen transition was incredibly messy. This lead to issues like screen transitions being displayed multiple times and rendering freaking out. This also directly lead into the final and most annoying issue, persistence.

Persistence

These issues all culminate into the huge tangled mess that persistance became. Through some initial research, it seemed like AsyncStorage was the best tool to store the student’s username and password. However, the module was often finnicky, especially when the app was reloaded – data would often be lost or incorrectly saved. So, I put multiple redundancies in so that if data wasn’t stored correctly, the app would call the API or request user input again. These redundancies lead to a huge headache, as new and unknown issues continued appearing whenever the app was reloaded; dealing with so many edge cases lead to more edge cases appearing. Everything was made worse by the lack of good documentation and support for React Native edge cases – there were so many abandoned threads and unresolved issues on Github and StackOverflow I had no idea what best practice I should have been following.

I think persistence should have been the first thing I figured out when approaching the app. I knew we would need to store user information and cache some sort of daily schedule, so I should have figured that out immediately, before routing and the API. Really, I should have built the foundation first and worried about styling the data later – I think the whole process would have been that much easier with that mindset.

Reflection

Almost a year later, I would read an excellent article by Eyal Gutherman about how Dropbox backed off from multiplatform code sharing frameworks like React Native. Reading the article is incredibly reminiscent of the work I did with React Native; from the convoluted development environment to the lack of interest and support for any issues that came up.

I think that multiplatform frameworks have potential; maybe Flutter will blow React Native out of the water and start a new age in mobile dev. However, two years ago, I wished I had never tried to use React Native and just stuck with the iOS code I knew. And, still today, I never want to see another of the React Native red screens of death. Maybe if I came from a different background, more JS and React focused, I would be developing incredible cross platform apps with React Native right now. However, from a mobile dev perspective, I’m gonna wait for something better.

My code for the frontend app can be found here - the most updated versions are all on the beta branch.