Geo-tracking in React Native

A mobile application can act as the “workplace” of an employee, while it may be necessary to transfer geographical coordinates and other data. In cross-platform application development on iOS and Android, frameworks such as Flutter or React Native are often used for this task. In this article, we talk about the features of working with geolocation in React Native using the example of our case.



The reliability, speed and maturity of React Native are sometimes criticized by developers, and about three years ago such doubts could be called justified. However, during this time, technology has become more advanced, and React Native has been recognized as a practical tool for creating a wide range of applications. In particular, React Native is suitable for applications that use geographical coordinates - for example, various trackers, applications for couriers, etc.

Also in stores are developed on React Native applications of online stores, restaurant menus, news feeds, delivery applications, customers for social networks - thus, a huge number of React Native constantly use, without even thinking about it. Simple examples are Facebook, Instagram, Skype, Uber Eats, Wallmart. According to forecasts, Microsoft can replenish this list and transfer its mobile applications to React Native, especially after it has implemented support for the Windows platform. There is React Native support for Qt , macOS , “built-in” weechat applications , although support and documentation for these platforms remains rather scarce.

React Native can do almost everything the same as a native application. For example, receive information from device sensors, as Java / Kotlin or ObjC / Swift applications do, or send data in the background using Foreground Service or Background fetch. The only difference is that in React Native you need to implement an interface for the interaction between the native part and Javascript. For everyday tasks, these interfaces are often already implemented. This also applies to tracking the location and operation of Javascript in the background: there are already solutions in the form of independent open source libraries. It so happened that the community independently “pulls” Javascript forward, both programmers and companies are actively using Open Source solutions. Few language communities boast the same number of libraries, frameworks,quality utilities. And yet, in React Native, some features that exist in native development are still limited, and this should be remembered.

Work with React Native


Consider several key factors that ensure the development speed of cross-platform applications on React Native:

Common code base


Once written logic, layout screens and components on different platforms work and look the same as possible (for example, on Android, instead of the usual shadows, they use the notion of elation). There is practically no eternal headache for a developer with old versions - for example, it is easy to add support even for Android 4.4, although in practice the implementation of this task also depends on the use of additional native modules.

Optimal Workflow


Often, React provides an optimal workflow that makes developing new features several times faster than in native languages. This applies to layout, component approach, dependency installation, common architectural solutions for React.

Hot reload


The developer does not need to rebuild the application each time to see the changes. Thanks to Hot Reload, it takes just a couple of seconds to update the application to the current state. And in version 0.61 Live Reload (now it is Fast Refresh) was additionally brought to mind, now when editing typesetting changes are pulled “on the fly”, without resetting the current state of the components.

When creating an application, several key areas require special attention:

1) Javascript

React Native aims to work with Javascript, allowing you to quickly describe the logic and interfaces. There are many ready-made modules from the world of Web and Node.js that can be used in the usual way (naturally, if they are not connected to the DOM or to the server side). Despite its high speed, Javascript is sometimes inferior to native languages. There is no multithreading. In React Native, we cannot directly access the Native API, although this restriction may soon be removed. Javascript primarily describes what needs to be done and rendered in the native part.

2) Bridge

A bus that accepts Javascript calls through special interfaces provided by the react-native library. Calls are regular serialized messages. For each component - a simple View, an input field or a call to the system dialog - there is an interface described in Javascript with the logic implemented in the native part. The bus is the slowest part, it is asynchronous, transferring data between Javascript and the native takes time (most likely, Facebook can completely get rid of it). Often, the performance problems of applications on React Native are caused precisely by the operation of the bridge, but if you use it correctly (reduce the transfer of data back and forth to the minimum necessary), then the performance in React Native applications will not differ significantly.

3) The native part

An application on React Native is initially an ordinary native application. The peculiarity is that all the necessary libraries are already connected in it so that you can write all the logic in React JS. Here we can write our own native logic in order to use (or not use it) in JS, add ordinary native libraries (for example, to support TLS v1.2 on KitKat and earlier). In ready-made native applications, you can add support for React Native.

Working with geo tracking in React Native


In one of the projects, we needed to develop a “mobile workplace” for our client’s employees, in particular, to ensure the transfer of their geographical coordinates in the background.

There are several tools for working with geolocation in React Native. For example, this is a package from the React Native @ react-native-community / geolocation community , which covers most use cases , however, does not work in the background.

To implement our task, we used several solutions, including a library with the speaking name @ mauron85 / react-native-background-geolocation. Initially, this library was a plugin for Cordova, but later the author separated the base and made separate wrappers for React Native. There is also a paid library of the same name from transistorsoft, but at the time of our work on the project, it only won with its documentation.

The library allowed us to set flexible settings for tracking location. For example, with what frequency to interrogate a system service, how exactly to work in the background, in what radius the position will be considered motionless. Settings and methods for sending these coordinates to the server could be used “out of the box”, but we decided not to spam requests, but to aggregate the received coordinates in the repository and send them with a large interval. At the same time, the library allowed us to launch our application as foregroundActivity, saving us from unnecessary headaches, since on many Android devices applications in the background can work only with additional settings ( Huawei, MIUI, Samsung recently ).

After installation, you can create a component or just a helper that manages the service. We decided to use the component to mount it with the conditions in the router and easily connect it to redux. Set up our service:

import BackgroundGeolocation, {
  Location,
} from '@mauron85/react-native-background-geolocation';
 
class ForegroundService extends PureComponent<Props> {
//...
startBackgroundService = (config: GeotrackerConfig) => {
     //    
	BackgroundGeolocation.configure({
  	desiredAccuracy: BackgroundGeolocation.HIGH_ACCURACY,
  	stationaryRadius: 50,
  	distanceFilter: config.distance,
  	notificationTitle: Strings.Geolocation.NOTIFICATION_TITLE,
  	notificationText: Strings.Geolocation.NOTIFICATION_DESCRIPTION,
  	debug: false,
  	startOnBoot: false,
  	stopOnTerminate: true,
  	locationProvider: BackgroundGeolocation.ACTIVITY_PROVIDER,
  	interval: this.runnerInterval,
  	fastestInterval: 10000,
  	activitiesInterval: 30000,
  	stopOnStillActivity: false,
  	notificationIconColor: Colors.primary,
  	notificationIconSmall: 'notification_icon',
	});
 
     //    start()      
                    	BackgroundGeolocation.on('authorization', status => {
  	if (status !== BackgroundGeolocation.AUTHORIZED) {
    	//   
  	} else if (status.hasPermissions) {
        // hasPermission -  ,    
  	}
	});
 
     //  
                    	BackgroundGeolocation.start();
};


We see that here you can also set settings for the notification, an icon (from native assets). What about permissions? For Android, after installing the package, nothing is required, the library will take over the requests for access to the system geolocation. But for normal operation on iOS in Xcode, you also need to enable Background processing and Location updates in the Signing & Capabilities tab of the project.



We implement the main method for saving and sending coordinates in the on ('location') event in the same method. It will work both with the active application and in the minimized state:

BackgroundGeolocation.on('location', location => {
    BackgroundGeolocation.startTask(async taskKey => {
     //       ,
     //     iOS-
      const batteryLevel = this.isSimulator
        ? 100
        : (await DeviceInfo.getBatteryLevel()) * 100;
      const updateDelta = location.time - this.lastUpdateAt;

     //      
      this.props.locationUpdate(location);
      this.props.locationAddTrackerData({
        altitude: location.altitude,
        batteryChargeLevel: batteryLevel,
        latitude: location.latitude,
        longitude: location.longitude,
        course: location.bearing,
        accuracy: location.accuracy,
        speed: location.speed,
        timestamp: Number(String(location.time).substr(0, 10)),
        isAlertOn: false,
      });

     //  ?
      if (updateDelta / 1000 > config.sendInterval) {
        this.syncDataWithInterval();
        this.lastUpdateAt = location.time;
      }

     //    AsyncStorage,    
      Storage.setLastKnownLocation(location);
      BackgroundGeolocation.endTask(taskKey);  //   !
    });
});


Here we call action from the store, where we transfer the coordinate data in a custom format, additionally storing data on the battery readings, using the react-native-device-info library. All this works equally well on Android and iOS when the application is active or in the background.

In addition, there are methods for obtaining the current location (but this only works when the service is running), subscribing to transitions of the application state from active to background, switching to system settings of the application. However, by and large, the most necessary is given in these examples.

The result is a component that can be mounted, for example, only for the authorized part:

class ForegroundService extends PureComponent<Props> {
	//...
	componentDidMount() {
		this.startBackgroundService(this.props.config);
	}

	componentWillUnmount() {
    BackgroundGeolocation.stop();
    BackgroundGeolocation.removeAllListeners();
  }

	startBackgroundService = (config: GeotrackerConfig) => {
    BackgroundGeolocation.configure({
		// ...
	};

	render() {
    return null;
  }
} 


If react-navigation is used, you can create navigation for the authorized part as follows:

//  StackNavigator   
const Navigator = createStackNavigator(RouteConfigs, NavigatorConfig);


class AppNavigator extends PureComponent<Props> {
  static router: NavigationRouter = Navigator.router;

  componentDidMount(): void {
    SplashScreen && SplashScreen.hide();
  }

  render() {
    return (
      <>
        <ForegroundService navigation={this.props.navigation} />
        <Navigator {...this.props} />
      </>
    );
  }
}

export default AppNavigator;


Further, this navigator can be used in the usual way in the root navigator:

const RootNavigator = createSwitchNavigator(
  {
    Auth: AuthNavigator,
    App: AppNavigator,
  },
  {
    initialRouteName: 'Auth',
  },
);

export default createAppContainer(RootNavigator);


Thus, the geolocation and data exchange service will work only in the authorized part of the application.

To summarize


In this article, we examined the features of using React Native in geolocation applications. As we found out, using this framework, developers can now easily track the geographical coordinates of the device - for example, in bicycle trackers, applications for couriers and forwarders, applications for choosing real estate, etc. The library covers most cases related to geotracking, in particular, it allows you to adjust the intervals and save device power, it works stably in the background on both platforms, and supports autorun on Android. At the same time, for complex applications, as a rule, native development is more suitable (for example, for working with calculations and widgets, as we already wrote in the previous article).

Thank you for your attention, we hope that the article was useful to you!

All Articles