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.
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 uniquekey
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.