Blog de Amazon Web Services (AWS)

Construye aplicaciones móviles de rastreo multiplataforma con AWS Amplify y Amazon Location Service

Las aplicaciones de rastreo basadas en localización se han vuelto muy populares en los últimos años debido al auge de compañías que ofrecen viajes compartidos y juegos de realidad aumentada al estilo “atraparlos a todos”. Sin embargo, no son las únicas. El rastreo o seguimiento de personal en sectores como la vigilancia, emergencias o la extinción de incendios ha sido una parte integral de la inteligencia requerida para la gestión y ejecución de la seguridad pública durante mucho tiempo.

Desafortunadamente, las organizaciones que han apostado por crear un valor añadido basado en la ubicación para el rastreo de personal o flotas se han visto limitadas por el elevado coste de las soluciones disponibles hasta la fecha. El origen de este coste se debe principalmente al desarrollo, mantenimiento y explotación de este tipo de soluciones, las cuales requieren de un considerable grado de experiencia. AWS ha identificado esta necesidad entre sus clientes y pone ahora a su alcance una forma más simple y económica para crear aplicaciones basadas en el seguimiento de ubicaciones mediante el reciente lanzamiento de nuestro servicio gestionado Amazon Location Service. Con Amazon Location Service puedes construir aplicaciones que incorporan todos los aspectos propios de sistemas basados en localización por un precio muy inferior al que se requería previamente.

Enfoque

Uno de los requisitos más importantes de cualquier aplicación de este tipo es el almacenamiento y seguimiento en tiempo real de la ubicación de los activos que gestiona. Amazon Location Service ofrece una funcionalidad llamada Tracking que permite exactamente eso: llevar a cabo el seguimiento de la ubicación de tus activos de una forma sencilla, escalable y segura a la vez que totalmente integrada con el resto de servicios y funcionalidades del ecosistema de AWS. No importa si estás rastreando diez o miles de activos, Tracking escala de forma automática y transparente según las necesidades de tu aplicación.

En esta entrada de nuestro blog, aprenderás a crear soluciones completas que rastreen la ubicación de dispositivos móviles (nuestros activos) mediante AWS Amplify y Amazon Location Service. Nos apoyaremos en React Native y Expo para desarrollar nuestra aplicación multiplataforma.

Descripción General de la Solución

AWS Amplify proporciona librerías en Kotlin y Swift que permiten su integración con Amazon Location Service para el desarrollo nativo de aplicaciones móviles en Android e iOS respectivamente. Aunque los lenguajes nativos tienen acceso a las APIs de los dispositivos para acceder al GPS y otros sensores, las librerías para JavaScript están implementadas dentro de un framework multiplataforma y por este motivo, no existe una librería nativa de Amplify para los servicios de localización. Dicho eso, en este tutorial exploraremos cómo conectar la ubicación de tus dispositivos con Amazon Location service a través de AWS AppSync mediante el uso de AWS Amplify.

El siguiente diagrama muestra la arquitectura de alto nivel que vamos a construir paso a paso, la cual, nos permitirá enviar la ubicación del dispositivo a Amazon Location Service. Esta funcionalidad puede constituir en si misma una aplicación móvil o bien ser un módulo de una solución más compleja.

Arquitectura

Imagen 1: Arquitectura

El área sombreada en gris en el diagrama de arriba contiene todos los componentes que son gestionados por AWS Amplify. La aplicación móvil se comunicará con el backend mediante HTTPS a través de una API de GraphQL gestionada por AWS AppSync. Esta API expondrá dos endpoints. Uno de ellos permite el registro de dispositivos móviles y para ello establece una relación entre el tracker de Amazon Location Service y el identificador único del dispositivo, la cual es almacenada en una tabla de Amazon DynamoDB. El otro endpoint envía la ubicación del dispositivo a Amazon Location Service y se implementa mediante una función de AWS Lambda.

Guía Paso a Paso

Este tutorial no cubre los aspectos básicos sobre cómo configurar una aplicación móvil con React Native Expo. Si lo necesitas, puedes encontrar las instrucciones para ello en la página oficial de Expo. Además, puedes revisar y descargar el código utilizado en este ejemplo en este repositorio de aws-samples en Github.

Configuración de un Tracker en Amazon Location Service

Paso 1: Para configurar un tracker o rastreador desde la consola de Amazon Location Service, seleccionamos Trackers en la lista de funcionalidades disponibles.

Consola de Amazon Location Service

Imagen 2: Consola de Amazon Location Service

Paso 2: Una vez en la pantalla de Trackers, hacemos clic en el botón de Create tracker para añadir uno nuevo.

Imagen 3: Pantalla de Trackers

Paso 3: Damos un nombre al rastreador y seleccionamos el tipo del plan de precios que deseamos usar. La descripción es opcional.

Imagen 4: Creación de un tracker

Para esta demostración vamos a utilizar datos de muestra, de forma que podamos aprovechar – durante los tres primeros meses de su uso – la capa gratuita de consumo basado en peticiones. Seleccionamos Yes para la pregunta Will you use this tracker with simulated/sample location data only?

Paso 4: Hacemos clic en el botón Create tracker en la parte inferior derecha para finalizar la creación del rastreador al cual nuestra aplicación móvil enviará los datos de su ubicación.

Imagen 5: Propiedades de un tracker

Configuración de AWS Amplify

Para añadir AWS Amplify a nuestra aplicación de React Native Expo, lo único que necesitamos es ejecutar el comando de inicialización en la carpeta raíz, proporcionar un nombre para la aplicación y aceptar los valores por defecto para el resto de configuraciones.

> amplify init
? Enter a name for the project Location
The following configuration will be applied:

Project information
| Name: Location
| Environment: dev
| Default editor: Visual Studio Code
| App type: javascript
| Javascript framework: react-native
| Source Directory Path: src
| Distribution Directory Path: /
| Build Command: npm.cmd run-script build
| Start Command: npm.cmd run-script start

? Initialize the project with the above configuration? (Y/n) Y

Esta operación añade la carpeta de Amplify a nuestro proyecto – la cual contiene la configuración de los recursos de AWS – así como una carpeta llamada src que contendrá el fichero aws-exports.js. Este archivo contiene todo lo necesario para asociar nuestra aplicación móvil a los recursos de backend que crearemos en la siguiente sección.

Añadiendo AppSync y DynamoDB

A continuación, añadiremos la API de GraphQL con AppSync y la tabla de Amazon DynamoDB. Estos recursos son necesarios para poder registrar un dispositivo, así como realizar consultas.

Recursos gestionados por AWS Amplify

Imagen 6: Recursos gestionados por AWS Amplify

Para añadir una API de GraphQL cuya base de datos es DynamoDB, necesitamos ejecutar el comando amplify add api y seleccionar GraphQL entre las opciones disponibles. Necesitaremos proporcionar un nombre para nuestra API y seleccionar API Key como el método autenticación con nuestra API.

Para provisionar una tabla de DynamoDB así como generar el código de las correspondientes consultas y mutaciones de GraphQL necesitamos especificar un esquema. Para nuestro ejemplo elegimos la opción Single object with fields. Seleccionamos Y para indicar que queremos editar el esquema.

> amplify add api
? Please select from one of the below mentioned services: GraphQL
? Provide API name: device
? Choose the default authorization type for the API API key
? Enter a description for the API key: public
? After how many days from now the API key should expire (1-365): 365
? Do you want to configure advanced settings for the GraphQL API No, I am done.
? Do you have an annotated GraphQL schema? No
? Choose a schema template: Single object with fields (e.g., “Todo” with ID, name, description)
? Do you want to edit the schema now? (y/N) Y

Seguidamente modificaremos el esquema de GraphQL generado por defecto (contiene un modelo denominado Todo). Reemplazamos el esquema autogenerado con el siguiente que permite configurar un modelo Device así como las propiedades necesarias para capturar la información del dispositivo.

type Device @model {
   id: ID!
   name: String!
   trackerName: String!
}

El campo name contendrá un nombre amigable y fácil de usar para el dispositivo que además será asociado con su identificador único. En trackerName almacenaremos el nombre del tracker o rastreador que creamos anteriormente.

Una vez hemos hecho estas modificaciones, guardamos los cambios y ejecutamos amplify push en la línea de comandos de forma que el CLI de Amplify despliegue los recursos en nuestra cuenta de AWS. Aceptamos los valores por defecto para las diferentes configuraciones, en especial la que hace referencia a generar el código para la API de GraphQL.

? Do you want to generate code for your newly created GraphQL API (Y/n) Y
? Choose the code generation language target javascript
? Enter the file name pattern of graphql queries, mutations and subscriptions src\graphql\**\*.js
? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions Yes
? Enter maximum statement depth [increase from default if your schema is deeply nested] 2

Conectando AppSync al Tracker

Ahora que ya tenemos la API y la base de datos necesarias para registrar un dispositivo en nuestro sistema, el último paso antes de poder integrarlas con nuestra aplicación móvil es configurar e implementar una función de AWS Lambda que se encargue de recibir la información de la API y actualizar el rastreador con el identificador del dispositivo, así como los datos relativos a su ubicación.

Tres son los pasos que necesitamos llevar a cabo para ello:

  • Crear una función de AWS Lambda
  • Dar permiso a la función para poder interactuar con el tracker de Amazon Location Service
  • Actualizar la API de GraphQL para que haga uso de la función

Crea una nueva función Lambda

Para crear una función de AWS Lambda, ejecutamos la siguiente operación en la línea de comandos: amplify add function.

> amplify add function
? Select which capability you want to add: Lambda function (serverless function)
? Provide an AWS Lambda function name: updateLocation
? Choose the runtime that you want to use: NodeJS
? Choose the function template that you want to use: Hello World
? Do you want to configure advanced settings? No
? Do you want to edit the local lambda function now? (Y/n) Y

Para este ejemplo vamos a usar NodeJS como runtime de forma que podemos seguir utilizando JavaScript para nuestra función.

Antes de modificar el código fuente, necesitamos cambiar el directorio que estamos usando desde la línea de comandos de la raíz de nuestra aplicación móvil a la raíz donde definimos nuestra función de AWS Lambda. En el caso del ejemplo anterior hemos utilizado updateLocation como nombre de la función, por lo que nos moveremos al directorio ./amplify/backend/function/updateLocation/src.

Una vez en esta carpeta, ejecutamos el siguiente comando:

npm install aws-sdk

Mediante el cual descargaremos el AWS SDK para JavaScript a la carpeta node_modules de la función. Una vez descargada, debemos cambiar la línea de comandos de vuelta a la carpeta raíz de nuestra aplicación.

A continuación, abrimos el fichero index.js de la función de AWS Lambda y lo modificamos para:

Importar el AWS SDK

Obtener el identificador del dispositivo, el nombre del rastreador y los datos de la ubicación del evento de entrada de la función y enviarlos al tracker de Amazon Location Service que creamos previamente

exports.handler = async (event) => {
    
    var location = new AWS.Location();
    console.log("update loc");
    
    var params = {
        TrackerName: event.arguments.trackerName, /* required */
        Updates: [ /* required */
          {
            DeviceId: `${event.arguments.deviceId}`, /* required */
            Position: [ /* required */
              parseFloat(event.arguments.Longitude),
              parseFloat(event.arguments.Latitude)
              /* more items */
            ],
            SampleTime: new Date || 123456789 /* required */
          },
          /* more items */
        ]
      };
    
      try{
        const data = await location.batchUpdateDevicePosition(params).promise(); 
        console.log(data);           // successful response
        console.log("update loc: success");
      } catch (err){
        console.log("update loc: error");
        console.log(err);
      }

    return params
};

En el código de arriba, hemos utilizado la operación batchUpdateDevicePosition perteneciente a la librería de localización para actualizar la información asociada a un único dispositivo. Sin embargo, esta función se puede usar para actualizar los datos de hasta un máximo de diez dispositivos. De esta forma, podemos gestionar varios dispositivos en una sola llamada.

Da permiso a la función Lambda al Tracker

Para que nuestra función lambda pueda comunicarse con Amazon Location Service y en concreto enviar los datos de la ubicación de los dispositivos al tracker – como acabamos de definir en el apartado anterior -, debemos proporcionarle permisos para ello a través de la política de ejecución de la función.

Para ello, abrimos el fichero con la plantilla de CloudFormation que define la función de AWS Lamda, situado en la carpeta . /amplify/backend/function/updateLocation y con nombre updateLocation-cloudformation-template.json. Añadimos la siguiente política:

"PolicyDocument": {
    "Version": "2012-10-17",
    "Statement": [
    {
        "Effect": "Allow",
        "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents",
        "geo:BatchUpdateDevicePosition"
        ],
        "Resource": [
        {
            "Fn::Sub": [
            "arn:aws:geo:*:${account}:tracker/*",
            {
                "account": {"Ref": "AWS::AccountId"}
            }
            ]
        },
        {
            "Fn::Sub": [
            "arn:aws:logs:${region}:${account}:log-group:/aws/lambda/${lambda}:log-stream:*",
            {
                "region": {"Ref": "AWS::Region"},
                "account": {"Ref": "AWS::AccountId"},
                "lambda": {"Ref": "LambdaFunction"}
            }
            ]
        }
        ]
    }
    ]
}

Acabamos de definir una política de ejecución que permite a nuestra función ejecutar la operación geo:BatchUpdateDevicePosition para cualquier rastreador de nuestra cuenta.

Actualizar la API de GraphQL para realizar consultas usando AWS Lambda

Ahora pasamos a modificar el fichero schema.graphql de GraphQL, el cual se encuentra en la carpeta ./amplify/backend/api/device. Incluimos el siguiente código al final:

type Mutation {
   updateLocation(trackerName: String, deviceId: String, Latitude: String, Longitude: String): String @function(name: "updateLocation-${env}")
}

En el código de arriba, definimos una función de tipo mutación con diferentes parámetros de entrada y la asociamos con nuestra función Lambda mediante @function. De esta forma, cada vez que la mutación sea invocada, los parámetros de entrada son utilizados para realizar la llamada a la función mediante la API de GraphQL en AppSync.

Ejecutamos el comando amplify push y confirmamos que queremos actualizar el código cuando seamos preguntados por ello. Esta acción despliega la función de AWS Lambda y actualiza la API de GraphQL con lo que ya hemos completado la configuración de los recursos de AWS mediante Amplify.

Llegados a este punto tenemos todos los recursos necesarios para poder enviar los datos de la ubicación al rastreador o tracker desde la aplicación móvil.

Tracker de la aplicación móvil

El último paso que queda pendiente es importar y configurar los recursos de AWS Amplify en la aplicación móvil.

A la hora importar y configurar Amplify, así como llevar a cabo la configuración de las funciones necesarias para que la aplicación móvil pueda registrar y enviar datos al rastreador, seguiremos un patrón de diseño habitual en desarrollo denominado repository pattern.

Repository pattern

Imagen 7: Repository pattern

Repository pattern define una simple estrategia según la cual abstraemos la lógica de cualquier aplicación de los detalles relacionados con el acceso a los datos de la misma. De esta forma, una aplicación puede cambiar los orígenes de datos con los que trabaja sin necesidad de modificar la lógica que interacciona con ellos.

Sabiendo esto, crearemos un sencillo repositorio en un fichero llamado device.js en la carpeta src que AWS Amplify creó cuando lo inicializamos. Este fichero definirá una clase Device que contendrá todas las funciones necesarias para que la aplicación móvil registre un dispositivo en el sistema, almacene el identificador del dispositivo en el mismo y envíe su localización al tracker.

Para registrar un dispositivo en el sistema, echa un vistazo al código presente en screens/setup.js para ver cómo la pantalla de configuración llama a la función register en la clase Device.

A la hora de enviar la localización del dispositivo móvil al tracker, todo lo que necesitamos hacer es importar la librería de expo-location – la cual proporciona acceso a la API de localización del dispositivo – e importar la clase Device para poder realizar consultas al backend de AWS Amplify.

Los siguientes fragmentos de código son parte de la lógica de la pantalla principal aunque se han dividido para explicar cada paso por separado.

En la función useEffect comenzamos inicializando el repositorio de Device, el cual realizará una consulta a la API de GraphQL para obtener el rastreador y el nombre del dispositivo de acuerdo al identificador que ha sido guardado en su almacenamiento local.

useEffect(() => {

    (async () => {

      deviceSvc.init()
      .then((resp)=> {
          if(resp !== null){
              setTracker(resp.trackerName);
              setDeviceName(resp.name);
              setDeviceId(resp.id);
          }
      });

...

Para poder usar la API de Location del dispositivo, primero necesitamos obtener permiso para ello por parte del usuario. Esto es gestionado por las librerías de localización mediante la función requestForegroundPermissionsAsync.

...

let { status } = await Location.requestForegroundPermissionsAsync();
  if (status !== 'granted') {
    setErrorMsg('Permission to access location was denied');
    return;
  } else {

...

Una vez el usuario ha autorizado que la aplicación pueda acceder a la API de localización, obtenemos la ubicación actual del dispositivo mediante la función getCurrentPositionAsync. Los datos obtenidos por esta función son entonces utilizados por la función setLocation del repositorio del dispositivo (Device) para enviar la ubicación al tracker de Amazon Location Service a través de la API de GraphQL gestionada por AppSync.

...
        
let isLocationServicesEnabled = await Location.hasServicesEnabledAsync();
let locationProviderStatus = await Location.getProviderStatusAsync();
console.log(`loc status: ${JSON.stringify(locationProviderStatus)}`);

if(isLocationServicesEnabled){
      //Get loc
      let loc = await Location.getCurrentPositionAsync({accuracy: Location.Accuracy.Highest});
      if(loc.coords !== null){
        console.log(`loc: ${JSON.stringify(loc)}`);
        setLocation(loc);
        deviceSvc.setLocation(loc.coords.latitude, loc.coords.longitude);
      }

...

Finalmente, para poder rastrear cuando la ubicación del dispositivo cambia utilizamos la función watchPositionAsync que comprueba si esta ha cambiado en más de 1 metro cada 30 segundos. En caso afirmativo, enviamos la nueva localización mediante la función setLocation de la clase Device.

...

          //Watch loc
          await Location.watchPositionAsync({
              enableHighAccuracy: true,
              distanceInterval: 1,
              timeInterval: 30000}, newLoc => {
                console.log(`new loc: ${JSON.stringify(newLoc)}`);
                setLocation(newLoc);
                deviceSvc.setLocation(newLoc.coords.latitude, newLoc.coords.longitude);
              });
        }
      }

    })();

}, []);

En este tipo de solución, la aplicación debe estar activa en primer plano para poder acceder a la API de localización y así monitorizar los cambios que se producen en la ubicación. En esta aplicación de ejemplo, comprobamos si hay cambios cada 30 segundos y actualizamos la ubicación si está ha cambiado más de 1 metro.

Elimina los recursos utilizados

El tracker no tiene ningún coste asociado mientras que no lo uses, aunque puedes eliminarlo desde la consola de Amazon Location Service.

Conclusión

En este guía paso a paso hemos creado un tracker o rastreador en Amazon Location Service y hemos aprendido cómo usar AWS Amplify con JavaScript para desplegar una solución que envía datos de localización a dicho tracker mediante una API de GraphQL gestionada por AWS AppSync.


Este blogpost es una traducción por Jesús Bernal (AWS Startup Solutions Architect) del original en Inglés.

Aaron Sempf

Aaron es un Senior Partner Solutions Architect en Amazon Web Services, en el equipo Global Systems Integrators (GSI). Cuando no está trabajando con partners GSI de AWS, podremos encontrarlo codeando prototipos para robots autonomos, dispositivos IoT, y soluciones distribuidas.

Florian Seidel

Florian es un Solutions Architect en Amazon Web Services, del equipo EMEA Automotive. Ha trabajado en servicios basados en ubicación in la industria automotriz en los últimos 3 años y tiene muchos años de experiencia en ingeniería de software y arquitectura de software.