Building a Real-time Location Tracking app with NativeScript-Vue under 350 lines of code 🔥

Suhaib Khan
Feb 14, 2019 · 7 min read

I recently tried my hands on NativeScript-Vue, just out of curiosity to experience how the most starred* JavaScript Framework on GitHub can help me in my app development process. Just 45 minutes into it, I ended up having this Real-time Location tracking app working flawlessly on both iOS and Android. In this write-up, I’m gonna walk you through the process of building this app from scratch.

Assuming :

Let’s just jump into it.

Non MacOS users like me need Nativescript Sidekick installed to be able to run iOS apps.

Step 1: Project Setup

First, lets create a new project based on the vue-cli-template (a blank Nativescript-Vue app) using terminal.

npm install -g @vue/cli @vue/cli-init
vue init nativescript-vue/vue-cli-template <project-name>
cd <project-name>
npm install
  1. Installs the latest Vue CLI and support for Vue CLI 2.x templates through the @vue/cli-init add-on.
  2. Creates a project using the vue-cli-template
Project configuration

3. Switches to the directory containing the newly created project.

4. Installs npm dependencies locally.

Step 2: Setting up project dependencies

For this project, we are going to use three NativeScript Plugins. Install them using NativeScript CLI tooling:

tns plugin add nativescript-google-maps-sdk
tns plugin add nativescript-geolocation
tns plugin add nativescript-permissions

Also, one npm package decode-google-map-polyline will be required for drawing routes on the map. Install it using:

npm install decode-google-map-polyline

Step 3: Getting the API Key(s)

Only registered Google users can gain access to API key(s) required for Maps to work properly. Key(s) can be obtained from Google Cloud Console.

For our project we need access to 4 Google APIs:

  1. Maps SDK for Android
    For rendering maps on Android devices.
  2. Maps SDK for iOS
    For rendering maps on iOS devices.
  3. Directions API
    For getting routes between locations.
  4. Distance Matrix API
    For calculating distance and Estimated Time of Arrival.

Clicking on the above 4 mentioned APIs will redirect you to their official documentation and also guide you on getting hold of the API key(s).

Google Cloud Console will ask you to create a project before starting to mess up with the APIs. Once you have created a project and a key is issued for an API, subsequent enabled APIs will be accessible using the same key which was issued for the first API. That means, the 4 APIs listed above can have one single key if falling under the same project, which exactly is the case with this app.

Step 4: Setting up the API key

Now that we have got our precious key with all required APIs enabled, we can proceed by letting Android and iOS know about it.

For Android : Modify AndroidManifest.xml located at app/App_Resources/Android/src/main/AndroidManifest.xml and insert this between <application> tags.

<meta-data android:name="com.google.android.geo.API_KEY" android:value="PUT_API_KEY_HERE" />

For iOS: Add the following snippet at the top of main.js

import * as platform from 'platform'if (platform.isIOS) { 
GMSServices.provideAPIKey("PUT_API_KEY_HERE")
}

Replace PUT_API_KEY_HERE with the actual key.

Step 5: Registering MapView Element with Vue

The Google Maps plugin we are using, does not support Vue out-of-the-box, we need to manually register MapView element in JS to be able to use it in our template layout. Add the following line of code to main.js :

Vue.registerElement('MapView', ()=> require('nativescript-google-maps-sdk').MapView)

After this step, our main.js should look something like this :

Step 6 : The Layout

As this project was initialized from vue-cli-template, you might have noticed main.js is importing a component named App located at ./components/App.vue. The actual layout and business logic of the app is defined inside this component, so lets start re-writing App.vue to our own specifications. At this point, we can remove everything present inside the file and paste the following template :

Keep in mind, we are going to use some method names and properties in this template for event handlers and data binding, which as of now are undefined and will be defined later in the <script> part of this component.

Visually, we have divided our UI into three main sections:

  • The top most part is the control center, consists of 4 buttons, each having its own @tap event handler :
    1. Get Directions
    2. Clear Route
    3. Start Journey
    4. End Journey
  • The middle part is the actual Google MapView having @mapReady (when the map has completed rendering) and @coordinateLongPressed (when a point on the map is long tapped) event handlers in place. Also, latitude,longitude and zoom attributes of the element are binded to the origin and zoom properties of the component respectively. Plus, the MapView only renders if the allowExecution property is set to true.
  • Lastly, the bottom part is simply a TextView binded to journeyDetails property to print the status of our journey.

Normalizing the layout :

A little bit of styling wont hurt, so let’s add the <style> section of our Vue component to bring some colors and alignment in place.

Before/After Styling

Step 7: Business Logic

Now that layout is done, we will start writing the <script> part of the component containing the business logic. But before starting let’s see an overview of what the script is gonna look like:

Comments are self explanatory

7.1 Data function:

returning data specific to the template part and permissions i,e:

Quick Summary:

origin : having co-ordinates fed to the map.
destination : storing destination co-ordinates.
journeyDetails : binded to the <TextView> to display journey details.
allowExecution : will hold location permission access status for Android.
journeyStarted : will hold journey status.
mapView : will be used to store the rendered map instance.
zoom : adjusting camera zoom level.
APIKEY : reference to our enabled API key.

7.2 Created Hook:

Executed at the beginning, it will be used to request Location access on Android devices. iOS will automatically asks for permission when the app attempts to access it but for Android we have to manually pop up the permissions dialog to get it through and we are going to achieve it by, adding the permissions and platform import at the top of <script> tags:

import * as permissions from 'nativescript-permissions'
import * as platform from 'platform'

and then write the permission requesting routine:

Also, add the following permissions to app/App_Resources/Android/src/main/AndroidManifest.xml :

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />    
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

7.3 Methods Object:

This is where all the magic happens. As stated above, it will consist of 6 methods used as event handlers in our template. Instead of writing the actual code, I have commented what exactly these methods are going to do. Go through this once, to get an idea.

For most of the commented lines above, we’ll be calling methods defined in another file, that will merge with our component options using Mixins . We could have defined everything in App.vue only, but sharing some space with helper Mixins will make our code more maintainable. We’ll come back to this methods object after we finish writing our helper objects.

7.4 Mixins

Create a new file named MapsHelper.js in/app/components/ and add the following imports at the top of the file :

import * as http from "http";
import * as geolocation from "nativescript-geolocation";
import { Accuracy } from "ui/enums";
import * as platform from "platform";
import * as decodePolyline from "decode-google-map-polyline";
import { Position, Marker, Polyline, Bounds } from "nativescript-google-maps-sdk";

Quick Summary:

http: for making http(s) requests.
geolocation : to access device location.
Accuracy : used by geolocation for imporving location accuracy.
platform : to detect device type.
decodePolyline : for decoding encoded points provided by Directions API. Position : creates a Position object from coordinates (fed to the map).
Marker : for adding marker(s) to the map.
Polyline : for drawing route(s) on the map.
Bounds : for setting camera view bounds (portion of map visible).

Rest of this file is going to 4 objects, namely,
a) MapsUIHelper
b) DirectionAPIHelper
c) DistanceMatrixAPIHelper
d) LocationHelper

that we’ll ship collectively at the end of the file and then import in App.vue to use as mixins. Most of the code below is self explanatory, so I won’t go into details, however objectives of each object are listed.

7.4.1 MapsUIHelper

Objectives: Add markers, set their properties and toggle my location button on the map.

7.4.2 DirectionsAPIHelper

Objectives: Hit Directions API, draw a polyline (route) over the received polyline points and get that route into view.

7.4.3 DistanceMatrixAPIHelper

Objectives: Get distance and ETA (Estimated Time of Arrival) between two locations and update journey details (TextView).

7.4.4 LocationHelper

Objectives: Fetch GPS location, watch for location changes and update journey details and polyline (route) accordingly.

The final part of the file would be to export all these components:

export default { MapsUIHelper, DirectionsAPIHelper, DistanceMatrixAPIHelper, LocationHelper };

Coming back to App.vue, lets import the helpers,

import MapsHelper from "./MapsHelper.js";

update Mixins array,

mixins: [ MapsHelper.MapsUIHelper, MapsHelper.DirectionsAPIHelper, MapsHelper.DistanceMatrixAPIHelper, MapsHelper.LocationHelper ]

and (re) write methods :

The nativescript-google-maps-sdk plugin we are using (at the time of writing) has some wierd iOS bug causing the map center to move up to the top-left corner. It can be fixed by uncommenting the 6 lines below/* ios map center bug fix */ block in mapReady method. More info at this SO answer.

That’s it! Our app is ready.

Try tns run android / tns run ios to test. For iOS provisioning profile and certificate, I used NativeScript Sidekick to automate the process. If something didn’t work for you, feel free to comment down the issue and I will respond.

Show me the code !?

Here, you go : GitHub : saibbyweb/RLTNSVue


This is my first ever Medium write up, if it was helpful in anyway, do show your support by following my publication @saibbyweb and me (Suhaib Khan).

EDIT : Thanks 丁丁(DingDing) for pointing out the mistakes :)

Written with ❤ in the most militarized zone in the world.

saibbyweb

web & app development agency.

Suhaib Khan

Written by

Editor saibbyweb. In love with code.

saibbyweb

saibbyweb

web & app development agency.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade