Building a live flight tracking app in React Native
Tags: react, reactnative, webdev, tutorial
Published: 2019-11-27 12:20:35

Wouldn't it be cool to have a way to watch flights move across the world; essentially see the flight's live location change in realtime? Of course, it would be, at least to a geek like me.

![flight tracking application preview](https://thepracticaldev.s3.amazonaws.com/i/ukjztkjoaow3967vk6q0.gif)

In the course of finding positive solutions, and easy implementation, but good enough to carry out this idea of mine - I came across [Aviation Edge](https://aviation-edge.com) - a global flight database with an API. But this a REST API and does not fulfill my real-time requirement.

And with the advent of event-streaming technologies, many data providers prefer streaming data in real-time using push protocols over letting their consumers poll for data frequently using REST - which is inefficient for both parties.

During this search, I found [Ably Hub](https://www.ably.io/hub). It is a marketplace for publicly hosted free, open and commercial data streams by various providers that are available to be consumed. Most of them are free to use. Luckily, I found just the thing that could scratch my itch with this project. A realtime data stream of flight data that uses data from Flight Radar called [Heathrow Flights](https://www.ably.io/hub/products/82). Even though the flight information coming through in this data stream is limited to flights originating or arriving at London Heathrow airport, this was enough for my project.

If you’ve never heard of Ably, you can read about them from [their site](https://www.ably.io) to learn more, and probably share what you learn with me too.

For this article, I’ll share how we can build a [Realtime flight tracking](https://expo.io/@opensourcesage/flight) app in React Native using the data from the Heathrow flight product on the Ably Hub.

Somethings that’ll be nice to have before we get started:
- Knowledge of JavaScript
- A free account with Ably
- Knowledge of React, React Hooks and React Native

### Features of the application:
-  *Arrival and Departure Screens*: each screen will have a list of airports labeled with location and the destination of aircrafts 

- *A search box*: to quickly find a flight with its IATAID (International Air Transport Association ID)

-  *The map area*: we’ll have a map that tracks the current location of a moving flight; a Polyline from origin to destination

### Step 1 - Creating a free Ably account
If you don't have a free account with Ably, you can create one [here](https://www.ably.io/signup#). If you already have an account, you can proceed to the [Heathrow Flights product on Ably Hub](https://www.ably.io/hub/products/82). This is a self-subscribe product. 


![Subscribing to the product](https://thepracticaldev.s3.amazonaws.com/i/fmm3sxgya7o5qhielf8q.PNG)

Locate the _'Subscribe to Heathrow Flight'_ button by the right corner of the product page as shown above. This will enable your Ably account to access the flight data stream from one or more of your apps. Let's go ahead and set one up next.

### Step 2 - Setting up an Ably App
We'll assume we already have an Ably free account and we've subscribed to [Ably's Hub Stream Product: Heathrow Flight](https://www.ably.io/hub/products/82). Now, we want to create an Ably app.

![Create an Ably app](https://thepracticaldev.s3.amazonaws.com/i/jx5que6skfgexqn85vnd.PNG)

If you've successfully arrived on your dashboard, you should see a blue button labelled 'create app'. Create an Ably app using the button, then enter a project name for the project/app, as shown below.

![Add a project name](https://thepracticaldev.s3.amazonaws.com/i/d2v8c7ioczc5cgtewfjb.PNG)


### Step 3 - Using Ably React Native Hub Stream Kit

I’ve created a starter kit to make building our project a lot faster and cleaner. The starter kit contains a quick setup of React Native with Expo Managed Workflow. Expo is highly recommended by the React Native team to build a small (or large project depending on complexity) with React. 


Quickly clone the project from GitHub using the command below:
```shell 
$ git clone https://github.com/ably/tutorials/tree/flight-tracking-app-starter-kit.git
```

Next, change directory into the project folder `cd flight-tracking-app-starter-kit`.

You'll want to confirm you have `yarn`, `expo-cli`, and `nodejs` installed on your machine.


#### Installations: 
- Node.js (https://nodejs.org)
- Yarn (`npm install -g yarn`)
- Expo-CLI (`npm install -g expo-client`)

We’d use Expo Client to test our application while building. Expo Client is currently available on both iOS and Android stores. Please, create an Expo account [here](https://expo.io/signup).

### Step 4 - Setting up locally
To get started with the project run `yarn` from the project directory your terminal.

```shell 
$ cd flight-tracking-app-starter-kit
```

Next, run `$ expo start` and also launch the running application on a mobile device.

### Step 5 - Obtaining Your Private API_KEY

Locate `env.js` in the root directory of your project and replace the code with the private API_KEY obtained from the Ably application you created in Step Two. Your API_KEY should look like this:


![Ably Private API_KEY](https://thepracticaldev.s3.amazonaws.com/i/m5oa9iwjqgpxw3ps72sh.PNG)


### Step 6 - Update and Modify Codes

Your folder structure should like this:
![Folder Structure](https://thepracticaldev.s3.amazonaws.com/i/3z9zmeqyvtvkc1j5trh5.PNG)

Navigate to the `hooks/ably.hooks.js` and update the `CHANNEL_SCOPE` with this code:

```javascript
const CHANNEL_NAME = '[product:ably-flightradar24/heathrow-flights]flights:airport:LHR'
```

![Ably Product Identifier](https://thepracticaldev.s3.amazonaws.com/i/p6ku6d7jeaqkncjrjrif.PNG)

The above channel subscribes to a data stream of airport information like the flights departing and arriving at Heathrow Airport, London.

### Step 7 - Connecting to Ably (Pub/Sub)

Ably uses a scalable and resilient messaging mechanism [Pub/Sub](https://www.ably.io/tutorials/publish-subscribe#lang-javascript). Ably Realtime service organizes the message traffic within applications into named [channels](https://www.ably.io/documentation/realtime/channels).

Add the code in `./hooks/ably.hooks.js` to connect and subscribe to a named channel.

```jsx
export const useAblyChannel = (channel, dependencies) => {

  const [onMessage, setOnMessage] = useState('Please wait..')

  const [isLoading, setLoading] = useState(true)
  //fetch channel data
  const [channelData, setChannelData] = useState(null)

  useEffect(() => {
   //add ably connection here 

    console.log('Ran Use Effects', channel)
     // know the current state of the application 
    ably.connection.on(function(stateChange) {
      console.log('New connection state is ' + stateChange.current)
      setOnMessage(stateChange.current)
      setLoading(true)
    })
    // use the channel scope in the application 
    const useChannel = ably.channels.get(`${CHANNEL_NAME}:${channel}`)
    useChannel.subscribe(message => {
      if (message.data.length > 0) {
        setOnMessage('Loading Data...')
        setLoading(false)
        setChannelData(message.data)
      }
    })

  }, dependencies)

  return [isLoading, onMessage, channelData]
}

```

Also quickly update `./context/ApplicationContext.js` to have [listeners](https://www.ably.io/documentation/realtime/channels#subscribe)

```jsx
export default ApplicationContext = ({ children }) => {
.....

  //add subscription listeners here 
const setChannel = iATA => {
    console.log(`This ${iATA} was clicked`)
    return ably.channels.get(`${HUB_STREAM}:${iATA}`)
  }

  const departureListener = message => {
    console.log('Still Listening Departure', message.data)
    message.data && subscribe(message.data)
  }

  const arrivalListener = message => {
    console.log('Still Listening', message.data)
    message.data && subscribe(message.data)
  }

  const subscribe = data => {
    setIsLoading(false)
    setArrivalsData(data)
  }
 // add unsubscribe listeners here
  const unsubscribe = (useChannel, type) => {
    console.log(`unmounting sub ${type}`)
    useChannel.off()
    useChannel.unsubscribe()
    type === 'arrival' ? setArrivalsData(null) : setDeparturesData(null)
    setIsLoading(true)
  }

  const setArrivals = (iATA, action) => {
    action === 'reset'
      ? unsubscribe(setChannel(iATA), 'arrival')
      : setChannel(iATA).subscribe(arrivalListener)
  }

  const setDepartures = (iATA, action) => {
    action === 'reset'
      ? unsubscribe(setChannel(iATA), 'departure')
      : setChannel(iATA).subscribe(departureListener)
  }
  
...
 
}

```

Next, update the `./screens/ArrivalScreen.js` with the code as follows:

```javascript 
import { ListCards } from '../components/ListCards'
import { arrival as channelData } from '../constants/RawData'
import { Container, Text, Content } from 'native-base'
```

The above imports dummy data structured in the form of a data stream; this gives you a glance of what the expected data looks like. 

```javascript 
import React, { useEffect } from 'react'
import { StyleSheet } from 'react-native'
import { Container, Text, Content } from 'native-base'
import { ListCards } from '../components/ListCards'
import { arrival as channelData } from '../constants/RawData'
```

Next, still in ArrivalScreen.js file, display a ListCard to show the origin and destination of the aircraft we want to watch in realtime. Add the codes below to display a ListCard: 


```jsx
export default ArrivalScreen = ({navigation}) => {
...
const Arrivals = channelData
    ? channelData.map((item, index) => {
        return (
          <ListCards
            key={index}
            text={`${item.origin} - ${item.destination} (${item.iataId})`}
            icon="ios-airplane"
            action={() =>
              navigation.navigate('PopModal', {
                iataId: item.iataId,
                action: 'arrival'
              })
            }
            rotate
          />
        )
      }) : []
...

  return (
    <Container style={styles.container}>
     <Content>{Arrivals}</Content>
    </Container>
  )
...
}

```

Repeat the steps for `./screens/ArrivalScreen.js` in `./screen/DepartureScreen.js`, and also rename 'Arrivals' constant to 'Departures'. Rename 

```jsx
...
import { departure as channelData } from '../constants/RawData'

export default DepartureScreen = ({ navigation }) => {

    const Departures = channelData
      ? channelData.map((item, index) => {
          return (
            <ListCards
              key={index}
              text={`${item.origin} - ${item.destination} (${item.iataId})`}
              icon="ios-airplane"
              action={() =>
                navigation.navigate('PopModal', {
                  iataId: item.iataId,
                  action: 'arrival',
                })
              }
            />
          )
        })
      : []

....
  return (
    <Container style={styles.container}>
      <Content>{Departures}</Content>
    </Container>
  )
}

```

Ensure you save and see changes as shown below in your mobile application before proceeding to Step Seven.

![Arrival Screen](https://thepracticaldev.s3.amazonaws.com/i/rl6ft5j7d2e8ids4bz6k.jpeg)
![Departure Screen](https://thepracticaldev.s3.amazonaws.com/i/by3jnsytab7na2u53o78.jpeg)

### Step 7 - Adding State Management

First, update the React's Context API. Open `./context/ApplicationContext.js`
Replace:
```jsx 
const HUB_STREAM = '<HUB_API_STREAM_CHANNEL>'
```
With:

```jsx 
const HUB_STREAM = '[product:ably-flightradar24/heathrow-flights]flights:plane'
```

The channel above subscribes to a data stream of aircraft information such as its current location, its route and the speed of the aircraft. 

Next, return back to `./screens/ArrivalScreen.js` and update code to use React Hooks for the state management of our application: `useAblyChannel(channel, [])`

```jsx
export default ArrivalScreen = ({navigation}) => {
...
  const [
    isLoading,
    displayMessage,
    channelData
  ] = useAblyChannel('arrivals', [])
...
...
  return (
    <Container>
      {isLoading ? (
        <LoadingScreen message={displayMessage} />
      ) : (
        <>
          <Content>{Arrivals}</Content>
        </>
      )}
    </Container>
  )
...
}
```

`./screen/DepartureScreen.js`

```jsx
export default DepartureScreen = ({navigation}) => {
...
  const [
    isLoading,
    displayMessage,
    channelData
  ] = useAblyChannel('departures', [])
...
...
  return (
    <Container>
      {isLoading ? (
        <LoadingScreen message={displayMessage} />
      ) : (
        <>
          <Content>{DeparturesScreen}</Content>
        </>
      )}
    </Container>
  )
...
}
```

Delete this import from both your `DepartureScreen` and `ArrivalScreen` since our data will be coming from live data stream using the hooks:

`./screens/ArrivalScreens.js`

`import { arrival as channelData } from '../constants/RawData'`

`./screens/DepartureScreens.js`

`import { departures as channelData } from '../constants/RawData'`

Next, open up `./components/ListCards.js` and add a little bit of spice to make the cards clickable. 

```jsx
export const ListCards = ({ text, rotate, action }) => {
...
  return (
    <Card noShadow>
      <CardItem button onPress={() => handleActions(action)}>
       ...
      </CardItem>
    </Card>
  )
...
}
```

Adding the `button` property to the CardItem makes the button clickable.

Save and play around with the project a little bit. Click on a ListCard and to launch the Flight Tracking. Your Application should look like the Gif we have played above. 


### Step 8: Cleaning up and Finalising

Let's quickly add a search function to our application. Update both `./screen/ArrivalScreen.js` and `./screen/DepartureScreen.js`


```jsx
...
import FabButton from '../components/FabButton'

export default DepartureScreen = ({navigation}) => {
...
...
...
return (
    <Container>
      {isLoading ? (
        <LoadingScreen message={displayMessage} />
      ) : (
        <>
          <Content>{Arrivals}</Content>
          <FabButton navigation={navigation} channelData={channelData}/>
        </>
      )}
    </Container>
  )
...
}
```


Save and right now your application should look and feel like this: 
![Flight Track Application Preview](https://thepracticaldev.s3.amazonaws.com/i/bnif48h5zzqu0hzidxr3.gif) 

You can see the [complete demo and source code for this app on GitHub](https://github.com/ably/tutorials/tree/flight-tracking-app-react).

## Summary
You’ve successfully built a realtime flight tracking application that tracks the real-world flights' location and shows it on a map. How cool!.