Krypton Solid

Poner en práctica el back-end móvil como servicio (Parte 2)

Poner en práctica el back-end móvil como servicio (Parte 2)

El back-end móvil como servicio (MBaaS) tiene como objetivo brindar a los desarrolladores de aplicaciones la capacidad de crear aplicaciones nativas y web multiplataforma completamente nuevas y sin problemas. En la primera parte de esta serie, analicé una demostración de la aplicación de mensajería impulsada por la aplicación Kinvey. Exploramos cómo aprovechar la gestión de usuarios, el almacenamiento de archivos y el almacenamiento de datos.

Para completar la demostración, necesitamos aprovechar dos piezas clave de la funcionalidad de Kinvey: los permisos proporcionados por el almacén de datos y las notificaciones push, que se habilitan a través de la funcionalidad de lógica empresarial.

Demostración de la aplicación de mensajería WaterCooler. (Ver versión grande)

Configuración

Tanto el primer artículo de esta serie como mi artículo inicial que explica el concepto de MBaaS son requisitos previos para este artículo. Además, este artículo adopta un enfoque de «aplicación real» para comprender MBaaS. Dicho esto, sería útil tener algún conocimiento de iOS y Swift. Sin embargo, he proporcionado el código fuente de la aplicación, para que pueda aprender y analizar a su propio ritmo.

Permisos

Además del soporte de almacenamiento de datos y archivos proporcionado por Kinvey, también existe un modelo de seguridad robusto que se puede aplicar a todos estos datos. Toda la configuración de permisos en archivos u objetos del almacén de datos ocurre con el KCSMetadata clase. Esta instancia puede configurarse con clases de modelos de datos personalizados o adjuntarse como parámetros a un archivo al cargarlo en el almacén de archivos.

Otras lecturas en SmashingMag

Permisos de archivos

Como discutimos anteriormente, la funcionalidad de administración de archivos es una pieza poderosa de cualquier herramienta MBaaS. Sin embargo, aclarar quién puede acceder a este contenido es igualmente importante. En nuestro ejemplo, todos los usuarios deberían poder ver las imágenes de perfil que se cargan. Si quisiéramos agregar el uso compartido de imágenes a la parte de mensajería uno a uno de la aplicación, entonces querríamos limitar quién puede acceder a la imagen. Como queremos permitir que cualquiera vea la foto de perfil, podemos aprovechar la setGloballyReadable método de KCSMetadata para asegurarse de que, si bien solo el creador puede editar este archivo, cualquier usuario puede leerlo.

// Extracted from SignupViewController.swift (http://tuck.cc/1vsaWcr)

// Initialize with the default initializer
let metadata = KCSMetadata();

// Ensure that all users can access this file
metadata.setGloballyReadable(true);

// This is added to a params object that is passed to the upload data method
var fileParams = [
  KCSFileMimeType : "image/jpeg",
  KCSFileACL : metadata
];

// By passing in this fileParams object, the file will be readable by all
KCSFileStore.uploadData(photoData, options: fileParams, completionBlock: { (file:KCSFile!, error:NSError!) -> Void in
    println("File Upload Completed")
}, progressBlock: nil);

Permisos de almacenamiento de datos

Para que esto sea lo más simple posible para integrarlo en la aplicación, he definido algunas Extensiones rápidas a las clases proporcionadas en el SDK de Kinvey para iOS. Una de estas extensiones es un inicializador de conveniencia para el KCSMetadata clase para configurar permisos para el Message y MessageThread clases:

/*
  This method is a convenience initializer that sets the permissions
  on a KCSMetadata instance for the user IDs passed in (and, optionally,
  for the current user as well). All persistable data in the Kinvey data
  store can be configured using a KCSMetadata instance.
 */
extension KCSMetadata {

  convenience init(userIds:[String], includeActiveUser:Bool) {
    self.init()
    var ids = userIds
    setGloballyReadable(false)
    setGloballyWritable(false)
    if(includeActiveUser) {
      ids.append(KCSUser.activeUser().userId)
    }
    for userId in ids {
      readers.addObject(userId)
      writers.addObject(userId)
    }
  }
}

Con esto en su lugar, ahora podemos inicializar la instancia de metadatos con la configuración de seguridad correcta cuando creamos una instancia del Message clase. Ahora, cuando se guarde, será accesible solo para el remitente y el destinatario.


class Message : NSObject, KCSPersistable {

  var entityId:String = ""
  var messageText:String!
  var senderId:String!
  var threadId:String!
  var userEntryTime:NSDate!
  var metadata:KCSMetadata! = nil

  init(messageText:String, recipientId:String) {
    senderId = KCSUser.activeUser().userId
    messageText = messageText
    entityId = NSUUID().UUIDString
    // This uses our convenience initializer to allow for the limited
    // permissions of only the sender and recipient
    metadata = KCSMetadata(userIds: [recipientId], includeActiveUser:true)
  }

  // You have to let Kinvey know which property corresponds to the meta-data
  // instance. Do this by assigning the property the KCSEntityKeyMetadata
  // value in the hostToKinveyPropertyMapping method.
  override func hostToKinveyPropertyMapping() -> [NSObject : AnyObject]! {
    return [
      "entityId" : KCSEntityKeyId,
      "messageText" : "message",
      "senderId" : "senderId",
      "threadId" : "threadId",
      "metadata" : KCSEntityKeyMetadata
    ]
  }
}

Lógica empresarial y notificaciones push

Una solución MBaaS que proporcione toda la funcionalidad detallada hasta ahora sería adecuada en un conjunto limitado de casos de uso. Sin embargo, la capacidad de centralizar la lógica empresarial cuando está vinculada al ciclo de vida de un objeto de datos abre muchas puertas para crear aplicaciones multiplataforma eficaces. En la aplicación de demostración WaterCooler, esto es relevante de dos formas distintas:

  1. Cuando una Message instancia se guarda en el almacén de datos, necesitamos determinar a quién se está enviando. Una vez que hayamos determinado el destinatario del mensaje, debemos enviarle una notificación automática.
  2. Cuando una Message instancia se guarda en el almacén de datos, también necesitamos recuperar el MessageThread instancia y guarde el latestMessage propiedad. Esto nos libera de tener que realizar una llamada de recuperación adicional al completar la vista del hilo del mensaje.

Si bien ambos podrían lograrse a través de la aplicación iOS, eso estaría poniendo la lógica en el lugar incorrecto. Considere si también teníamos una aplicación web y de Android para la aplicación WaterCooler. Querríamos que se produjera la misma funcionalidad al guardar un mensaje en ambas plataformas. En este caso, cualquier lógica de negocios que abarque múltiples plataformas debe residir en el servidor utilizando la capacidad de lógica de negocios de Kinvey.

Configuración de notificaciones push

Para que las notificaciones push funcionen correctamente en iOS, necesitamos configurar Kinvey para que funcione con nuestros certificados de notificaciones push para la aplicación. Este paso implica dos pasos distintos:

  1. Configure la aplicación con certificados de notificación push para desarrollo y producción en el Centro para desarrolladores de iOS.
  2. Exporte y cargue el certificado requerido a Kinvey.

El proceso completo de completar estos pasos está más allá del alcance de este artículo, pero Kinvey tiene un «Empujar”En la documentación para ayudarlo en el proceso de integración general.

Ganchos de la colección Kinvey

Para lograr los dos objetivos enumerados anteriormente, aprovechamos una pieza particular de funcionalidad en Kinvey: los ganchos de colección. Con los ganchos de colección, podemos definir la lógica empresarial del lado del servidor en JavaScript que se ejecuta en puntos particulares. Los puntos predefinidos para una colección se enumeran a continuación:

  • onPreSave Esta lógica se ejecuta antes de que se guarde una instancia en el almacén de datos. Cualquier error que ocurra en esta lógica evitaría la save acción de ser ejecutada.
  • onPreFetch Esta lógica se ejecuta antes de cualquier llamada de recuperación en el entity tipo. Cualquier error evitaría fetch acción de ser ejecutada.
  • onPreDelete Esta lógica se ejecuta antes de cualquier entity instancia que se elimina del almacén de datos. Cualquier error evitaría que la instancia se elimine del almacén de datos.
  • onPostSave Esta lógica se ejecutará después de que se guarde una instancia en el almacén de datos. Cualquier error aquí no evitaría que el objeto se almacene en el almacén de datos, pero provocaría la devolución de un error en la solicitud.
  • onPostFetch Esta lógica se ejecutaría después de un fetch El comando ha sido ejecutado por el almacén de datos. Cualquier error aquí no evitaría que el objeto se recupere del almacén de datos, pero provocaría la devolución de un error en la solicitud.
  • onPostDelete La lógica se ejecutará después de que se elimine una instancia del almacén de datos. Cualquier error aquí no evitaría que se elimine el objeto, pero causaría que se devuelva un error para la solicitud.

Las notificaciones push son una parte importante de cualquier aplicación de mensajería. Idealmente, querríamos que se enviara uno cada vez que un remitente guarda un Message objetar al almacén de datos. Kinvey proporciona esta capacidad a través de la funcionalidad de enganches de su lógica empresarial. En resumen, puede agregar lógica JavaScript personalizada que se ejecuta en ciertos puntos del ciclo de vida de un objeto de datos.

Módulos de lógica empresarial

Kinvey proporciona un conjunto de módulos que le permiten acceder a la funcionalidad principal de Kinvey dentro del código de su lógica empresarial. Hay varios módulos disponibles, pero he aprovechado los siguientes módulos con el enlace de lógica empresarial:

  • logger Proporciona una forma sencilla de registrar información desde la lógica de su negocio a la consola de Kinvey.
  • collectionAccess Proporciona una forma de consultar el almacén de datos de Kinvey desde la lógica de su negocio.
  • push Proporciona acceso a la funcionalidad de notificaciones push para Kinvey con su lógica empresarial
  • async Proporciona un enfoque para tratar con JavaScript asincrónico. Este es el asincrónico módulo que puede haber utilizado en aplicaciones Node.js.

  • Para obtener una referencia completa sobre los módulos de lógica empresarial, consulte la sección «Módulos”En la sección“ Lógica empresarial ”de la documentación de Kinvey.

Estos ganchos se configuran a través del Consola Kinvey. Para agregar un onPostSave gancho para la colección de mensajes, siga estos pasos:

  1. Seleccione la opción «Lógica empresarial» en la navegación en el lado izquierdo de la consola Kinvey.
  2. Seleccione la opción «Crear su primer guión».
  3. En el menú emergente, seleccione la opción «Agregar gancho» para la colección de Mensajes.
  4. Selecciona el onPostSave opción.
  5. Verás lo nuevo onPostSave Enlace de JavaScript dentro del navegador, que puede editar.
Captura de pantalla de la consola Kinvey
La sección de lógica empresarial de la consola de Kinvey. (Ver versión grande)

Código completo para lógica empresarial

Al juntar estas piezas, se obtiene un único método de JavaScript de solo 115 líneas (con comentarios). Este código recibe al destinatario, actualiza el hilo con el último mensaje y luego envía la notificación de inserción al destinatario.

Para habilitar este gancho, copie este JavaScript en el gancho que acaba de crear:

// Extracted from Messages-PostSave.js (http://tuck.cc/1MPrCAc)

function onPostSave(request, response, modules) {

  // Get references to Kinvey's business logic modules
  var push = modules.push;
  var collectionAccess = modules.collectionAccess;
  var logger = modules.logger;
  var async = modules.async;

  // Pull information from the request (the postSave messages request)
  var senderUsername = request.username;
  var messageId = request.entityId;
  var message = request.body;

  // Set up our state variables
  var recipient = {};

  // This method fetches the recipient of the message based on the thread
  // ID. This is needed to send the push notification to the correct user.
  var getRecipient = function(callback) {
    var recipientId = recipientIdToFetch();
    collectionAccess.collection('user').findOne({ "_id": recipientId }, function(error, user) {
      if(error) {
        logger.info("ERROR Fetching Recipient KCSUser");
        callback(error);
      } else {
        recipient = user;
        callback(null, recipient);
      }
    });
  };

  // This method sets the lastMessage on the MessageThread
  var updateLatestMessage = function(callback) {
    var threadId = message.threadId;

    // This is how Kinvey stores relationships to other entities.
    // We simply need to insert this object for the thread at the
    // specified ID and we will have updated the reference.
    var lastMessageRef = {
      _type: "KinveyRef",
      _id: threadId,
      _collection: "Messages"
    };

    // This method finds and updates the thread with the updated
    // reference to the message. It returns the updated thread from
    // the data store.
    collectionAccess.collection('MessageThreads').findAndModify({ "_id": threadId }, { "_id": 1 }, { $set: { "lastMessage" : lastMessageRef } }, { new: true }, function(error, thread) {
      if(error) {
        logger.error("Could Not Fetch Thread from ID: " + threadId);
      } else {
        callback(null, thread);
      }
    });
  };

  // This checks the threadId and, based on the ID of the user who saved the
  // message, determines the ID of the recipient of the message.
  var recipientIdToFetch = function() {
    var threadId = message.threadId;
    var ids = threadId.split(":");
    var recipientId = (ids[0] == message.senderId) ? ids[1] : ids[0];
    return collectionAccess.objectID(recipientId);
  };

  // This method creates both the APS and extras objects to be used in the push
  // notification.
  var pushDataFromMessageAndUser = function(user) {
    return {
      aps: {
        alert: notificationAlertMessage(user),
        badge: 0,
        sound: "notification.wav"
      },
      extras: {
        messageText: message.message,
        creationDate: message._kmd.ect,
        threadId: message.threadId,
        senderId: message.senderId,
        entityId: messageId
      }
    };
  };

  // This method calculates the text of the push notification based on the
  // sender and the text of the message.
  var notificationAlertMessage = function(user) {
    var alertMessageComponents = [];
    var alertMessage = user.first_name + " ";
    alertMessage += user.last_name + ": ";
    alertMessage += message.message;
    return alertMessage;
  };

  // This method is executed after the recipient has been fetched from the
  // user collection.  It proceeds with making the push notification call to
  // the recipient with the correct information.
  var callback = function(error, results) {
    if(error) {
      response.body = {
        error: error.message
      };
      response.complete(400);
    } else {
      var pushData = pushDataFromMessageAndUser(results[1]);
      push.sendPayload(recipient, pushData.aps, pushData.extras);
      response.complete(200);
    }
  };

  // This kicks off the process to fetch the recipient and update the thread.
  // Once that is complete, it calls the callback function to send the
  // push notification.
  async.parallel([updateLatestMessage, getRecipient], callback);
}

Con este código en su lugar (y las notificaciones push configuradas correctamente), la aplicación ahora enviará una notificación push al destinatario y también actualizará la lastMessage propiedad en el hilo de mensajes correspondiente. En la imagen a continuación, puede ver una notificación de inserción de muestra que se envió al destinatario de un mensaje:

Captura de pantalla de la notificación push en la aplicación WaterCooler
Una notificación de inserción en la aplicación WaterCooler. (Ver versión grande)

Poniendo las piezas juntas

Si bien no analizamos cada paso de cómo se creó la aplicación, ahora tiene un ejemplo completo de una aplicación de Kinvey que puede usar como punto de partida para su propia aplicación. Con suerte, este artículo le ha proporcionado una buena comprensión del código necesario, pero también le ha ayudado a pensar en cómo resolver problemas utilizando una solución MBaaS.

Capturas de pantalla de la aplicación WaterCooler
Demostración de la aplicación de mensajería WaterCooler. (Ver versión grande)

Si desea seguir aprendiendo, puede agregar muchas funciones a esta aplicación:

  • soporte para imágenes en mensajes,
  • notificaciones para cuando un usuario está escribiendo,
  • mensajería grupal.

Todas estas características serían posibles con el back-end de Kinvey.

Recursos adicionales

Aquí hay algunos recursos que se utilizaron en la creación de nuestra aplicación:

  • SlackTextViewController, Slack, GitHub El equipo de Slack ha lanzado un control asombroso para ingresar texto en una aplicación de mensajería.
  • MBProgressHUD, Jonathan George, GitHub Este es un control común para mostrar superposiciones cuando la aplicación está esperando a que finalice una tarea. Es muy personalizable y configurable.
  • Buscar con licencia Creative Commons iconos en Freepik, SimpleIcon, Google y Flaticon.
  • Brandon McQuilkin, GitHub McQuilkin solía proporcionar excelentes Extensiones de imágenes rápidas para enmascarar imágenes. Desde mi trabajo inicial en la aplicación, los ha eliminado de GitHub.

Conclusión

Espero que, a través de los últimos artículos, haya visto formas en que una solución MBaaS puede ayudarlo a crear experiencias móviles atractivas centradas en el negocio y el consumidor. Sin duda, este es un espacio con mucho enfoque en este momento, y espero una rápida madurez y evolución de estos servicios durante el próximo año. La mayoría de los desarrolladores de dispositivos móviles estarían de acuerdo en que esta es una herramienta valiosa para tener en su arsenal.

Los siguientes recursos adicionales de la documentación de Kinvey lo ayudarán a crear aplicaciones iOS:

Deja un comentario