Adding Authentication to React Native Chat App Using Auth0

Adding Authentication to React Native Chat App Using Auth0

In this tutorial, you'll learn how to: Set up authentication with Auth0, Use Auth0 in a React Native app and Integrate Auth0 with Chatkit.

Most mobile apps require users to log in with their email and password, or any of the third-party login providers to use their service.

Popular chat apps such as Messenger and WhatsApp both have an authentication system in place to verify the identity of their users as well as store their information.

One of the limitations of Chatkit is that it doesn't integrate with authentication services by default.

Prerequisites

Basic knowledge of React Native is required to follow this tutorial. You should know how to install native modules and run the app on your selected environment.

The following package versions are used in this tutorial:

  • Node 11.2.0
  • Yarn 1.13.0
  • React Native CLI 2.0.1
  • React Native 0.59.5

If you encounter any issues getting the app to work, try using the above versions instead.

App overview

We're going to update a pre-coded chat app created using Chatkit and React Native Gifted Chat. It has the following features:

  • Login using an existing Chatkit user.
  • Listing the user's rooms.
  • Sending and receiving messages.
  • Loading older messages.

We'll update it so it uses Auth0 for authenticating the user. When a user logs in with Auth0 (either Facebook or Google), we submit their basic user data to the server and create a corresponding Chatkit user account (if it doesn't already exist). After that, we authenticate the user to use Chatkit.

On the client side, we then securely store their access token and refresh token so we can automatically authenticate them the next time they log in.

You can view the code used in this tutorial on this GitHub repo.

Setting up the authentication

In this section, we'll be looking at how you can set up Auth0 and create the corresponding auth clients for Google and Facebook. We'll be using those two providers to authenticate the users.

Setting up Auth0

The first step is for you to create an Auth0 app instance:

Once the app is created, select React Native under the Quick Start tab:

That will show you how to configure your React Native project to use Auth0. Follow the instructions in that page after going through the Bootstrapping the app section. The following details are already mentioned in that documentation, but I’ll mention it again because it’s very important.

In your application settings, input a value to the Allowed Callback URLs field:

For me, it looks like this. The first one is the callback URL for Android, and the second one is for iOS:

    com.rnchatkitauth0://wern.auth0.com/android/com.rnchatkitauth0/callback,
    org.reactjs.native.example.rnchatkitauth0://wern.auth0.com/ios/org.reactjs.native.example.RNChatkitAuth0/callback

The Android callback URL follows the format:

{YOUR_APP_PACKAGE_NAME}://{YOUR_AUTH0_DOMAIN}/android/{YOUR_APP_PACKAGE_NAME}/callback

While iOS follows the format:

{YOUR_BUNDLE_IDENTIFIER}://${YOUR_AUTH0_DOMAIN}/ios/{YOUR_BUNDLE_IDENTIFIER}/callback

The name of the React Native project that you’ll be creating later is RNChatkitAuth0. Note that the app’s name in the second instance for the bundle identifier above uses the same letter case as your project folder (RNChatkitAuth0 as opposed to rnchatkitauth0).

Next, go to ConnectionsSocial and enable Google and Facebook:

Auth0 has dedicated documentation on how to configure the two, but at the time of writing this tutorial, it’s not really updated with the recent UI changes:

In the next two sections, I’ll be showing you how to configure the two.

Setting up Google login

The first step is to go to console.developers.google.com. By default, this will open a project that you’ve previously configured. In that case, you can probably skip this section because you already know what you’re doing. The goal of this section is to create an OAuth client ID.

If you don’t already have one then keep reading.

Click on the Select a project button on the upper left corner of the screen. This should open a modal which shows the button for creating a new project:

Once the project is created, click on the burger menu on the upper left side of the screen, hover over APIs & Services then click on Credentials. That should show you the screen for creating credentials. Click on the Create credentials button and select OAuth client ID:

If you haven’t previously configured a consent screen, it should show the following. Just click on the Configure consent screen button:

On the screen that follows, input the application name (RNChatkitAuth0) then scroll down to the Authorised domains section. Add auth0.com to it, press enter, then click on the Save button.

Once that’s done, go back to creating the OAuth client ID by clicking on the Credentials tab. On the screen that shows up, select Web application for the application type, enter the name of the client (RNClient), and add your Auth0 login callback URL under the Authorized redirect URIs field. For me, it’s [https://wern.auth0.com/login/callback](https://wern.auth0.com/login/callback). So you just have to replace wern.auth0.com with the domain assigned to your Auth0 account. You can find it on your app settings page. Once you’ve added it, click on the Create button:

If you’re wondering why we selected Web Application as the Application Type: this is because Auth0 will just open a browser tab to authenticate the user. It’s not really baked into the app itself just like a native Android or iOS app would be.

Once the OAuth client is created, it should show the client ID and client secret which you can paste into Auth0’s Google settings modal. Click SAVE to save the credentials or TRY to try logging in with your Google account using Auth0:

You should see a screen similar to the following if the authentication flow works:

Setting up Facebook

Go to developers.facebook.com to create an app. On that page, click on the Add a New App button. That will show the modal for creating a new app:

Once the app is created, it lets you select the scenarios in which you’re going to use it. Select Integrate Facebook Login:

Because you selected Facebook login, it will recommend you to go through the quick start for the Facebook login. Select Web and enter your Auth0 domain as the site URL.

Next, click on Continue and then click Next a bunch of times until you get to the end of it. At that point, you can click on the Settings menu on the left:

It should show the following. Here, you can add your Auth0 login callback URL. For me, it was [https://wern.auth0.com/login/callback](https://wern.auth0.com/login/callback). Just change wern.auth0.com to the domain name assigned by Auth0 to your account:

Once that’s done, go to your app’s basic settings page and add auth0.com as an app domain and save the changes:

At this point, you can copy your app ID and app secret to the Auth0 connection for Facebook:

Be sure to check only the Public Profile because that’s the only one we need. Because at the time of writing this tutorial, there’s a problem with the integration of Facebook login on Auth0. The error looks something like this:

Unsupported get request. Object with ID 'me' does not exist, cannot be loaded due to missing permissions, or does not support this operation. Please read the Graph API documentation at https://developers.facebook.com/docs/graph-api

This has to do with the release of Facebook Graph API version 3.3.

The solution is to disable the Social Context in the Facebook settings on Auth0. This was previously checked by default, so if this is your first time creating an Auth0 app, it should already be unchecked by default.

Bootstrapping the app

To set up the starter app, clone the GitHub repo and switch to the starter branch:

    git clone http://github.com/anchetaWern/RNChatkitAuth0
    git checkout starter

After that, install the dependencies, re-create the android and ios folders, then link the native modules:

    yarn

    react-native eject

    react-native link react-native-auth0
    react-native link react-native-config
    react-native link react-native-device-info
    react-native link react-native-restart
    react-native link react-native-sensitive-info
    react-native link react-native-gesture-handler

Next, update the android/app/build.gradle file to include the config for the React Native Config package:

    apply from: "../../node_modules/react-native/react.gradle"

    apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle" // add this

Lastly, update the .env and server/.env file with your Auth0 and Chatkit credentials:

    // .env
    AUTH0_DOMAIN="YOUR AUTH0 DOMAIN"
    AUTH0_CLIENT_ID="YOUR AUTH0 APP CLIENT ID"
    AUTHO_SCOPE="openid profile offline_access"
    AUTH0_AUDIENCE="https://YOUR_AUTH0_DOMAIN/userinfo"

    CHATKIT_INSTANCE_LOCATOR_ID="YOUR CHATKIT INSTANCE LOCATOR ID (omit v1:us1:)"
    CHATKIT_SECRET_KEY="YOUR CHATKIT INSTANCE SECRET KEY"


    // server/.env
    CHATKIT_INSTANCE_LOCATOR_ID="YOUR CHATKIT INSTANCE LOCATOR ID (omit v1:us1:)"
    CHATKIT_SECRET_KEY="YOUR CHATKIT INSTANCE SECRET KEY"

All of the config above are self-explanatory aside from the AUTH0_SCOPE and AUTH0_AUDIENCE so I’ll walk you through those briefly:

  • AUTH0_SCOPE - used for specifying which authentication protocol (openid), user data (profile), and privileges (offline_access) we want the user to provide us. offline_access allows us to retrieve the user’s refresh token. This token allows us to make a request for a new access token after the existing one expires. Note that this requires you to pass in a unique device ID (as you’ll see later).
  • AUTH0_AUDIENCE - this is the Auth0 endpoint which returns the user’s data.
Updating the app

Now we’re ready to update the app. In this section, we will integrate social login with Auth0 and create a corresponding Chatkit user for the user who logged in. That user will then be used when they log in in the future.

The first thing that you need to do is update the Login screen so it uses the packages we installed earlier:

    // src/screens/Login.js
    import Auth0 from "react-native-auth0"; // for using Auth0 within React Native
    import DeviceInfo from "react-native-device-info"; // for getting unique device ID
    import SInfo from "react-native-sensitive-info"; // for securely storing the access and refresh tokens returned by Auth0
    import RNRestart from "react-native-restart"; // for restarting the app after acquiring a new access token
    import { NavigationActions, StackActions } from "react-navigation"; // for implementing navigation

Next, we initialize Auth0:

    const auth0 = new Auth0({
      domain: Config.AUTH0_DOMAIN,
      clientId: Config.AUTH0_CLIENT_ID
    });

Once the component is mounted, we try to get the access token from the secure storage. If there’s an access token, we try to log in the user with it. The loginUser() method is responsible for connecting the user to Chatkit and navigating to the Rooms screen. The second argument that we’ve passed is the function to execute when the access token passed as the first argument has already expired:

    componentDidMount() {
      SInfo.getItem("accessToken", {}).then((accessToken) => {
        if (accessToken) {
          this.loginUser(accessToken, this.refreshAccessToken);
        } else {
          this.setState({
            hasInitialized: true // hide the loader
          });
        }
      });
    }

Next, update the code for logging the user in so it uses Auth0. auth0.webAuth.authorize() will open the user’s default browser and ask them to log in with the providers we set up earlier. Once the user has successfully logged in, Auth0 returns an access token and refresh token.

After that, we call the loginUser() method and securely store the user tokens. This way, we can still request their details the next time they open the app:

    login = async () => {
      try {
        const { accessToken, refreshToken } = await auth0.webAuth.authorize({
          scope: Config.AUTHO_SCOPE,
          audience: Config.AUTH0_AUDIENCE,
          device: DeviceInfo.getUniqueID(), // required if you want to retrieve the refresh token
          prompt: "login"
        });

        this.loginUser(accessToken);

        // keep the tokens for later access
        SInfo.setItem("accessToken", accessToken, {});
        SInfo.setItem("refreshToken", refreshToken, {});

      } catch (auth0LoginError) {
        console.log('error logging in: ', auth0LoginError);
      }
    }

Here’s the code for the loginUser() method. This accepts the access token and the function to execute if the access token has already expired. We then use the access token to request for the user’s info which we then use for creating a Chatkit user (if it doesn’t already exist). The sub property contains the unique user ID for connecting to Chatkit:

    loginUser = (accessToken, errorCallback) => {
      auth0.auth
        .userInfo({ token: accessToken })
        .then(async (userData) => {
          try {
            await axios.post(`${CHAT_SERVER}/create-user`, userData);

            const chatManager = new ChatManager({
              instanceLocator: CHATKIT_INSTANCE_LOCATOR_ID,
              userId: userData.sub,
              tokenProvider: new TokenProvider({ url: CHATKIT_TOKEN_PROVIDER_ENDPOINT })
            });

            const currentUser = await chatManager.connect();
            this.currentUser = currentUser;

            this.goToRoomsPage({id: userData.sub, currentUser: this.currentUser});

          } catch (chatManagerError) {
            console.log("error connecting to Chat Manager: ", chatManagerError);
          }
        })
        .catch(errorCallback);
    }

Here’s a sample response data we get from the auth0.auth.userInfo() call:

    {
      "sub":"google-oauth2|UNIQUE_GOOGLE_ACCOUNT_ID",
      "givenName":"Shu",
      "familyName":"Takada",
      "nickname":"takada.shu",
      "name":"Shu Takada",
      "picture":"URL TO PROFILE PICTURE",
      "gender":"male",
      "locale":"en-GB",
      "updatedAt":"2019-05-13T04:00:56.920Z"
    }

Here’s the goToRoomsPage() method. This accepts the object containing the user’s ID and the reference to the current Chatkit user as its argument because we need to pass those to the next page. But instead of the navigating to the next page using this.props.navigation.navigate(), we have to use the StackActions to reset the stack. This is because we don’t want the user to be manually going back to the Login screen after they got authenticated. That should only happen when they choose to log out:

    goToRoomsPage = ({ id, currentUser }) => {
      // navigate to the Rooms page and treat it as the very first page
      const resetAction = StackActions.reset({
        index: 0,
        actions: [
          NavigationActions.navigate({
            routeName: "Rooms",
            params: {
              id,
              currentUser,
              auth0
            }
          })
        ]
      });

      this.props.navigation.dispatch(resetAction);
    }

Lastly, we have the refreshAccessToken() method. This gets executed if the access token that we securely stored has already expired. From the login code earlier, we stored a refresh token along with the access token. This token allows us to make a request for a new access token, and that’s exactly what we’re doing. Once a new access token is returned, we store it again then restart the app so that componentDidMount() fires again using the new access token:

    refreshAccessToken = () => {
      SInfo.getItem("refreshToken", {})
        .then((refreshToken) => {
          auth0.auth
            .refreshToken({ refreshToken: refreshToken })
            .then((newAccessToken) => {
              SInfo.setItem("accessToken", newAccessToken);
              RNRestart.Restart(); // restart so componentDidMount fires again with the new access token
            })
            .catch((newAccessTokenError) => {
              this.setState({
                hasInitialized: true
              });
              Alert.alert("Cannot refresh access token. Please login again.");
            });
        });
    }

Log out user

The authentication integration wouldn’t be complete without a logout sequence. In this section, we’ll be looking at how to do that.

First, open the src/screens/Rooms.js file and get the reference to auth0 that was passed from the Login screen:

    componentDidMount() {
      // ...

      this.currentUser = navigation.getParam("currentUser");
      this.auth0 = navigation.getParam("auth0"); // add this

      // ...
    }

Next, update the enterChat() function to pass auth0 as a navigation param:

    enterChat = async (room) => {
      this.props.navigation.navigate("Chat", {
        roomName: room.name,
        auth0: this.auth0 // add this
      });
    }

On the Chat screen, import the packages that we need for implementing the logout functionality:

    import { ActivityIndicator, View, TouchableOpacity, Text, Alert } from "react-native"; // add TouchableOpacity, Text, and Alert
    import { StackActions, NavigationActions } from "react-navigation"; // for navigating back to the login page
    import SInfo from "react-native-sensitive-info"; // for clearing the tokens we saved earlier

Next, inside the navigation header, add the button for logging the user out:

    static navigationOptions = ({ navigation }) => {
      const { params } = navigation.state;
      return {
        headerTitle: params.roomName,
        headerRight: (
          
            
              
                Logout
              
            
          
        )
      };
    }

In the constructor, extract the reference to auth0 that we passed from the Rooms page earlier:

    constructor(props) {
      // ...
      this.roomName = navigation.getParam("roomName");
      this.auth0 = navigation.getParam("auth0"); // add this
    }

Once the component is mounted, we set the logoutUser() function as an additional navigation param:

    componentDidMount() {
      this.props.navigation.setParams({
        logoutUser: this.logoutUser
      });

      // ...
    }

Lastly, add the code for logging the user out:

    logoutUser = async () => {
      // delete the tokens from the storage
      SInfo.deleteItem("accessToken", {});
      SInfo.deleteItem("refreshToken", {});
      try {
        await this.auth0.webAuth.clearSession(); // clear the browser session
      } catch (clearSessionError) {
        console.log("error clearing session: ", clearSessionError);
      }
      Alert.alert("Logged out", "You are now logged out");
      this.gotoLoginPage();
    }

Note: The auth0.webAuth.clearSession() function only works on iOS. This is because the Auth0 library cannot determine the browser used by the user to login in Android. It works in iOS because it only ever uses one browser to perform web-related actions inside an app.

Here’s the code for the gotoLoginPage() function:

    gotoLoginPage = () => {
      const resetAction = StackActions.reset({
        index: 0,
        actions: [
          NavigationActions.navigate({
            routeName: "Login"
          })
        ]
      });
      this.props.navigation.dispatch(resetAction);
    }
Updating the server

The final step is to add the route for creating the user on the server/index.js file. This will create the corresponding Chatkit user for the user who has logged in (if an account hasn’t been created already):

    app.post("/create-user", async (req, res) => {
      const { sub: id, name, picture: avatarURL } = req.body;  
      try {
        let user = await chatkit.createUser({
          id,
          name,
          avatarURL
        });
        res.send('ok');
      } catch (err) {
        if (err.error === "services/chatkit/user_already_exists") {
          res.send('ok');
        } else {
          let statusCode = err.error.status;
          if (statusCode >= 100 && statusCode < 600) {
            res.status(statusCode);
          } else {
            res.status(500);
          }
        }
      }
    });
Running the app

At this point, you can now run the server:

    cd server
    yarn start
    ~/.ngrok http 5000

Replace the CHAT_SERVER URL with your ngrok HTTPS URL on the src/screens/Login.js file and run the app:

    react-native run-android
    react-native run-ios
Conclusion

In this tutorial, you learned how to integrate Auth0 to your existing Chatkit chat app. Along the way, you also learned how to configure an auth client with Google and Facebook and added them as a login provider to Auth0.

You can find the code used in this tutorial on this GitHub repo.

How to Share Code Between React and React Native

How to Share Code Between React and React Native

Learn how to share code between React and React Native to avoid duplicating logic. Sharing Code Between React and React-Native: What Not to Share. This question of sharing code between React and React Native, in fact, one of the hot topics among React and React native developers all over the world. React and React-Native allow a learn once write anywhere paradigm. This is great, because one tech team can build both your web app and native mobile experience. The problem is developers hate writing things twice. There have been a couple of efforts to build a unifying technology to write an application once and have it work on both web and native.

How to Share Code Between React and React Native

Sharing Code Between React and React-Native: What Not to Share - Ben Ellerby

React and React-Native allow a learn once write anywhere paradigm. This is great, because one tech team can build both your web app and native mobile experience. The problem is developers hate writing things twice. There have been a couple of efforts to build a unifying technology to write an application once and have it work on both web and native. Yet this is not always the best approach. There is value in only sharing your business and state logic; keeping your render code separate.

In this talk I will give real examples from my work with MADE.COM, migrating their web and mobile application to React and React-Native with code sharing as a primary objective.

How to Share Code Between React and React Native

Learn how to share code between React and React Native to avoid duplicating logic

Sharing Code Between React and React Native

React and React-Native allow a learn once write anywhere paradigm. This is great, because one tech team can build both your web app and native mobile experience. The problem is developers hate writing things twice. There have been a couple of efforts to build a unifying technology to write an application once and have it work on both web and native. Yet this is not always the best approach. There is value in only sharing your business and state logic; keeping your render code separate.

React Native Map components for iOS + Android

React Native Map components for iOS + Android

React Native Map components for iOS + Android.Contribute to react-native-community/react-native-maps development by creating an account on GitHub

React Native Map components for iOS + Android

Installation

See Installation Instructions.

See Setup Instructions for the Included Example Project.

Compatibility

Due to the rapid changes being made in the React Native ecosystem, we are not officially going to support this module on anything but the latest version of React Native. With that said, we will do our best to stay compatible with older versions as much that is practical, and the peer dependency of this requirement is set to "react-native": "*" explicitly for this reason. If you are using an older version of React Native with this module though, some features may be buggy.

Note about React requires

Since react-native 0.25.0, React should be required from node_modules. React Native versions from 0.18 should be working out of the box, for lower versions you should add react as a dependency in your package.json.

General Usage
source-js
import MapView from 'react-native-maps';

or

source-js
var MapView = require('react-native-maps');

This MapView component is built so that features on the map (such as Markers, Polygons, etc.) are specified as children of the MapView itself. This provides an intuitive and react-like API for declaratively controlling features on the map.

Rendering a Map with an initial region

MapView
source-js-jsx
  <MapView
    initialRegion={{
      latitude: 37.78825,
      longitude: -122.4324,
      latitudeDelta: 0.0922,
      longitudeDelta: 0.0421,
    }}
  />

Using a MapView while controlling the region as state

source-js-jsx
getInitialState() {
  return {
    region: {
      latitude: 37.78825,
      longitude: -122.4324,
      latitudeDelta: 0.0922,
      longitudeDelta: 0.0421,
    },
  };
}

onRegionChange(region) {
  this.setState({ region });
}

render() {
  return (
    <MapView
      region={this.state.region}
      onRegionChange={this.onRegionChange}
    />
  );
}

Rendering a list of markers on a map

source-js-jsx
import { Marker } from 'react-native-maps';

<MapView
  region={this.state.region}
  onRegionChange={this.onRegionChange}
>
  {this.state.markers.map(marker => (
    <Marker
      coordinate={marker.latlng}
      title={marker.title}
      description={marker.description}
    />
  ))}
</MapView>

Rendering a Marker with a custom view

source-js-jsx
<Marker coordinate={marker.latlng}>
  <MyCustomMarkerView {...marker} />
</Marker>

Rendering a Marker with a custom image

source-js-jsx
<Marker
  coordinate={marker.latlng}
  image={require('../assets/pin.png')}
/>

Rendering a custom Marker with a custom Callout

source-js-jsx
import { Callout } from 'react-native-maps';

<Marker coordinate={marker.latlng}>
  <MyCustomMarkerView {...marker} />
  <Callout>
    <MyCustomCalloutView {...marker} />
  </Callout>
</Marker>

Draggable Markers

source-js-jsx
<MapView initialRegion={...}>
  <Marker draggable
    coordinate={this.state.x}
    onDragEnd={(e) => this.setState({ x: e.nativeEvent.coordinate })}
  />
</MapView>

Using a custom Tile Overlay

Tile Overlay using tile server

source-js-jsx
import { UrlTile } from 'react-native-maps';

<MapView
  region={this.state.region}
  onRegionChange={this.onRegionChange}
>
  <UrlTile
    /**
     * The url template of the tile server. The patterns {x} {y} {z} will be replaced at runtime
     * For example, http://c.tile.openstreetmap.org/{z}/{x}/{y}.png
     */
    urlTemplate={this.state.urlTemplate}
    /**
     * The maximum zoom level for this tile overlay. Corresponds to the maximumZ setting in
     * MKTileOverlay. iOS only.
     */
    maximumZ={19}
    /**
     * flipY allows tiles with inverted y coordinates (origin at bottom left of map)
     * to be used. Its default value is false.
     */
    flipY={false}
  />
</MapView>

For Android: add the following line in your AndroidManifest.xml

text-xml
<uses-permission android:name="android.permission.INTERNET" />

For IOS: configure App Transport Security in your app

Tile Overlay using local tiles

Tiles can be stored locally within device using xyz tiling scheme and displayed as tile overlay as well. This is usefull especially for offline map usage when tiles are available for selected map region within device storage.

source-js-jsx
import { LocalTile } from 'react-native-maps';

<MapView
  region={this.state.region}
  onRegionChange={this.onRegionChange}
>
  <LocalTile
   /**
    * The path template of the locally stored tiles. The patterns {x} {y} {z} will be replaced at runtime
    * For example, /storage/emulated/0/mytiles/{z}/{x}/{y}.png
    */
   pathTemplate={this.state.pathTemplate}
   /**
    * The size of provided local tiles (usually 256 or 512).
    */
   tileSize={256}
  />
</MapView>

For Android: LocalTile is still just overlay over original map tiles. It means that if device is online, underlying tiles will be still downloaded. If original tiles download/display is not desirable set mapType to 'none'. For example:

<MapView
  mapType={Platform.OS == "android" ? "none" : "standard"}
>

See OSM Wiki for how to download tiles for offline usage.

Overlaying other components on the map

Place components you that wish to overlay MapView underneath the MapView closing tag. Absolutely position these elements.

source-js-jsx
render() {
  return (
    <MapView
      region={this.state.region}
    />
    <OverlayComponent
      style={{position: “absolute”, bottom: 50}}
    />
  );
}

Customizing the map style

Create the json object, or download a generated one from the google style generator.

source-js-jsx
// The generated json object
mapStyle = [ ... ]

render() {
  return (
    <MapView
      region={this.state.region}
      onRegionChange={this.onRegionChange}
      customMapStyle={mapStyle}
    />
  );
}

For iOS, in addition to providing the mapStyle you will need to do the following

source-js-jsx
import MapView, { PROVIDER_GOOGLE } from 'react-native-maps'

// ...

<MapView
  provider={PROVIDER_GOOGLE}
  customMapStyle={MapStyle}
>

Then add the AirGoogleMaps directory:

https://github.com/react-native-community/react-native-maps/blob/1e71a21f39e7b88554852951f773c731c94680c9/docs/installation.md#ios

An unofficial step-by-step guide is also available at https://gist.github.com/heron2014/e60fa003e9b117ce80d56bb1d5bfe9e0

Examples

To run examples:

source-shell
npm i
npm start

#Android
npm run run:android

#iOS
npm run build:ios
npm run run:ios

MapView Events

The <MapView /> component and its child components have several events that you can subscribe to. This example displays some of them in a log as a demonstration.

Tracking Region / Location

Programmatically Changing Region

One can change the mapview's position using refs and component methods, or by passing in an updated region prop. The component methods will allow one to animate to a given position like the native API could.

Changing the style of the map

Arbitrary React Views as Markers

Using the MapView with the Animated API

The <MapView /> component can be made to work with the Animated API, having the entire region prop be declared as an animated value. This allows one to animate the zoom and position of the MapView along with other gestures, giving a nice feel.

Further, Marker views can use the animated API to enhance the effect.

Issue: Since android needs to render its marker views as a bitmap, the animations APIs may not be compatible with the Marker views. Not sure if this can be worked around yet or not.

Markers' coordinates can also be animated, as shown in this example:

Polygon Creator

Other Overlays

So far, <Circle />, <Polygon />, and <Polyline /> are available to pass in as children to the <MapView /> component.

Gradient Polylines (iOS MapKit only)

Gradient polylines can be created using the strokeColors prop of the <Polyline> component.

Default Markers

Default markers will be rendered unless a custom marker is specified. One can optionally adjust the color of the default marker by using the pinColor prop.

Custom Callouts

Callouts to markers can be completely arbitrary react views, similar to markers. As a result, they can be interacted with like any other view.

Additionally, you can fall back to the standard behavior of just having a title/description through the <Marker />'s title and description props.

Custom callout views can be the entire tooltip bubble, or just the content inside of the system default bubble.

To handle press on specific subview of callout use <CalloutSubview /> with onPress. See Callouts.js example.

Image-based Markers

Markers can be customized by just using images, and specified using the image prop.

Draggable Markers

Markers are draggable, and emit continuous drag events to update other UI during drags.

Lite Mode ( Android )

Enable lite mode on Android with liteMode prop. Ideal when having multiple maps in a View or ScrollView.

On Poi Click (Google Maps Only)

Poi are clickable, you can catch the event to get its information (usually to get the full detail from Google Place using the placeId).

Animated Region

The MapView can accept an AnimatedRegion value as its region prop. This allows you to utilize the Animated API to control the map's center and zoom.

source-js-jsx
import MapView, { AnimatedRegion, Animated } from 'react-native-maps';

getInitialState() {
  return {
    region: new AnimatedRegion({
      latitude: LATITUDE,
      longitude: LONGITUDE,
      latitudeDelta: LATITUDE_DELTA,
      longitudeDelta: LONGITUDE_DELTA,
    }),
  };
}

onRegionChange(region) {
  this.state.region.setValue(region);
}

render() {
  return (
    <Animated
      region={this.state.region}
      onRegionChange={this.onRegionChange}
    />
  );
}

Animated Marker Position

Markers can also accept an AnimatedRegion value as a coordinate.

source-js-jsx
import Mapview, { AnimatedRegion, Marker } from 'react-native-maps';

getInitialState() {
  return {
    coordinate: new AnimatedRegion({
      latitude: LATITUDE,
      longitude: LONGITUDE,
    }),
  };
}

componentWillReceiveProps(nextProps) {
  const duration = 500

  if (this.props.coordinate !== nextProps.coordinate) {
    if (Platform.OS === 'android') {
      if (this.marker) {
        this.marker._component.animateMarkerToCoordinate(
          nextProps.coordinate,
          duration
        );
      }
    } else {
      this.state.coordinate.timing({
        ...nextProps.coordinate,
        duration
      }).start();
    }
  }
}

render() {
  return (
    <MapView initialRegion={...}>
      <MapView.Marker.Animated
        ref={marker => { this.marker = marker }}
        coordinate={this.state.coordinate}
      />
    </MapView>
  );
}

If you need a smoother animation to move the marker on Android, you can modify the previous example:

source-js-jsx
// ...

componentWillReceiveProps(nextProps) {
  const duration = 500

  if (this.props.coordinate !== nextProps.coordinate) {
    if (Platform.OS === 'android') {
      if (this.marker) {
        this.marker._component.animateMarkerToCoordinate(
          nextProps.coordinate,
          duration
        );
      }
    } else {
      this.state.coordinate.timing({
        ...nextProps.coordinate,
        duration
      }).start();
    }
  }
}

render() {
  return (
    <MapView initialRegion={...}>
      <Marker.Animated
        ref={marker => { this.marker = marker }}
        coordinate={this.state.coordinate}
      />
    </MapView>
  );
}

Take Snapshot of map

source-js-jsx
import MapView, { Marker } from 'react-native-maps';

getInitialState() {
  return {
    coordinate: {
      latitude: LATITUDE,
      longitude: LONGITUDE,
    },
  };
}

takeSnapshot () {
  // 'takeSnapshot' takes a config object with the
  // following options
  const snapshot = this.map.takeSnapshot({
    width: 300,      // optional, when omitted the view-width is used
    height: 300,     // optional, when omitted the view-height is used
    region: {..},    // iOS only, optional region to render
    format: 'png',   // image formats: 'png', 'jpg' (default: 'png')
    quality: 0.8,    // image quality: 0..1 (only relevant for jpg, default: 1)
    result: 'file'   // result types: 'file', 'base64' (default: 'file')
  });
  snapshot.then((uri) => {
    this.setState({ mapSnapshot: uri });
  });
}

render() {
  return (
    <View>
      <MapView initialRegion={...} ref={map => { this.map = map }}>
        <Marker coordinate={this.state.coordinate} />
      </MapView>
      <Image source={{ uri: this.state.mapSnapshot.uri }} />
      <TouchableOpacity onPress={this.takeSnapshot}>
        Take Snapshot
      </TouchableOpacity>
    </View>
  );
}

Zoom to Specified Markers

Pass an array of marker identifiers to have the map re-focus.

Zoom to Specified Coordinates

Pass an array of coordinates to focus a map region on said coordinates.

Troubleshooting

My map is blank

  • Make sure that you have properly installed react-native-maps.
  • Check in the logs if there is more informations about the issue.
  • Try setting the style of the MapView to an absolute position with top, left, right and bottom values set.
  • Make sure you have enabled Google Maps API in Google developer console
source-js
const styles = StyleSheet.create({
  map: {
    ...StyleSheet.absoluteFillObject,
  },
});
source-js-jsx
<MapView
  style={styles.map}
  // other props
/>

Inputs don't focus

  • When inputs don't focus or elements don't respond to tap, look at the order of the view hierarchy, sometimes the issue could be due to ordering of rendered components, prefer putting MapView as the first component.

Bad:

source-js-jsx
<View>
  <TextInput/>
  <MapView/>
</View>

Good:

source-js-jsx
<View>
  <MapView/>
  <TextInput/>
</View>

React Native vs Flutter | Difference Between React Native & Flutter

React Native vs Flutter | Difference Between React Native & Flutter

This video on React Interview Questions will help you crack your next React interview with ease. React is the most popular front-end JavaScript library today and is being adopted by many big companies like Netflix, Airbnb, New York Times, and many more...

This video on React Interview Questions will help you crack your next React interview with ease. React is the most popular front-end JavaScript library today and is being adopted by many big companies like Netflix, Airbnb, New York Times, and many more. It is also used in small projects by web developers to showcase their skills in the field of web development. The video includes general React interview questions as well as questions focused on Redux, Hooks, and Styling. This video is ideal for both beginners as well as experienced professionals who are appearing for React web development job interviews. Learn the most important React interview questions and answers and know what will set you apart in the interview process.