This article about building a location-based app with react native is written by Julia Korsun.
What good can happen when we tap Allow on the pop-up that asks to access our location? Some apps provide a better experience, like Facebook suggesting events nearby. Others — can’t work properly without knowing device location, like Uber or Google Maps.
These location-based apps use device location to enable and control some features. From wok delivery to Find My iPhone, location-based apps help us with our everyday tasks just by knowing where we are.
Location can be either the primary function, like in Tinder; or auxiliary, like in Instagram: when uploading a photo, Instagram will suggest you a place so you can tag your location. Whether it’s the main function or not, location does improve the user experience.
In this article, I’ll tell you about the main tech components of location-based apps, and you’ll learn how to develop one using React Native. First, I’ll briefly describe React Native and compare it to native app development. Then I’ll share approaches to gathering and displaying the location in an app, and finally, in a short passage I’ll describe some of the design challenges and how React Native copes with them.
What to Consider When Building the Backend for a Location-Based Service_If you make a quick review of apps in various categories — healthcare, games, finance, — it will show that the device’s…_djangostars.com
In this part, I will briefly describe the React Native framework, its pros & cons, and why it’s great for building location-based apps. React Native is an open source JavaScript framework that allows developers to create cross-platform apps with their native behavior.
What is behavior in this description? Let me explain. iOS and Android are different — their interface is different, their animation is different, lots of things are different. Having the same app for iOS and Android would require developing two separate apps. It used to be a laborious process, but using React Native, developers write one code, and it works correctly on both platforms.
This allows businesses to offer their app to both iOS and Android users which means a bigger market share. That’s why many companies prefer React Native — they can’t afford developing two separate apps or are confident about whether their users have iOS or Android. And considering that the cross-platform market is likely to grow to $80 billion by 2020, it seems a rational choice for startups.
Now, I’ll explain the pros and cons of React Native in terms of developing location-based apps.
However, there are cases when you might not want to use React Native.
They include:
This part is about gathering and displaying the location data. Depending on the specifics of your app, you will opt for a particular way.
I single out three ways to gather device location.
Note: This is a generic overview for you to understand the cases when to opt for each case and the differences between them.
There’s a native JavaScript API that identifies the location of a device. It’s easy to install and use, but there’s a ‘but’ — it neither works on the background nor shows the location provider (3G, Wi-Fi, GPS).
It’s a package that determines the location of a device from 0 to 1000 meters (0.6 miles). It takes more battery energy, but on the other hand, it’s up to you to configure how often to track the location. The package also integrates with SQLite — you can store the recorded location data and sync it to your database via HTTP.
import { NativeModules, DeviceEventEmitter, PermissionsAndroid } from 'react-native' import Config from 'react-native-config' import get from 'lodash/get' const { GeoLocation } = NativeModules
class BackgroundGeoLocation { constructor(token, user_id) { this.state = null } start(dispatch, nextState) { this.dispatch = dispatch const token = get(nextState, 'session.data.token') const user_id = get(nextState, 'user.data.user_id') const id = get(nextState, 'user.data.id') this.state = { user_id, token, }
return PermissionsAndroid.check(PermissionsAndroid.PERMISSIONS.ACCESS_COARSE_LOCATION) .then(is_granted => is_granted === PermissionsAndroid.RESULTS.GRANTED ? is_granted : PermissionsAndroid.requestMultiple([ PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION, PermissionsAndroid.PERMISSIONS.ACCESS_COARSE_LOCATION, ]) ) .then(_ => PermissionsAndroid.check(PermissionsAndroid.PERMISSIONS.ACCESS_COARSE_LOCATION)) .then(is_granted => is_granted === PermissionsAndroid.RESULTS.GRANTED ? true : new Error()) .then(_ => setTimeout(() => GeoLocation.startService(token, user_id, id, `${Config.API_URL}/live/car-tracking/gps-pos/`), 300)) .catch(e => console.log(e)) } stop() { return GeoLocation.stopService() .then(_ => console.log(_)) }
handleLocationChange(geo) { console.log(geo) }}
export default BackgroundGeoLocation
Being a package, it requires regular maintenance and updates, but there’s a support channel on GitHub from the creator of the package.
Note: Package price: iOS — free; Android — $300 for one app.
The main issue with the first approach (to use native JavaScript API) can be solved by adding native code that will start a foreground service in a separate thread. That’s exactky what we did when developing our food delivery product Azyan. Partly, we chose this way because React Native made it easy to bridge native code to React Native components.
package com.djangostars.azyan;
import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.location.Criteria; import android.location.Location; import android.location.LocationManager; import android.os.Build; import android.support.v4.content.ContextCompat;
import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableNativeMap;
/** * Created by AGulchenko on 5/7/18. */
public class GeoLocationModule extends ReactContextBaseJavaModule { public static final String CHANNEL_ID = "ExampleService_Channel"; public GeoLocationModule(ReactApplicationContext reactContext) { super(reactContext); if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel(CHANNEL_ID,"testName", NotificationManager.IMPORTANCE_DEFAULT); NotificationManager manager = reactContext.getSystemService(NotificationManager.class); manager.createNotificationChannel(channel); } }
@Override public String getName() { return "GeoLocation"; }
@ReactMethod public void startService(String token, String user_id, String id, String url_string, Promise promise) { WritableMap result = Arguments.createMap(); result.putString("ststus", "success"); try { Intent serviceIntent = new Intent(getReactApplicationContext(), GeoLocationService.class); serviceIntent.putExtra("token", token); serviceIntent.putExtra("user_id", user_id); serviceIntent.putExtra("id", id); serviceIntent.putExtra("url_string", url_string); getReactApplicationContext().startService(serviceIntent); promise.resolve(result); } catch (Exception e) { e.printStackTrace(); promise.reject("rrrrr",e); return; }
}
@ReactMethod public void stopService(Promise promise) { String result = "Success"; try { Intent serviceIntent = new Intent(getReactApplicationContext(), GeoLocationService.class); getReactApplicationContext().stopService(serviceIntent); } catch (Exception e) { promise.reject(e); return; } promise.resolve(result); }
@ReactMethod public void getLocation( Promise promise) { WritableMap res = Arguments.createMap(); try { LocationManager locationManager = null;
locationManager = (LocationManager) this.getReactApplicationContext().getSystemService(Context.LOCATION_SERVICE); int permissionCheck = ContextCompat.checkSelfPermission(this.getReactApplicationContext(), android.Manifest.permission.ACCESS_FINE_LOCATION); if (permissionCheck == PackageManager.PERMISSION_GRANTED) { Criteria criteria = new Criteria(); String bestProvider = locationManager.getBestProvider(criteria, false); Location location = locationManager.getLastKnownLocation(bestProvider); if(location != null) { res.putDouble("latitude", location.getLatitude()); res.putDouble("longitude", location.getLongitude()); promise.resolve(res); }
} } catch (Exception e) { promise.reject(e); return; } }}
Getting permission to access the location data sometimes causes troubles, but the troubles are bearable. React Native solves the problem without too much fuss.
Different systems ask for the permission to access the location data on different stages: iOS requests the permission first time you open an app; Android — upon the download. It could cause trouble if we were using the native code, however, React Native simplifies this process using the check access to location data module. It allows access to location data without triggering the permission alert.
The location data isn’t always precise. You must have seen something like this:
Here’s why it may happen: the device collects the location data from three sources: Wi-Fi, cellular and GPS, the latter being the least accurate. Our devices are in the constant state of checking if there’s good Internet connection. If there’s none, the device will enable GPS. And if there’s a quick leap from 4G to GPS, the device is _‘_lost’.
To solve this problem, I recommend Fused Location Client by Google. It allows you to set the time and distance at which the location data is updated: for instance, update the data every 50 meters and every 10 seconds. You will avoid the noisy data as this API matches all device locations to roads and sidewalk. However, if the device is far from either, it won’t be effective.
A Few Words About Design
In this short part, I will tell you about obstacles that may arise with building location-based apps and how to solve them with React native components.
React Native allows for simple ways of displaying maps. It UI components for Material Design simplify the job for engineers. We would use Material Design to create a Google Maps wrapper, and then React Native would adjust them to the specific features of each platform.
Infinite List is a React Native feature that makes an endless list of search results. In Uber, if you start typing an address, you will get all the destinations starting with what you’ve entered. So try 3rd Ave, it will show all 3rd Aves around. Such lists aren’t really endless — they just load more results as we scroll down through them. Flat List — another build-in UI component includes the fixed header, footer, and delimiters to the search. If you were to create such result lists from scratch, it would take you more time than building a complete React native app.
<FlatList data={get(props, 'order.data.items', [])} renderItem={listItem} keyExtractor={({ id }) => id} style={style.list} />
function listItem({ item: { quantity, name } }) { return ( <View style={style.main}> <Text style={[style.count, style.listItemText]}>{quantity}</Text> <Text style={[style.listItemText, style.name]}>{name}</Text> </View> )}
Bottom Line
React Native may be the right choice if you are going to build a location-based application. If most of the described advantages are true for you, go do deeper research on the technology and its capabilities.
I now encourage you to pay more attention to the apps you use. You’ll be surprised to find out that most of them ask to allow access to your location. For many companies, it’s crucial to know user location to provide quality and more user-oriented service. Why don’t you try?
If you find this post useful, please tap 👏 button below :)