Developing a Mobile App with GraphQL

How our Float Schedule App handles thousand of items at the same time using React Native.

Artem Yavorsky
Float
7 min readJun 7, 2018

--

There is no one size fits all approach to building an app. A beginner might start out with a small application with minimal functionality, poor performance, non-optimized API requests, and a codebase that will need refactoring someday. This might be a good solution for most of your clients who can use the app as needed, while you gradually enhance it over time by improving the code and resolving non-critical bugs.

That’s a solid approach, but it certainly doesn’t guarantee a successful application, and it’s definitely not what we chose to do when we set out to build our mobile app. In this article, we’ll describe and share some of our experiences building a high-grade app with new technology and without much precedent or guidance to rely on (the keyword is new).

As a view library, we picked React. Sometimes it’s hard to achieve the performance you want just by creating an application with React, but it’s a helpful tool that allows you to create everything from the simplest applications you can imagine to huge complex applications that will work in both web and native environments. In that sense, you can be confident you won’t have to waste any time in the future migrating your codebase to something new. React’s ecosystem is so huge, in fact, that more than 55K results can be found by just typing react from npmjs.com.

There is often a misconception that React is just a library to be used for client-side browser oriented website development. React Native is a framework that refutes that by allowing you to develop mobile applications. It provides a set of building blocks that look and feel like native mobile application elements, but are actually glued together using JavaScript and React. This approach simplifies and accelerates development by decreasing the expertise required, and it creates a universal tool that serves as the basis for building iOS and Android apps.

While we were sure about using React as a view library, there was some discussion about a state management library and side-effects handling. We initially thought it would be easiest to use Redux as a state management library, or handle it via component state. But our data structure is compound, and we didn’t want to increase the complexity of our data handlers by iterating it through too many entities before passing it to the render.

So we decided to try something new. GraphQL looked like an excellent choice, as it would allow us to get rid of a huge amount of complexity in our client-side state management and reduce the scope of our code to just render in the UI. We could load people by demand with some nesting, and prevent extra code complexity and additional operations on the client side.

Let’s look at a simple example. Without GraphQL, our client might look something like this:

  1. When <Calendar/> component mounts, invoke action creator to fetch matches by Id and normalize data:

2. Once the data is normalized — update Redux store with latest data.

3. <Calendar/> component receives all of the match data via props and filters it down to render all data properly.

Looks like a lot of steps to show even simplest schedule in Calendar component. This approach also makes us spend a lot of time on how we should retrieve our data.

Let’s contrast that with GraphQL:

Presentational component is connected with a higher-order component that just loads people with nested entities.

And that’s it! Much more declarative, because we’re only focusing on what data we need to render the component. We even could optimize it and use Fragments:

So we don’t need to explicitly declare every field we need. The JSON for this query looks like:

With this structure we’re receiving only tasks that should appear on the schedule. We can use infinite scrolling with fetching some people by from: ID and first: Int params and start, end range for tasks inside it. It looks really awesome, but we also wanted a tool that could help us manage these paginations, connect data with React’s components, etc.

Enter Apollo.

Apollo provides a powerful caching mechanism that compares data you’ve passed and fetches only when something is changed. For example, we could just update end for tasks or from for the whole schedule, and new data fetching is processed. The app is not only a lot slimmer because we’re using Apollo, but it’s also more declarative since our components only request the data that they need. It works well with both React and React Native apps. Moreover, Apollo has an awesome official blog, hundreds of articles on Medium, and tons of examples on Github repo that are resolved quickly.

In terms of managing state, Apollo creates its own internal redux store to manage queries and their results by default. It can also be integrated with an existing store instead, which is what we did. We hooked Apollo into our existing redux store to maintain one source of truth. We’re using redux as our state management tool to store application related data, such as filters and sorters, and it fits our needs perfectly. React Native also provides a convenient way to store persistent data in a key-value way using AsyncStorage, and we are actively using it to store device-specific data that may not be stored in a global database and requested from the backend.

We picked Expo as a wrapper since it supports more features for fast and productive development than a high-grade web application has. By just running create-react-native-app you receive a zero-configuration project with live reloading, and a simple 1-step launch on your emulator or real device, creating a standalone build that you can easily upload for testing and App Store publishing. You could handle most of the cases you need for development with GUI (Expo XDE) or with exp CLI. So there are just a few exp commands from empty directory to native app you can deploy to the App Store. But zero configuration by default doesn’t mean that you can’t configure your app at all. With app.json you can update some trivial stuff like icons and app names or even XML values from info.plist.

Using new and powerful tools like these help speed up the development process, without worrying too much about infrastructure. In terms of performance (a vital component of any app), we added some logic into shouldComponentUpdate hooks, so if you’ve updated a task for a specific person, other ones won’t be re-rendered. We could have just used PureComponent, but we also didn’t want shouldComponentUpdate pass if a task was changed, but no visual effects for it were applied. There are also some instances where we wanted to show updates immediately. Fortunately, Apollo has a built-in feature that allows us to process an update before a request is finished, and revert it back if the request is unsuccessful.

Navigation was tricky because it was hard to pick the library we needed by just taking a quick pass. In the end, we picked React-Navigation. It’s convenient, covers most of the cases, and it provides good native navigation mapping. For the bottom bar we are using a custom tab bar with enhanced lazy loading. That means that initially we won’t have a request for each tab, but during an idle period, some requests might be processed in the background.

Project structure was interesting, as we already have a web application with similar functionality. We moved all projects into a monorepo handled by Lerna. It’s an awesome tool that makes it easy to organize monorepositores with just a few commands. Just running lerna bootstrap is enough to create semantic links between projects and incrementally build every one. We found a reusable method for both apps helpers by creating a separate project called float-core. By having the same version in the package’s root and in dependencies for other projects, we can just create symlink for it by bootstrapping it with Lerna. After we provide a change for one of the core feature, we don’t need to fix it for all kinds of projects we have. Updating only the core package applies all changes for related projects. To be consistent in styling, we’ve created an internal library (float-ui), where we can handle different types of components that look the same but work on both native and web environments.

There are always complexities like these for software developers to address while working within the bounds of the latest technologies and best practices. In the end though, we know we’ll ultimately be judged by the speed and performance of the application. We’ll continue working hard to monitor the code quality and follow the best practices with our app.

You can download the Float Schedule App at: https://float.com/ios-app

--

--