Realtime Geolocation Tracking with React Native
Tags: reactnative, react, geolocation, pubnub
Published: 2019-07-15 23:04:37

---
canonical_url: https://www.pubnub.com/blog/realtime-geo-tracking-app-react-native/
---
At PubNub, we believe in powering the global frontier of <a href="https://www.pubnub.com/blog/connected-shared-experiences-a-developers-guide/?utm_source=Syndication&utm_medium=DevTo&utm_campaign=SYN-CY19-Q2-DevTo-July-15">connected experiences</a>. Whether it be the burgeoning <a href="https://www.pubnub.com/solutions/iot/?utm_source=Syndication&utm_medium=DevTo&utm_campaign=SYN-CY19-Q2-DevTo-July-15">paradigm of IoT</a> or the relentless expansion of online applications, our mission extends to Internet technologies of all shapes and sizes.

With our fully supported <a href="https://www.pubnub.com/docs/react-native-javascript/pubnub-javascript-sdk/?utm_source=Syndication&utm_medium=DevTo&utm_campaign=SYN-CY19-Q2-DevTo-July-15" target="_blank" rel="noopener noreferrer">React SDK</a>, developers now have the freedom of realtime web-app development at their fingertips.

In this article, we will show you just how easy it is to build realtime<span style="font-weight: 400;"> <a href="https://www.pubnub.com/solutions/realtime-updates/?utm_source=Syndication&utm_medium=DevTo&utm_campaign=SYN-CY19-Q2-DevTo-July-15">geolocation tracking</a> with </span>one of the most popular mobile app frameworks. Whether you want to build a rideshare app, <a href="https://www.pubnub.com/blog/what-is-the-on-demand-economy/?utm_source=Syndication&utm_medium=DevTo&utm_campaign=SYN-CY19-Q2-DevTo-July-15">on-demand delivery service</a>, or a Pokemon Go game, this article aims to give you all the necessary tools and skills to get you on your way!

Today, we will be building a simple geotracking app that renders multiple users on a map view. Users will be able to toggle their location permissions as well as click a button to zoom the map on their location. And of course, it wouldn't be PubNub if we didn't include <a href="https://www.pubnub.com/products/presence/?utm_source=Syndication&utm_medium=DevTo&utm_campaign=SYN-CY19-Q2-DevTo-July-15" target="_blank" rel="noopener noreferrer">PubNub Presence</a> to track how many users are currently online in the app.

![](https://www.pubnub.com/wp-content/uploads/2019/06/Screen-Shot-2019-06-26-at-1.30.38-PM.png")


Before jumping right into the code, be sure you <a href="https://dashboard.pubnub.com/signup/?utm_source=Syndication&utm_medium=DevTo&utm_campaign=SYN-CY19-Q2-DevTo-July-15" target="_blank" rel="noopener noreferrer">sign up for a free PubNub account</a> so we don't run into any issues later.
<h2>Step 1: Setting Up Your Environment</h2>
In this section, we are going to install the necessary tools and dependencies to be able to simulate, run, and test our react-native application.

The first tool you'll need to add to your set is Xcode. For Mac users, you can simply download Xcode for free in the app store. For PC users, you will need to simulate Mac OS with a Virtual Machine if you want to develop your app for iPhone. You can see how to do this <a href="https://codewithchris.com/xcode-for-windows/" target="_blank" rel="noopener noreferrer">here</a>.

The next tool we're going to install is <a href="https://developer.android.com/studio?gclid=EAIaIQobChMInP6pp6r74gIVE8JkCh2f9A0XEAAYASAAEgJID_D_BwE" target="_blank" rel="noopener noreferrer">Android Studio</a>. This will allow you to develop your app for Android.

Next, you're going to install the heart and soul of our app: React Native. This is an open-source platform developed by Facebook that has become very popular over the years. React Native allows developers to write their applications in one language across multiple platforms, which will make our job a lot easier for developing for iOS and Android.

To set up our React Native development environment, we'll be using the React Native CLI, which will allow us to quickly install libraries, link packages, and simulate our app.

Assuming that you have <a href="https://nodejs.org/en/download/" target="_blank" rel="noopener noreferrer">Node 10+</a> installed, you can use npm to install the React Native CLI command line utility:

```javascript
npm install -g react-native-cli
npm install -g react-native
```

Then run the following commands to create a new React Native project called "YourProject":

```javascript
react-native init YourProject
cd YourProject
```

To see if everything's working properly, run this command to test your app with Xcode's iPhone simulator:


```javascript
react-native run-ios
```

## Step 2: Installing and Linking Libraries

Now we are going to install the libraries we are going to use, then link them to our React Native app. 

The first library we are going to import and link is PubNub's React SDK for handling data streaming. In your project directory, install the library with:

```javascript
npm install --save pubnub pubnub-react
```

and link the library with:

```javascript
react-native link pubnub-react
```

Next, we'll need the [react-native-maps library made by Airbnb](https://github.com/react-native-community/react-native-maps) for our interactive map API. Install the library just as before:

```javascript
npm install react-native-maps --save
```

and link the library with:

```javascript
react-native link react-native-maps
```

Lastly, we will install the react-native-responsive API, which will make styling our components easier:

```javascript
npm install --save react-native-responsive-screen
```

and then link:

```javascript
react-native link react-native-responsive-screen
```

## Step 3: Building the App

Now it's time to finally start building our app in React Native!

### Importing Libraries

Open up your App.js file and import the libraries we installed earlier along with some basic React-Native components.

```javascript
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View, TouchableOpacity, Switch, Image} from 'react-native';
import {widthPercentageToDP as wp, heightPercentageToDP as hp} from 'react-native-responsive-screen';
import MapView, {Marker} from 'react-native-maps';
import PubNubReact from 'pubnub-react';
```
### Constructor and State Variables

In the same App.js file initialize a constructor to pass in our props as well as initialize a PubNub instance.

```javascript
constructor(props) {
  super(props);

  this.pubnub = new PubNubReact({
    publishKey: "YOUR PUBLISH KEY",
    subscribeKey: "YOUR SUBSCRIBE KEY"
  });

  this.pubnub.init(this);
}
```

As you can see, we first declare a PubNub instance variable with the Publish and Subscribe keys we were given when [signing up for a free PubNub account](https://dashboard.pubnub.com/signup/?utm_source=Syndication&utm_medium=DevTo&utm_campaign=SYN-CY19-Q2-DevTo-July-15). Then we initialized the PubNub instance at the end of the constructor.

_NOTE: It is very important that the PubNub instance is initialized at the end of the constructor as the code will not work if done otherwise._ 

Now let's create some state variables that we'll need while our app is running. If you haven't used state in React yet, it may be useful to read up about [state in React Native](https://facebook.github.io/react-native/docs/state) before moving on.

```javascript
constructor(props) {
  super(props);

  this.pubnub = new PubNubReact({
    publishKey: "YOUR PUBLISH KEY",
    subscribeKey: "YOUR SUBSCRIBE KEY"
  });

  //Base State
  this.state = {
    currentLoc: { //Track user's current location
      latitude: -1,
      longitude: -1
    },
    numUsers: 0, //track number of users on the app
    username: "A Naughty Moose", //user's username
    fixedOnUUID: "",
    focusOnMe: false, //zoom map to user's current location if true
    users: new Map(), //store data of each user in a Map
    isFocused: false, 
    allowGPS: true, //toggle the app's ability to gather GPS data of the user
  };

  this.pubnub.init(this);
}
```

While most the state variables seem fairly intuitive for a geotracking app, the users map requires further explanation. 

The users map will facilitate how we render multiple users on our app. Each entry in the map will represent one user and will map to the specific in-app data that user contains (GPS coordinates, UUID, allowGPS, etc). We will then use PubNub to publish JSON data updates from each user to update the mapping and re-render the application's state variables accordingly. You will see this in action in the later sections. 

For example, if we want to update a user's allowGPS variable, we publish a JSON object to update that user's variable mapping:

```javascript
this.pubnub.publish({
  message: {
    hideUser: true
  },
  channel: "channel"
});
```

[![](https://www.pubnub.com/wp-content/uploads/2019/06/Blank-Diagram.png)](/wp-content/uploads/2019/06/Blank-Diagram.png)

### PubNub

First, declare an asynchronous function in the **ComponentDidMount()**.

```javascript
async componentDidMount() {
  this.setUpApp()
}
```

In order to start receiving PubNub messages within our app, we must declare a PubNub event listener followed by a PubNub subscriber callback, specifying the channel like so:

```javscript
async setUpApp(){

    this.pubnub.getMessage("YOUR CHANNEL", msg => {

/*------------WE'LL IMPLEMENT THIS LATER------------*/

    });

    this.pubnub.subscribe({
      channels: ["YOUR CHANNEL"],
    });
  }
```

We'll further implement this function as we later build upon our app.

### React Native Maps

We will now start implementing the interactive map for our users as well as track their GPS data. 

To collect a user's position, we implement the react-native-maps **watchPosition()** function below our PubNub subscriber:

```javascript
//Track motional Coordinates
navigator.geolocation.watchPosition(
  position => {
    this.setState({
      currentLoc: position.coords
    });
    if (this.state.allowGPS) {
      this.pubnub.publish({
        message: {
          latitude: position.coords.latitude,
          longitude: position.coords.longitude,
        },
        channel: "channel"
      });
    }
    //console.log(positon.coords);
  },
  error => console.log("Maps Error: ", error),
  {
    enableHighAccuracy: true,
    distanceFilter: 100 //grab the location whenever the user's location changes by 100 meters
  }
);
```

You should now begin to see the reasoning for our user map framework. After we collect the position coordinates, we publish the latitude and longitude data to the channel. The channel will later update this user's location data based on the publisher's UUID. 

Now just like any other map application, we should add a function to center the map on the user's location if a button is pressed. To add this functionality, add this function implementation.

```javascript
focusLoc = () => {
   if (this.state.focusOnMe || this.state.fixedOnUUID) {
     this.setState({
       focusOnMe: false,
       fixedOnUUID: ""
     });
   } else {
     region = {
       latitude: this.state.currentLoc.latitude,
       longitude: this.state.currentLoc.longitude,
       latitudeDelta: 0.01,
       longitudeDelta: 0.01
     };
     this.setState({
       focusOnMe: true
     });
     this.map.animateToRegion(region, 2000);
   }
 }
```

When called, this function will center the map's viewing region on the user's current location. 

Lastly, if we want the user to have the ability to turn off their GPS location, we need to toggle the allowGPS state. To do this, also add this function to your code.

```javascript
toggleGPS = () => {
   this.setState({
     allowGPS: !this.state.allowGPS
   });
 };
```

### User Data Aggregation

Now, let's go back to the PubNub event listener we defined earlier. The purpose of the event listener for this app is to take the data updates published to our channel and update the state variables of our app accordingly. 

In order to update the users map, we'll first initialize a copy of the mapping to manipulate:

```javascript
this.pubnub.getMessage("channel", msg => {
  let users = this.state.users;
});
```

We then check if the incoming message is a request from a user to hide their GPS data and remove them from the mapping accordingly.

```javascript
if (msg.message.hideUser) {
  users.delete(msg.publisher);
  this.setState({
    users
  });
}else{
/*something else*/
}
```

Otherwise, the message contains data updates for a user and we must declare a new user (with the updated values) to replace the old one.

```javascript
else{
        coord = [msg.message.latitude, msg.message.longitude]; //Format GPS Coordinates for Payload

        let oldUser = this.state.users.get(msg.publisher);

        let newUser = {
          uuid: msg.publisher,
          latitude: msg.message.latitude,
          longitude: msg.message.longitude,
        };

        if(msg.message.message){
          Timeout.set(msg.publisher, this.clearMessage, 5000, msg.publisher);
          newUser.message = msg.message.message;
        }else if(oldUser){
          newUser.message = oldUser.message
        }
        users.set(newUser.uuid, newUser);

        this.setState({
          users
        });
}
```

We just implemented the receiving end of updating a user's data. Now we shall implement the sending end where the user will actually publish messages with their data updates. 

In order to know when a user has changed one of their data variables, we must use an event handler to detect these changes. For this we will use React's **componentDidUpdate()** function, which will trigger anytime there is a change in the app's data. 

We first specify the **componentDidUpdate()** function to pass in the previous props and state.

```javascript
componentDidUpdate(prevProps, prevState) {

}
```

Within this function, we then check whether the user has toggled their **allowGPS** and **focusOnMe** variables and make the necessary changes to the app's function and state.

```javascript
if (prevState.allowGPS != this.state.allowGPS) { //check whether the user just toggled their GPS settings
  if (this.state.allowGPS) { //if user toggled to show their GPS data, we add them to the user Map once again
    if (this.state.focusOnMe) { //if user toggled to focus map view on themselves
      this.animateToCurrent(this.state.currentLoc, 1000);
    }
    let users = this.state.users; //make a copy of the users array to manipulate

    //create a new user object with updated user values to replace the old user
    let tempUser = {
      uuid: this.pubnub.getUUID(),
      latitude: this.state.currentLoc.latitude,
      longitude: this.state.currentLoc.longitude,
      image: this.state.currentPicture,
      username: this.state.username
    };
    users.set(tempUser.uuid, tempUser);
    this.setState( //quickly update the user Map locally
      {
        users
      },
      () => {
        this.pubnub.publish({ //publish updated user to update everyone's user Map
          message: tempUser,
          channel: "channel"
        });
      }
    );
  } else { //if user toggled to hide their GPS data
    let users = this.state.users;
    let uuid = this.pubnub.getUUID();

    users.delete(uuid); //delete this user from the user Map
    this.setState({ //update the userMap
      users,
    });
    this.pubnub.publish({ //let everyone else's user Map know this user wants to be hidden
      message: {
        hideUser: true
      },
      channel: "channel"
    });
  }
}
```

For those of you who are paying close attention to what we are doing here, you may have noticed a redundancy in this code snippet. Why are we setting the state of the updated user locally and then publishing the updated user object to the channel? Isn't that setting the state twice? 

While the assumption is correct, there is a method to the madness. We first update the state locally so we can update the user's screen as fast as possible. Then we publish the updated user object to the channel so that everyone else on the network can update their state as well.

### Rendering

It's time to watch our realtime geotracking app beautifully come together! We'll now be working in the **render()** function of our _App.js_ file. As we incrementally build upon components in this section, be sure to look out for these comments:

```javascript
/*-----Next Snippet Goes Here-----*/
```

...as they will direct you where to insert the next piece of code. 

Let's first declare our user map into a useable array

```javascript
let usersArray = Array.from(this.state.users.values());
```

Now inside your return, render your map component from React-Native-Maps by setting the initial region to whatever coordinates you want.

```javascript
return (
     <View style={styles.container}  >
          <MapView
            style={styles.map}
            ref={ref => (this.map = ref)}
            onMoveShouldSetResponder={this.draggedMap}
            initialRegion={{
              latitude: 36.81808,
              longitude: -98.640297,
              latitudeDelta: 60.0001,
              longitudeDelta: 60.0001
            }}
          >
/*-----Next Snippet Goes Here-----*/          
          </MapView>
     </View>
);
```

We shall now iterate through our map to begin rendering every user on our network.

```javascript
{usersArray.map((item) => (/*------Next Snippet Goes Here-------*/))}
```

For each user, we must render a marker component from React-Native-Maps as well as an image to represent that user.

```javascript
<Marker
  style={styles.marker}
  key={item.uuid} //distinguish each user's marker by their UUID
  coordinate={{ //user's coordinates 
    latitude: item.latitude,
    longitude: item.longitude
  }}
  ref={marker => {
    this.marker = marker;
  }}
>
  <Image
      style={styles.profile}
      source={require('./LOCATION OF YOUR USER IMAGE PROFILES')} //User's image 
  />
</Marker>
```

Below the MapView, we can define a toggle switch for the user to toggle their **allowGPS** state

```javascript
<View style={styles.topBar}>
  <View style={styles.rightBar}>
      <Switch
      value={this.state.allowGPS}
      style={styles.locationSwitch}
      onValueChange={this.toggleGPS}
      />
  </View>
</View>
```

Lastly, we can add a button to center the map on the user.

```javascript
<View style={styles.bottom}>
<View style={styles.bottomRow}>   
  <TouchableOpacity onPress={this.focusLoc}>
    <Image style={styles.focusLoc} source={require('./heart.png')} />
  </TouchableOpacity>
</View>
</View>
```

### Styling

Of course, you may style your components however you like, but here's a simple template I used for this project:

```javascript
const styles = StyleSheet.create({
  bottomRow:{
    flexDirection: "row",
    justifyContent: "space-between",
    alignItems: "center"
  },
  marker: {
    justifyContent: "center",
    alignItems: "center",
    marginTop: Platform.OS === "android" ? 100 : 0,
  },
  topBar: {
    top: Platform.OS === "android" ? hp('2%') : hp('5%'),

    flexDirection: "row",
    justifyContent: "space-between",
    alignItems: "center",
    marginHorizontal: wp("2%"),
  },
  rightBar: {
    flexDirection: "row",
    justifyContent: "flex-end",
    alignItems: "center"
  },
  leftBar: {
    flexDirection: "row",
    justifyContent: "flex-start",
    alignItems: "center"
  },
  locationSwitch: {
    left: 300,
  },
  container: {
    flex: 1
  },
  bottom: {
    position: "absolute",
    flexDirection:'column',
    bottom: 0,
    justifyContent: "center",
    alignSelf: "center",
    width: "100%",
    marginBottom: hp("4%"),
  },
  focusLoc: {
    width: hp("4.5%"),
    height: hp("4.5%"),
    marginRight: wp("2%"),
    left: 15
  },
  userCount: {
    marginHorizontal: 10
  },
  map: {
    ...StyleSheet.absoluteFillObject
  },
  profile: {
    width: hp("4.5%"),
    height: hp("4.5%")
  },
});
```

### Android Compatibility

For Android OS compatibility, place this snippet at the beginning of your **setUpApp()** function:

```javascript
let granted;

if (Platform.OS === "android"){
  granted = await PermissionsAndroid.request( PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION ,
    {
      title: 'Location Permission',
      message:
        'PubMoji needs to access your location',
      buttonNegative: 'No',
      buttonPositive: 'Yes',
    });      
}
```

then place an **if()** statement around the **watchPosition()** function like so

```javascript
if (granted === PermissionsAndroid.RESULTS.GRANTED || Platform.OS === "ios") { /*-----watchPosition()----*/ } 
    else {
      console.log( "ACCESS_FINE_LOCATION permission denied" )
    }
```

## Step 4: Testing the App

When you're all good and ready you can simulate your app with either:

#### simulate IOS

```javascript
react-native run-ios
```

#### or for Android

```javascript
react-native run-android
```

## Conclusion

Congratulations! You have created your very own realtime geotracking app in React Native! Feel free to send us any of your questions, concerns, or comments at devrel@pubnub.com. 

If you're still hungry for more PubNub React Native content, here are some other articles that you may be interested in:

*   [Building a React Native Chat App – Part One: Basic Messaging](https://www.pubnub.com/blog/building-a-chat-app-with-react-native-and-pubnub-part-one-messaging/)
*   [How to Setup Push Notifications in React Native (iOS & Android) with PubNub](https://www.pubnub.com/blog/react-native-push-notifications-ios-android/)
*   [A Guide To Realtime IoT Analytics: Visualizing Your Data with Dashboards](https://www.pubnub.com/blog/guide-to-realtime-iot-analytics-data-visualization-dashboard/)