Front-End Web & Mobile

Create an iOS tracker application with Amazon Location Service and AWS Amplify

This article was written by Fernando Rocha Silva and Panna Shetty

Emergency management teams are constantly tackling one crisis after another, whether it is managing the COVID pandemic or battling raging wild fires across California. These frontline workers venture into dangerous situations to rescue those in need, potentially risking their own lives.

Firefighters go above and beyond the call of duty for the safety of others. To keep themselves safe, they follow policies and standard operating procedures that outline how to react quickly in emergency situations to avoid unnecessary risks. In the case of natural disasters such as forest fires, the scale of the event is too large to easily track the safety of each crew member by line of sight and a different approach is required. In these scenarios a muster point is established as a designated safety zone, or a geofence. This geofence cannot always be predetermined and may need to change in response to evolving conditions, thus emergency managers have to keep track of members in the field when location changes occur. One of the ways to improve this process is automating member tracking and response activity, so that emergency managers can quickly account for all members and then focus on recovery operations.

Emergency teams must be able to have their current location tracked so emergency managers who handle vital field communications can determine if they are in a compromised position and quickly establish a geofence. The emergency managers must be able to visualize in real-time the current status of members in the field and provide them with the location of the defined geofence. Each of the members must be able to see the current geofence on their device so they can quickly move to that location. Once the member is in the geofenced area, the emergency manager is notified.

On June 1st 2021, AWS made Amazon Location Service generally available to customers . With Amazon Location you can build applications that provide maps and points of interest, convert street addresses into geographic coordinates, calculate routes, track resources, and trigger actions based on location. It enables you to access location data simply with developer tools, and also move your applications to production faster with included monitoring and management capabilities.

In this blog post, we will show you how to build a full-stack application that leverages Amazon Location Service alongside other AWS solutions to create a serverless architecture that is capable of tracking the user’s current location and identify if they are in a safe area or not.

Overview of solution

In the following architecture, we have two types of mobile clients securely connecting to a provisioned backend leveraging the Amplify libraries. In this particular example, we use the Amplify iOS library.

The iOS clients rely on Amazon Cognito for authentication and authorization, provisioned using the Amplify CLI authentication category. The Amplify DataStore is used to persist the data locally on the client side, providing capabilities that allow automatic synchronization of data between devices and the cloud. The DataStore relies on a GraphQL API layer powered by AWS AppSync connected to Amazon DynamoDB for data storage.

The AWS SDK is used to collect the geofences created in the Amazon Location Service using the Geofences API. The Tracker API is used to collect the team member (employee) current location and to identify ENTER and EXIT events that occur against the Geofence Collection associated to the tracker. Once geofence events occur, they are sent to Amazon EventBridge where a rule is triggered and the event is pushed to an AWS Lambda function. The event is then parsed and sent to AppSync, persisting the data on DynamoDB. The patrol client application detects the change in DynamoDB and updates the user current status.

 

The employee mobile application connects to Amazon Location’s Tracker via the Amazon Location SDK, once the latitude and longitude provided by the tracker crosses a geofence, an event is sent to EventBridge and a Lambda Function is triggered. This Lambda function updates DynamoDB via AppSync with the user’s information, marking them as safe or not. Another app, targeting the patrol officers, track the users and their safety status. Both mobile applications rely on Cognito for authentication and authorization. AWS Amplify is used by the mobile app to connect to Cognito, via Amplify Auth, and AppSync, via Amplify API and Amplify DataStore.

Data privacy

With Amazon Location, you retain control of your organization’s data as the service anonymizes all queries sent to data providers by removing customer metadata and account information associated to the requests.

Additionally, sensitive tracking and geofencing location information, such as facility, asset, and personnel locations, never leave your AWS account. This helps to protect sensitive information from third parties, secure user privacy, and reduce your application’s security risks.

Walkthrough

The solution consists of three equally important pieces: Amazon Location Service tracker and geofence collection, the mobile clients alongside their supporting backends, and Amazon EventBridge.

You can deploy the solution by following the deployment tutorial on GitHub.

Tracker and Geofence Collections

Amazon Location adds capabilities such as maps, points of interest, geocoding, routing, geofences, and tracking to applications. In this example, we use the geofencing API to create a geofence collection, which represents the muster points that are collected at the user’s application, pointing to safe locations where they can move to; and tracking APIs, used at the application layer to detect the device’s current location as it moves.

As the device location is updated, Amazon Location detects if the current location is within a geofence from the geofence collection to generate events that are sent to Amazon EventBridge as ENTER or EXIT. Below, you see an example of an ENTER event generated by the tracker:

{
  "version": "0",
  "id": "aa11aa22-33a-4a4a-aaa5-example",
  "detail-type": "Location Geofence Event",
  "source": "aws.geo",
  "account": "636103698109",
  "time": "2020-11-10T23:43:37Z",
  "region": "eu-west-2",
  "resources": [
    "<GeofenceCollection-ARN>",
    "<TRACKER-ARN>"
  ],
  "detail": {
    "EventType": "ENTER",
    "GeofenceId": "<GEOFENCE-ID>",
    "DeviceId": "<DEVICE-ID>",
    "SampleTime": "2020-11-10T23:43:37.531Z",
    "Position": [
      -123.12390073297821,
      49.23433613216247
    ]
  }
}

iOS application, AWS Amplify, and the AWS SDK

The client applications are built in iOS (SwiftUI) and benefit from AWS Amplify’s Auth and DataStore categories to authenticate and authorize users with Cognito in addition to synchronize the user data to the cloud using AppSync and DynamoDB. Amplify is also used to create the AWS Lambda function that is triggered by EventBridge when a geofencing event occurs.

The image displays a mobile app where the user is represented as a center blue point, geofences are shows as red polygons in the map. The user is inside the geofence.

Collecting Geofences

We use the listGeofences API provided by the AWS SDK to collect the boundaries associated to the geofences created on Location Service.

@Published var overlays = [MKPolygon]()
    
func listGeofences() {
    let request = AWSLocationListGeofencesRequest()!
    request.collectionName = “<GEOFENCE-COLLECTION-NAME>”

    let result = AWSLocation.default().listGeofences(request)
    result.continueWith { (task) -> Any? in
        if let error = task.error {
            print("error \(error)")
        } else if let taskResult = task.result {
            var overlays = [MKPolygon]()
            
            for entry in taskResult.entries! {
                let polygonEntry = entry.geometry?.polygon![0]
                
                var polygons = [CLLocationCoordinate2D]()
                for polygon in polygonEntry! {
                    let lon = polygon[0] as! Double
                    let lat = polygon[1] as! Double
                    polygons.append(CLLocationCoordinate2D(latitude: lat, longitude: lon))
                }
                
                let polygon = MKPolygon(coordinates: polygons, count: polygons.count)
                overlays.append(polygon)
            }
            DispatchQueue.main.async {
                self.overlays = overlays
            }
        }
        return nil
    }
}

The overlay returned by listGeofences is used to draw the muster points on the client app.

Setting up the Tracker

To track the device’s position, let’s use the AWSLocationTracker API provided by the AWS SDK.

let locationTracker = AWSLocationTracker(
    trackerName: <YOUR-TRACKER-NAME>, 
    region: AWSRegionType.<YOUR-REGION>,
    credentialsProvider: AWSMobileClient.default())

When the user authorizes the mobile application to detect the device’s location, we kick off the location tracker using the user id as a device id. We also set a listener to intercept the response from Amazon Location after the position is transmitted.

func locationManagerDidChangeAuthorization(manager: CLLocationManager) {
        switch manager.authorizationStatus {
        case .authorizedWhenInUse:
            print("Received authorization of user location, requesting for location")
            let result = locationTracker.startTracking(
                delegate: self,
                options: TrackerOptions(
                    customDeviceId: Amplify.Auth.getCurrentUser()?.userId,
                    retrieveLocationFrequency: TimeInterval(5),
                    emitLocationFrequency: TimeInterval(20)),
                listener: onTrackingEvent)
            switch result {
            case .success:
                print("Tracking started successfully")
            case .failure(let trackingError):
                switch trackingError.errorType {
                case .invalidTrackerName, .trackerAlreadyStarted, .unauthorized:
                    print("onFailedToStart \(trackingError)")
                case .serviceError(let serviceError):
                    print("onFailedToStart serviceError: \(serviceError)")
                }
            }
        default:
            print("Failed to authorize")
        }
    }

When the user starts moving, we can intercept the new latitude and longitude:

func locationManager(
    manager: CLLocationManager, 
    didUpdateLocations locations: [CLLocation]) 
{
    print("Got locations: \(locations)")
    locationTracker.interceptLocationsRetrieved(locations)
}

Using Amazon EventBridge to trigger changes to the device’s safety status

The last piece of our solution is deployed by setting up an Amazon EventBridge rule to detect ENTER and EXIT geofencing events generated by the user’s device. This rule triggers an AWS Lambda function that leverages AppSync to update the DynamoDB table that contains the user’s safety attribute.

Here is what the Lambda function looks like:

/* Amplify Params - DO NOT EDIT
  API_MUSTERPOINTLOCATIONAPI_GRAPHQLAPIENDPOINTOUTPUT
  API_MUSTERPOINTLOCATIONAPI_GRAPHQLAPIIDOUTPUT
  ENV
  REGION
Amplify Params - DO NOT EDIT */

const https = require('https')
const AWS = require('aws-sdk')
const urlParse = require('url').URL
const region = process.env.REGION
const appsyncUrl = process.env.API_MUSTERPOINTLOCATIONAPI_GRAPHQLAPIENDPOINTOUTPUT

const request = (queryDetails, appsyncUrl, apiKey) => {
  const req = new AWS.HttpRequest(appsyncUrl, region)
  const endpoint = new urlParse(appsyncUrl).hostname.toString()

  req.method = 'POST'
  req.path = '/graphql'
  req.headers.host = endpoint
  req.headers['Content-Type'] = 'application/json'
  req.body = JSON.stringify(queryDetails)

  if (apiKey) {
    req.headers['x-api-key'] = apiKey
  } else {
    const signer = new AWS.Signers.V4(req, 'appsync', true)
    signer.addAuthorization(AWS.config.credentials, AWS.util.date.getDate())
  }

  return new Promise((resolve, reject) => {
    const httpRequest = https.request({ ...req, host: endpoint }, (result) => {
      result.on('data', (data) => {
        resolve(JSON.parse(data.toString()))
      })
    })

    httpRequest.write(req.body)
    httpRequest.end()
  })
}

const updateSafetyMutation = /* GraphQL */ `
  mutation updateUser($input: UpdateUserInput!) {
      updateUser(input: $input){
        id
        isSafe
        username
        _lastChangedAt
        _version
        _deleted
      }
    }
`
const queryUser = /* GraphQL */ `
  query getUser($id: ID!) {
        getUser (id: $id) {
            id
            username
            isSafe
            _version
         }
    }
`

exports.handler = async (event) => {
  const userId = event?.detail?.DeviceId
  console.log('new geofence event:', event)

  var queryResult = await request(
      {
        query: queryUser,
        variables: { id: userId },
      },
      appsyncUrl
    )
  console.log('query result', queryResult)


  const user = queryResult?.data?.getUser
  if (!user) {
    return {
      statusCode: 404,
      body: `User with id ${userId} not found in the database.`,
    };
  }

  const version = user?._version
  var result = await request(
    {
      query: updateSafetyMutation,
      variables: {
        input: {
            id: userId,
            isSafe: event?.detail?.EventType === "ENTER",
            _version: version
        },
      },
    },
    appsyncUrl
  )
  console.log('appsync result', result)
}

The Patrol Application shows a list of users, represented in red if they are unsafe and green, if they are safe.

Conclusion

In this blog post, you learned how to create a serverless solution using Amazon Location Service and AWS Amplify to track the safety status of users. You can follow the deployment tutorial on GitHub or start building your own solutions using Amazon Location by reading the developer guide, which provides a conceptual overview and practical examples to help you understand the features provided by Amazon Location and how to use them. You can also follow the sample app guides on our GitHub repository or by visiting the Amplify guides for JavaScript, iOS, and Android.

 

Author bio

Fernando Rocha headshot Fernando Rocha is a Solutions Architect who specializes in mobile technologies. He currently helps customers build their solutions on AWS Amplify and Amazon Location Service.
Panna Shetty headshot Panna Shetty is a Sr. Technical Account Manager with Amazon Web Services (AWS), working with public sector customers. She has a background in software development and is currently pursuing her Master’s at Penn Engineering.