Build a geofencing app

How-To Details

One of the most useful features of mobile devices is it’s location-awareness. It helps you navigate, automatically switch to the timezone you’re in, allow for location targeted push notifications, or give insights into whatever activities take place at a certain location.You could say there are two types of location services:

Active, i.e. where you perform a task based on your location (for instance turn-by-turn navigation, checking into a place on social media, find the nearest espresso bar, etc.)

Passive, i.e. where your mobile device performs an action based on your location (for instance location based mobile advertising, notify warehouse workers a truck is about to arrive at the dock, alert people of entering a dangerous area, open your garage door when you approach your home, etc.)

One solution to be able to passively act on your current location is by using geofences. A geofence is nothing more than an area on a virtual map. And when a mobile device enters or leaves such predefined areas, this can be detected and an application can act on that: send a notification, update a backend system, trigger another hardware device to do something; basically anything that can be done manually can now be done automatically.

In this tutorial, you will first create an SAP HANA MDC database which will hold location data as geofences. You then expose this data via an OData service. Then you will build an app using this OData service, add logic to display the stored geofences, as well as perform action when your device enters these geofences.

Go back to the sapgeoOverview page and click the SAP HANA Web-based Development Workbench link. If being asked your login credentials, provide the same SYSTEM user credentials you used to accessing the SAP HANA Cockpit.

To allow creation and administration of the database and service, you need to assign the correct roles to the user.

For the simplicity of this tutorial, you will use the sapgeoSYSTEM user to create and maintain the database. In a real-world environment, however, you would never use the SYSTEM user but use a dedicated user.

.

Click on the Security tile of the SAP HANA Web-based Development Workbench landing page.

In the left pane, navigate to the SYSTEM user:

In the Granted Roles tab, click the Add button. Add the following roles:

Click the Run button (or press F8) to execute the command. The console in the bottom pane should indicate a successful execution and you should see the newly added table under the SAPGEO schema.

The just created table is used to store the geofence data you will use in the mobile app.

NOTE 1: Ultimately, geofences can be any two-dimensional polygon. However, since iOS by default only supports circular regions and it takes quite some extra coding to support polygon regions as well as a more complicated database structure to store this polygon data (see also Note 2 below), this tutorial will use the simple circular region.

NOTE 2: Instead of using separate latitude and longitude columns, SAP HANA supports geospatial columns of type ST_POINT and ST_POLYGON. These columns, however, are stored in a binary format, and are currently not supported by OData version 2 which you will be using for this tutorial (there is limited supported in OData version 4). For simplicity of the service and not doing any conversions, a simple table with separate latitude, longitude and radius columns is used instead to store the geofence properties, but this could be easily adapted for use with a ST_POINT or ST_POLYGON column.

For this tutorial, it is assumed the geofence data is already stored in the database. In a real-world scenario, geofence data can be provided in many ways. For convenience of not having to run around with your mobile device to manually submit geofences to the database, but also to simplify the mobile app coding, you will just run a few SQL INSERT statements with pre-set data.

The easiest way to retrieve longitude and latitude data from a map would be to use Google Maps.

Click on an area on the map (preferably a location near or at your current location) and copy the coordinates displayed at the bottom of the screen:

Click on the Editor tile of the SAP HANA Web-based Development Workbench landing page. After you have provided the SYSTEM user credentials, the editor workbench opens:

Right-click the Content node, and from the context menu, select New > Package. In the dialog, enter the following details:

Field Name

Value

Package name

sapgeo

Click Create when finished. Select the new sapgeo package, and from the toolbar click the Menu button and select File > Create Application. In the dialog that appears, specify the following:

Field Name

Value

Template

Empty application (with XSAccess and XSApp)

Package

sapgeo

Click Create when done. The sapgeo package now expands and should contain the files .xsaccess, .xsapp and index.html.

To allow execution of the OData service you will create later on, you first set the privilege to do so. Right-click the sapgeo package, and from the context menu, select New > File. Specify a file name .xsprivileges.

Log on to your SAP Cloud Platform mobile service for development and operations cockpit, and navigate to Mobile Applications > Native/Hybrid. Click the New button, and in the dialog, add the following information:

Field Name

Value

Configuration Templates

Native

ID

com.sap.tutorials.demoapp.SAPGeo

Name

SAPGeo

Click Save when finished. You should now see the application definition details:

The Connectivity feature is listed as Incomplete, because you haven’t yet specified which OData service the application will use. Click the Connectivity item, and in the following screen, click the Create Destination button. In the dialog that appears, enter the following data:

Field Name

Value

Type

Mobile Destination

Destination Name

com.sap.tutorials.demoapp.SAPGeo

Click Next. In the next page, specify the following data:

Field Name

Value

URL

<OData URL you noted at the end of Step 7>

Proxy Type

Internet

Maximum Connections

10

Timeout

0

Rewrite Mode

Rewrite URL

Click Next. In the next page, specify the following data:

Field Name

Value

SSO Mechanism

Basic Authentication

Click Next. In the next page, specify the following data:

Field Name

Value

User Name

SYSTEM

Password

<SYSTEM user password>

For the simplicity of this tutorial, you will use the sapgeoSYSTEM user to access the database. In a real-world environment, however, you would never use the SYSTEM user but use a dedicated user to access the database.

.

Click Next. In the next page, no changes are required:

Click Finish to complete the wizard. The dialog will close, and the connection is created:

Click the Ping button next to the destination to check whether the OData service is accessible from the destination.

Click the Plus button on the top-right of the SDK Assistant. The first page of the Xcode Project generation wizard lets you define the Project Properties.

Enter the following details:

Field

Value

Product Name

SAPGeo

Author

<your name>

Organization Name

<your company name>

Organization Identifier

com.sap.tutorials.demoapp

Destination

<choose a local destination>

Make sure <Organization Identifier>.<Product Name> matches the value of Application ID you entered in Step 8

.

Click Next to advance to the SAP Cloud Platform mobile service for development and operations Configuration step.

In the SAP Cloud Platform mobile service for development and operations Configuration page, select the Use Existing tab button.

Click the Select from SAP Cloud Platform mobile service for development and operations button next to Application Identifier.

Select the com.sap.tutorials.demoapp.SAPGeo data source and click OK.

The selected data source is now pre-filled in the SAP Cloud Platform mobile service for development and operations Configuration page.

Click Next to advance to the OData Services step.

In the OData Services page, the primary OData service connection you have specified in the previous wizard step is displayed:

Click Next to advance to the Optional Features step.

In the Optional Features page, you have the option to generate a Master-Detail Application, enable logging and log uploads, and enable remote notifications.

Make sure the checkboxes Generate Master-Detail Application, Enable Logging and Enable Log Upload are selected and click Finish to complete the wizard.

After you have clicked Finish in the previous step, the SDK Assistant now loads the OData service’s metadata. This metadata describes the data model, and can be accessed via <service URL>$metadata. For your service, the metadata URL would be https://sapgeo<your account>trial.hanatrial.ondemand.com/sapgeo/SAPGeoService.xsodata/$metadata

If you have followed the tutorial to the letter, you may now get a message the SDK Assistant could not load the metadata. This happens because the application definition created in Step 8 by default is configured with SAML authentication. If you see this warning, simply download the contents of the https://sapgeo<your account>trial.hanatrial.ondemand.com/sapgeo/SAPGeoService.xsodata/$metadata locally, and upload it to the SDK Assistant.

.

After the SDK Assistant has finished, Xcode will launch and open the just generated SAPGeo project.

In this step, you will add a new View Controller which will display a map with the stored geofences. In all fairness, for the geofences to work you don’t need to see the geofences in a map at all. For the purpose of the tutorial, having a visual clue of the geofence locations, it should make things a bit more clear.

Open Main.storyboard, and from the Object library, drag a View Controller right next to the Collections scene. With the new view controller selected, set its title to Map View Controller in the Attributes inspector:

Next, drag a Map Kit View from the Object Library onto the Map View Controller. Resize the map so its borders align with the view’s dimensions:

With the map control still selected, click the little “triangle TIE-fighter” button in the lower right of the storyboard and from the context menu, select Reset to Suggested Constraints. This ensures that regardless of the screen dimensions your app will run, the map viewport will have the same dimensions as its parent view controller.

You have created the map view as well as a custom implementing class, but there’s no navigation path to that view. Since the view is merely intended as feedback, it makes sense to navigate to the map via a toolbar action.

First, you need to enable the toolbar. Select the Navigation Controller connected to the Collections scene, and from the Attributes inspector, tick the checkbox next to Shows Toolbar.

Next, drag a Bar Button Item onto the toolbar of the Collections scene. In the Attributes inspector, set the button’s title to Show Map:

Finally, Ctrl-drag from the toolbar button to the Map View Controller scene. From the action list, choose the Show Detail action segue.

Select the segue, and in the Attribute inspector, provide the IdentifiershowMap:

If you now build and run the app and click the Show Map button in the toolbar, a map zoomed to display the country you’re currently in is displayed:

In the next steps, you will implement logic to visually show the geofence data stored in the SAP HANA MDC.

With the Map View Controller selected in the Storyboard, click the Show Assistant Editor button. The custom MapViewController.swift file you created earlier is now opened.

To create an outlet for the Map control, Ctrl-drag from the Map control to the MapViewController.swift file, below the class definition. Name the new outlet mapView:

Click Connect once done. Your code should now give an couple of errors. This is because it cannot resolve the MKMapKit class for the mapView outlet:

Add the following import statements:

import MapKit
import CoreLocation
import SAPCommon

The MapKit import solves the error message, and CoreLocation is needed to determine your location, as well as handling the geofences later on in the tutorial. The SAPCommon import is used to implement the SDK’s Logger functionality.

Add the following private stored properties just above the viewDidLoad() method:

Here you set the view controller as the delegate for both the mapView and locationManager instances. You also set the required location permissions to Always. This is needed because you want the app to monitor geofences also when the app is not running. To allow the user to grant this authorization, open Info.plist and add the following two entries:

Field

Value

Key

Privacy - Location When In Use Usage Description

Value

SAPGeo requires your location in order to notify you when you enter a geofence

Field

Value

Key

Privacy - Location Always And When In Use Usage Description

Value

SAPGeo requires your location also when you are not using the app

Now you only need to display your current location on the map, and correct the two errors that are shown in the editor. These errors are because you have set the view controller as a delegate, but you haven’t yet implemented the required delegate methods.

At the bottom of the MapViewController.swift file, add the following extensions:

The delegate methods for the mapView instance will be implemented later, and will eventually display a pin and overlay for the geofences onto the map. The delegate method for the locationManager instance enables the mapView instance to display your current location. If you now build and run the app and navigate to the map, you should first grant access for the app to always use your location:

If you run the app from the simulator, click the Simulate Location button and select a location nearest to you:

On the map, scroll to the selected location, and you should now see a blue dot with your simulated location:

In this step, you will display the geofences stored in the SAP HANA MDC onto the map.

The OData service returns instances of GeoLocation. While this is perfectly fine, it is convenient to translate these into objects which are easier to handle for both the map as well as the location manager. For both offline storage as well as using the object as an annotation on the map, the class should implement both NSCoding as well as MKAnnotation.

Right-click the Model group in the Project navigator, and select New File…. Add a new Swift File and name it SAPGeoLocation. An empty file is created:

The constructor takes the OData service’s GeoLocationType instance as input, and creates a SAPGeoLocation which implements both NSCoding and MKAnnotation. The required init? and encode methods implement the NSCoding’s required decode and encode functionality, respectively. The structure is for convenience and contains the property names as strings.

Switch back to the MapViewController.swift file, and add the following private method to the MapViewController class:

This method takes the array of SAPGeoLocation objects, adds an annotation to the map, and in addition, adds a circle at the given coordinates and radius. For both these calls, the respective delegate methods of the mapView instance are called, but they are not yet implemented. Find the MKMapViewDelegate extension and replace both delegate methods with the following two methods:

The first delegate method is called when the map instance’s addAnnotation method is called. It adds an MKPinAnnotationView instance with a pin in one of the standard SAP Fiori colors. The second delegate method checks whether the object being added is of type MKCircle, and adds it as an overlay on the map, again with one of the standard SAP Fiori colors.

The one thing missing is to actually load the stored GeoLocationType objects, and call the methods to plot them on the map.

Just below the stored property logger, add the following stored property referencing the applications AppDelegate:

This method loads the actual GeoLocationType entities from the OData service, converts the resulting array to an array of SAPGeoLocation objects, which is then provided to the previously created renderLocationsOnMap(locations:) method. Call this loadLocations() function at the end of the viewDidLoad() method so it resembles this:

If you now run the app, you should see one or more pins marking your stored geofences. If you click on it, it shows the call-out with the geofence title and subtitle, as well as a detail disclosure indicator as specified in the mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) delegate method:

Zoom in (with the simulator, use Alt-Click for two-finger pinch) and you should now see the circular representation of the geofences too:

Depending on how far apart you have specified your geofences, you might need to zoom in significantly to distinguish the various geofences you have defined in the database. In this step, you will add a toolbar button which will zoom in to the selected geofence.

Open the Storyboard and drag a Bar Button Item onto the Map View Controller’s toolbar. In the Attribute inspector, set the title to Zoom to geofence:

Open the Assistant editor, and Ctrl-drag the newly added toolbar button to the MapViewController class, just below the mapView outlet.

This method checks if a geofence is selected on the map, and then sets the map viewport to center at the geofence coordinates, and span the north-to-south distance as well as the east-to-west distance to approximately 250 meters.

Run the app, select a pin on the map, and click the Zoom to geofence button. You will now zoom in on the map with the selected geofence in the center:

Method getRegionForLocation(location:) takes an SAPGeoLocation instance as input, and creates a CLCircularRegion instance off it. A CLCircularRegion is a circular region defining the actual geofence at the specified location. The geofence is set up so the location manager gets notified only when you enter the geofence.

You could extend the GeoLocation database table to have extra columns for notifications upon entry and exit, making this a dynamic instead of a fixed setting.

As stated before, you want to detect geofence events even when the app is inactive, not running or offline. The way the location manager works is, if your device detects a geofence event, it will launch the app in the background. Acting on geofence events is then best done in the app’s AppDelegate class.

Open AppDelegate.swift and import CoreLocation:

import CoreLocation

Then, add the following stored property:

let locationManager = CLLocationManager()

Locate method applicationDidFinishLaunching(_:) and below the line UINavigationBar.applyFioriStyle() add the following:

Method getGeoLocation(fromRegionIdentifier:) retrieves an instance of SAPGeoLocation which has been stored in UserDetails. Method handleEvent(forRegion:didEnter:) takes the CLRegion geofence received from the CLLocationManagerDelegate delegate, and displays a notification.

Your app is now ready to test the stored geofences. You could now deploy the app on a physical device and drive around town, but that would be both quite cumbersome as well as impossible to detect any failures or analyze logged messages. You could, however, test geofences using a GPX file.

At the root of your project, add a new Group and name it Test. Right-click the Test group and from the context menu, select New File…. From the dialog, choose GPX File:

Click Next. In the next page, name the file TestLocations and make sure it sits in the Test group:

Click Create when done. A new TestLocations.gpx file is added to your project:

Add at least two waypoints which will cross one or more geofences:

If you do an online search for “GPX generator”, you will find some tools which allow you to simply click on a map and generate a GPX file with a series of waypoints

.

Now, build and run the app in the simulator. If the app runs, click the Locations button in the Debug pane and select TestLocations:

Navigate to the map. You should now see your simulated location move over the map, based on the waypoints you have defined in the TestLocations.gpx file. Even more, if you cross a geofence, it will fire a geofence event, and displays an alert:

Also, if you dismiss the app to the background, you will receive a notification:

You may find the simulator acts quite inaccurate at times when testing geofence events. Build and deploy the app on a physical device and enjoy a greater accuracy!

The tutorial ends here. However, you could enhance the app even further.

For instance, you now only receive a notification when crossing a geofence. You could simply create a second database table which stores records for the geofence events with timestamps and user details, and instead of displaying an alert or notification, add a record in that table. Imagine being a truck driver crossing multiple geofences around warehouses. The logistics department would then be notified which driver is in the vicinity of which warehouse.

You could also use Offline OData for storing geofence data, which may give you different kinds of possibilities.

Or use the event to trigger a separate REST service on SAP Cloud Platform which sends a signal to an IoT device, for instance a connected gate or garage door… The geofencing possibilities are endless!