Hire Us
Maps with React Native, Rails, and PostgreSQL

Maps with React Native, Rails, and PostgreSQL

Railsware is a company that deals with numerous tasks and ideas associated with web and mobile development. Our engineers always seek the best solution for optimization of both performance and budgeting. The core vision of Railswarians is a lean approach.

In this overview, we are going to review a development of a feature of geolocation search around user’s current location. React Native was chosen as the framework for building a single application for both the most popular operating systems in the market, namely iOS and Android. The backend part was put into effect through Rails as a web framework and PostgreSQL as a database. Are you ready to learn details and peculiarities of using such an approach for mobile app development? You are welcome to read our experience.

Geolocation feature in mobile app

Let’s begin our publication with the core points related to frontend development. So, using React Native is opening the case.

Track current location

Setup

Before enabling geolocation tracking in the application, you should remember that the final product is targeted at both cohorts of mobile device users. We’ll look at Apple first.
In order to enable geolocation tracking on iOS, you need to include NSLocationWhenInUseUsageDescription key in Info.plist

NSLocationWhenInUseUsageDescription
The reason why your app want to use location goes gere

A location request access for Android devices is implemented through adding AndroidManifest.xml to your application:

 

React Native Geolocation API

The React Native Geolocation API extends the Geolocation web specs and is available globally through the navigator.geolocation.

There is the getCurrentPosition function that helps access current position:

Geolocation.getCurrentPosition(success, [error], [options]);

Once the latest location information is available, the success callback is invoked. However, the we decided to make a small wrapper function that returns Promise and hides callbacks logic inside, so that it could be easily chained with other asynchronous actions:

getCurrentPosition() {
  return new Promise((resolve, reject) => {
    navigator.geolocation.getCurrentPosition(
      ({coords}) => resolve({
        latitude: coords.latitude, longitude: coords.longitude
      }),
      reject,
      {enableHighAccuracy: false, timeout: 5000, maximumAge: 1000}
    )
  })
}

Using react-native-maps

In order to display maps, the react-native-maps package was used. Although this solution is imperfect due to multiple bugs and lack of documentation to cope with them, it provides a set of features we need to display information on maps. Moreover, it works on both iOS and Android at the same time.

Render map

We used a default setup where both mobile operating systems render their own maps: Apple Maps on iOS and Google Maps on Android.

The MapView component is built to show maps on the device. It provides a react-like API for declaratively controlling features. All features on maps including Markers, Polygons, etc. should be specified as children of this component. Have a look at the following example use:

  

  • showsUserLocation property is used to show natively user location.
  • initialRegion displays the initial region on the map.
  • style is basically set to be positioned absolute and fill the entire screen.

A complete API description of MapView component can be found in the following documentation.

Render Markers on the map

The PlacesMarkers is a React Native component implemented in our application to render place markers on the map. Its functionality is as:

render() {
  const {places} = this.props

  if (!places.length) return null

  return (
    
      {
       places.map(place => {
          return (
            
          )
        })
      }
    
  )
}

The list of places to render on the map pass as props to this component (it comes from the redux state but we skipped this part here).

Another component provided by react-native-maps is called MapView.Marker. It renders markers on the map and provides a set of features described in the documentation. Of all them, we used the following ones:

  • coordinate – to place marker to the proper position;
  • key – since we render a list of items, a unique key value is required by React Native;
  • image – to specify a contone image to be shown as a marker.

Issues

As it was mentioned above, there are a lot of issues documented in the repo. Some of them were reported more than one year ago but still persist. One such issue is the display of distant markers in the top left corner of the maps. We exhausted multiple approaches to fix it and eventually solved with a funny hack: we simply moved the entire map 20 pixels up (marker height), so that these markers always remain out of the visible area for user :)

map: {
  ...StyleSheet.absoluteFillObject,
  top: -20 // hack required for https://github.com/airbnb/react-native-maps/issues/678
}

Backend

That’s it for the frontend. Let’s take a look at the backend now. We’ll put aside React Native and focus on other tools we used. Rails was chosen as a web framework to build API for mobile applications and PostgreSQL – as a database. The latter draws our special attention.

Using PostGIS to search within an area

PostGIS that is a spatial database extender for PostgreSQL database was used to search places within an area. The extender adds support for geographic objects enabling the user to run location queries in SQL.

You should use the following migration to enable PostGIS extension:

class EnablePostgis < ActiveRecord::Migration[5.1]
  def change
    enable_extension :postgis
  end
end

All our places have latitude and longitude fields containing coordinates on the map. Hence, our ActiveRecord model scope looks as follows:

class Place < ApplicationRecord
  scope :within, -> (latitude, longitude, distance_in_meters = 1) {
    where(
      format(%{
          ST_DWithin(
            ST_GeographyFromText(
              'SRID=4326;POINT(' || places.longitude || ' ' || places.latitude || ')'
            ),
            ST_GeographyFromText('SRID=4326;POINT(%f %f)'),
            %d
          )
        }, longitude, latitude, distance_in_meters)
    )
  }
end

PostGIS provides the ST_DWithin spatial function, which was used to check whether places are located within the specified distance from the current user location passed to the scope.

Conclusion

The above-described issues should be the bullet points to consider in projects dealing with embedding a geolocation search feature. Certainly, it refers to using React Native as a frontend technology, and Rails plus PostgreSQL as tools to build the backend part. The mentioned technologies proved their efficiency and practicability for such type projects.