Desarrollo móvil multiplataforma
 

Notificaciones Push en iOS con PhoneGap / Cordova

Notificaciones Push en iOS con PhoneGap / Cordova

En este artículo voy a explicar cómo integrar notificaciones push en un proyecto con Cordova/PhoneGap para iOS.

Requisitos

Las notificaciones push no funcionan en el simulador iOS incorporado en Xcode, y por tanto necesitaremos una cuenta de desarrollador iOS para poder generar los certificados apropiados y poder probar nuestra aplicación en un terminal. Además, al tratarse de una aplicación iOS necesitaremos trabajar con un equipo Mac.

Resultado

Al final de este artículo tendremos una aplicación iOS basada en Cordova/PhoneGap capaz de recibir mensajes push y una pequeña aplicación de servidor realizada en PHP capaz de mandarlos. Ambas aplicaciones están exentas de florituras y serán extremadamente sencillas, con el código estrictamente necesario para realizar su cometido, que es enviar y recibir mensajes push.

Preparativos previos

Antes de empezar, vamos a crear una carpeta en nuestro equipo donde almacenaremos todos los elementos de este proyecto. La carpeta la llamaremos testpush y crearemos 3 directorios: certs, app y server.

push-ios-folder-project

En certs meteremos los certificados de Apple (ya veremos qué son y cómo se crean), en app meteremos el proyecto Cordova/PhoneGap y en server el código PHP de nuestro servidor capaz de enviar push.

Introducción a las notificaciones push en iOS

Con mucha probabilidad ya sabrás lo que son las notificaciones push, que son básicamente mensajes generados desde un servidor y que llegan a nuestro terminal sin necesidad de que el terminal esté consultando constantemente al servidor.

El primero que introdujo este concepto fue Blackberry, pero actualmente está presente en todas las plataformas y cada una aborda el problema de forma ligeramente diferente. La estrategia que sigue Apple es la siguiente:

  1. La aplicación deberá indicar al sistema operativo que desea recibir notificaciones, y el sistema operativo le pedirá permiso al usuario (típica pantalla de “La aplicación X desea enviarle notificaciones”)
  2. Si el usuario ha aceptado, la aplicación se registrará en los servidores de Apple APNS (Apple Push Notification Service) y obtendrá un token, el cual podemos ver como una dirección a la que se enviarán los mensajes y que identifica a la aplicación y al dispositivo sobre el que se ha instalado.
  3. Nuestra aplicación le hará llegar el token al servidor (normalmente nuestro) que realizará las notificaciones push. En terminología Apple a estos servidores los llaman Providers.
  4. Cada vez que el servidor desee comunicarse con ese dispositivo, hará una petición al APNS indicándole el mensaje push y el token asociado del dispositivo al que queremos enviarlo.

Fácil, ¿verdad?

Particularidades a las notificaciones push en iOS

También es conveniente saber ciertas peculiaridades de las notificaciones push en iOS:

  • No son confiables: es decir, no hay ninguna garantía de que una notificación push llegue al destino, aunque el APNS haya aceptado el mensaje. El dispositivo podría estar apagado o fuera de cobertura, y el APNS lo reintentará varias veces, pero si no lo consigue lo desechará.
  • Los mensajes no pueden contener más de 256 caracteres
  • Aparte del mensaje, una notificación push podría indicar estas opciones:
    • sound: con qué sonido debe sonar la notificación (en caso de que el usuario haya permitido los sonidos). Si no se indica sonará con el sonido por defecto.
    • badge: el numerito que debe aparecer en el globo del icono (en caso de que el usuario haya permitido globos en los iconos)

Alta de aplicación en Apple y generación de certificados

Antes de empezar a crear la aplicación es conveniente crear todos los elementos que posteriormente se necesitarán para que funcionen las notificaciones push. Así que de momento nos toca “hacer papeleo”.

Concretamente necesitaremos que crear los siguientes elementos:

  • App ID: deberemos dar de alta un ID de aplicación para que podamos asociarle el resto de elementos, además de activarle explícitamente el soporte a Push Notifications.
  • Certificado Push: debemos generar un certificado específics para el servicio Push que será utilizado por nuestro servidor para comunicarse con el APNS. Este certificado irá asociados al App ID, así que únicamente servirá para mandar mensajes a esa aplicación.
  • Perfil de aprovisionamiento (provisioning profile): es necesario crear un perfil asociado al App ID para que podamos configurarlo en Xcode y así poder instalar la aplicación en un dispositivo real.

Para crear todos estos elementos, entraremos en nuestra cuenta de desarrollador iOS, en la sección Certificates, Identifiers & Profiles.

Paso 1: Creación del App ID

Nos vamos a Identifiers y luego a App IDs, donde pulsando en el botón + podremos crear uno nuevo.

En el formulario de creación debemos especificar estos valores:

  • App ID Description: el nombre de nuestra app. En este caso Test Push.
  • Explicit App ID: el identificador único de nuestra app, normalmente especificado como dominio inverso. Yo he puesto com.alfonsomarin.testpush (tú tendrás que poner otro diferente)
  • App Services: qué servicios queremos tener disponibles en nuetra app. Debemos marcar al menos Push Notifications, que es para lo que queremos la app.

Confirmamos y se generará el App ID, al cual iremos asociando los certificados y profiles

Paso 2: Generación de certificado Push

Ahora toca crear el certificado, que es uno de los pasos más pesados. Antes de continuar comentar que Apple identifica dos entornos diferenciados: desarrollo (development) y producción (production). El entorno de desarrollo nos permitirá instalar nuestra aplicación en dispositivos de pruebas mientras estemos desarrollándola, y el entorno de producción nos permitirá generar las versiones destinadas a publicación, ya sea para la App Store o instalaciones Ad Hoc.

En este tutorial vamos a generar el certificado y el perfil de desarrollo, siendo idénticos los pasos a seguir para generar los de producción.

Para crear el certificado necesitaremos generar un fichero llamado Certificate Signing Request (CSR), el cual nos pedirá Apple para poder generar los certificados. A grosso modo y sin entrar en detalles de funcionamiento, los certificados Apple están basados en clave pública y clave privada, y el CSR realmente contiene la clave privada con la que Apple generará el certificado público, por eso debemos proporcionarlo nosotros.

Para crear el CRS debemos hacer lo siguiente:

  1. Abrir el programa Acceso a llaveros
  2. En el menú superior, seleccionamos Acceso a llaverosAsistente para CertificadosSolicitar un certificado de una autoridad de certificación
  3. Indicamos los siguientes valores:
    1. Dirección de correo del usuario: ponemos cualquier dirección de correo
    2. Common Name: ponemos Test Push. Podemos poner cualqueir cosa pero poner el nombre de la app nos ayudará a localizar posteriormente la clave privada.
    3. Seleccionamos “Se guarda en el disco”, y pulsamos Continuar

test-push-csr

Nos pedirá que guardemos la clave en algún sitio de nuestro disco duro. La guardaremos como TestPush.certSigningRequest en el directorio certs de nuestro proyecto.

Antes de salir del programa Acceso a llaveros, exportaremos la clave que acabamos de generar a un fichero .p12 que posteriormente también necesitaremos. Para ello nos vamos a la sección “Claves” y al final aparecerán el par de claves (pública y privada) que acabamos de crear. Pinchamos en la clave privada con el botón derecho y seleccionamos Exportar “Test Push”. Lo guardaremos como TestPush.p12 también en el directorio certs de nuestro proyecto, y nos pedirá un password con el que cifrar el fichero. Es importante recordar ese password pues posteriormente lo necesitaremos.

export-key

Ahora sí que podemos crear el certificado Push. En el panel de desarrollador de Apple nos vamos a Certificates y en All pulsamos el botón + para empezar con la creación:

  1. Seleccionamos Apple Push Notification service SSL (Sandbox), de la sección Development. Si quisiésemos generar los de producción tendríamos que seleccionar Apple Push Notification service SSL (Production) de la sección Production.
  2. En el siguiente paso seleccionamos el App ID que acabamos de crear
  3. Ahora nos pide que subamos el CSR, el cual encontraremos en el directorio certs de nuestro proyecto
  4. Una vez subido el CSR se generará el certificado, que debemos descargar y guardar en el directorio certs. Lo guardaremos como testpush_aps_development.cer

Al llegar a este punto deberías tener 3 ficheros en el directorio certs:

  • El fichero CSR
  • La clave privada en formato .p12
  • El certificado SSL generado por Apple en formato .cer

De momento el fichero CSR ya no lo necesitaremos más, pero es conveniente que lo guardemos para poder utilizarlo de nuevo cuando tengamos que renovar el certificado SSL (expiran cada año), ya que así no tendremos que generar una nueva clave y podremos reutilizar el mismo fichero .p12 ya generado. Además, también podremos utilizarlo para solicitar el certificado de producción.

A la hora de trabajar con certificados en PHP es más cómodo trabajar con un único fichero en formato PEM que contenga tanto la clave privada como el certificado. Vamos a crear ese fichero .pem con los siguientes comandos (ejecutados desde el directorio certs de nuestro proyecto):

$ openssl x509 -in testpush_aps_development.cer -inform der -out TestPushCert.pem
$ openssl pkcs12 -nocerts -out TestPushKey.pem -in TestPush.p12

(introducir la contraseña que pusimos cuando creamos el fichero .p12, y pedirá una nueva para el fichero .pem. Yo he utilizado otra vez testpush)

$ cat TestPushKey.pem TestPushCert.pem > TestPushCK.pem

Y por fin hemos terminado con los certificados. Esto nos generará el fichero combinado TestPushCK.pem, que junto a la contraseña que hemos usado es lo que tendrá que utilizar nuestro servidor para enviar notificacioens push.

Paso 3: Creación de Provisioning Profile

Ya solo queda crear el perfil de aprovisionamiento para poder firmar nuestra app en Xcode. En el panel de desarrollador de Apple nos vamos a Provisioning Profiles y en All pulsamos el botón + para empezar con la creación:

  • Seleccionamos iOS App Development: recordemos que estamos en desarrollo. También podríamos seleccionar para distribución en AppStore o AdHoc.
  • Seleccionamos el App ID generado en el paso 1.
  • Seleccionamos el certificado de desarrollador que incorporará el perfil
  • Seleccionamos los dispositivos sobre los que podremos instalar la aplicación. Aquí ya deberías tener los tuyos dados de alta, pero si no es así tendrás que hacerlo previamente desde Devices → All.
  • Por último, indicamos un nombre descriptivo para el profile. Le ponemos TestPushDev (a mi me gusta poner sufijos Dev, AdHoc o AppStore según sea el tipo de perfil que estoy creando).

Una vez generado podremos descargarlo y a través del Finder arrastrarlo al icono de Xcode para instalarlo, pero si tenemos configurada nuestra cuenta de desarrollador en Xcode podremos sincronizarla con el portal y el perfil se descargará automáticamente. Podemos sincronizar desde el menú Xcode → Preferences → Accounts → View Details (pulsar icono de sincronización).

Creación de la aplicación Cordova/PhoneGap para iOS

Ha llegado el momento de crear nuestra aplicación. El cometido de la misma será únicamente obtener el token proporcionado por el APNS tras el registro y mostrarlo por pantalla. En una aplicación real lo que debería hacer es enviarlo al servidor y que este lo almacenase de alguna forma (en base de datos, por ejemplo), pero por simplificar símplemente lo mostrará por pantalla para que podamos copiarlo y meterlo manualmente en una variable del servidor. Así simplificaremos el código de ambas aplicaciones evitando tener que implementar funcionalidades complementarias que no están directamente relacionadas con el proceso de notificaciones push.

Paso 1: creacion del proyeto

Para crear el proyecto Cordova/PhoneGap haremos uso del cliente cordova, que ya deberías tener instalado. Para ello abrimos un terminal, nos situamos dentro del directorio app de nuestro proyecto y escribimos:

$ cordova create . com.alfonsomarin.testpush TestPush

Añadimos la plataforma ios al proyecto

$ cordova platform add ios

Paso 2: Instalación del plugin de notificaciones Push

Cordova no ofrece un plugin oficial para soporte de notificaciones push, así que tendremos que hacer uso de algún plugin de terceros. El que voy a utilizar es PushPlugin debido a su popularidad y por ser uno de los más completos, estables y mejor mantenidos por el momento. De hecho, aparte de iOS y Android ofrece soporte a Windows Phone 8, Windows8 y Amazon Fire OS, lo cual es muy interesante en vistas a futuras adaptaciones de nuestra aplicación a dichas plataformas.

Por tanto, el siguiente paso es instalar dicho plugin:

$ cordova plugin add https://github.com/phonegap-build/PushPlugin.git

Paso 3: Inclusión del código de registro de nuestra aplicación

Como ya sabemos lo primero que debe hacer nuestra aplicación es indicar al sistema operativo que deseamos recibir notificaciones, para que este le pida permiso al usuario y así poder obtener nuestro token.

El plugin ofrece esta funcionalidad a través de la llamada register(tokenHandler, errorHandler, options), cuyos parámetros tienen el siguiente significado:

  • tokenHandler: función que recibirá el token. Esta función sería la encargada de registralo en el servidor, pero como hemos comentado, por simplicidad únicamente mostrará el token por pantalla.
  • errorHandler: si ha habido algún error, el plugin invocará esta función
  • options: un hash donde indicaremos los siguientes valores:
    • badge: para indicarle al APNS si nuestra aplicación hará uso de globos en los iconos
    • sound: para indicarle al APNS si nuestra aplicación especificará el sonido a emitir en las notificaciones
    • alert: para indicarle al APNS si los mensajes contendrán algo de texto o no.
    • ecb: función callback que será invocada por el plugin cada vez que se reciba una notificación.

Sabiendo esto podremos entender mejor el código que vamos a meter a continuación. Este código lo meteremos en el propio fichero www/js/index.js generado en el proyecto base de Cordova/PhoneGap, el cual ya se encarga de gestionar la recepción del evento inicial ondeviceready, que es el que nos indica que podemos hacer uso de los plugins.

Así que editamos este fichero y al final de la función onDeviceReady incluímos este código

window.plugins.pushNotification.register(
    function(token){
        alert(token);
    },
    function(){alert(‘Error al registrarse en el servidor APNS’);},
    {
        "badge":"true",
        "sound": "true",
        "alert": "true",
        "ecb": "onNotificationAPN"
    });

Vemos que hemos definido tokenHandler y errorHandler como funciones inline, hemos especificado que queremos activar todas las opciones y por último hemos indicado que nuestra función para recibir las notificaciones push será onNotificationAPN.

Ahora solo nos queda incluir la función onNotificationAPN, que la incluiremos fuera del objeto app en el propio index.js:

function onNotificationAPN (event) {
    if (event.alert) {
       alert(event.alert);
    }

    if (event.sound) {
        var snd = new Media(event.sound);
        snd.play();
    }

    if (event.badge) {
        window.plugins.pushNotification.setApplicationIconBadgeNumber(function(){}, function(){}, event.badge);
    }
}

La implementación es bastante sencilla. Si la notificación push incluye un mensaje, lo mostramos en una pantalla de alerta. Si incluye un sonido, lo reproducimos con un objeto Media (necesitaríamos instalar el plugin org.apache.cordova.media). Y por último, si incluye un número a establecer como globo en el icono, lo hacemos con setApplicationIconBadgeNumber, otra función ofrecida por el plugin.

Paso 4: Compilación y ejecución

Ahora debemos compilar la aplicación para generar el proyecto Xcode

$ cordova build ios
$ open platforms/ios/TestPush.xcodeproj

Una vez dentro de Xcode, debemos hacer unos ajustes antes de poder ejecutar la aplicación en nuestro dispositivo iOS. Tendremos que ajustar en el proyecto el perfil de aprovisionamiento (provisioning profile) que creamos anteriormente. Este ya deberías tenerlo instalado en xcode si lo descargaste y arrastraste el fichero al icono de Xcode, o si sincronizaste tu cuenta a través de las preferencias como te comenté.

En el panel Project Navigator seleccionamos nuestro proyecto y nos vamos a la pestaña Build Settings, donde buscaremos la sección Code Signing. En dicha sección buscaremos la opción Provisioning Profile, donde seleccionaremos el perfil TestPushDev.

xcode-profile

Ahora ya podremos enchufar el dispositivo iOS a nuestro Mac y ejecutar la aplicación. Veremos que al arrancar lo primero que hace es pedirnos permiso para enviarnos notificaciones, que es lo que esperábamos. Si aceptamos, a los segundos nos aparecerá la alerta mostrándonos nuestro token.

ios-apns-register

En sucesivas ejecuciones de la aplicación nos aparecerá el token directamente, pues el permiso solo la primera vez.

Desarrollo de la aplicación servidor (provider)

Ahora vamos a hacer un pequeño script en PHP que será capaz de enviar mensajes Push a nuestra aplicación. Este script representaría al servidor, y es un poco “tosco” pues como veremos le meteremos el token a mano, pero como ya hemos comentado lo haremos así por simplicidad y para centrarnos en el proceso de mensajería push.

Dentro del directorio server de nuestro proyecto, crearemos un script llamado server.php con el siguiente contenido:

// Nuestro token
$deviceToken = '4a9988acca4d63f7434279de17290eab37fd4006d57d66879d4c743937db958c';
 
// El password del fichero .pem
$passphrase = 'testpush';
 
// El mensaje push
$message = '¡Mi primer mensaje Push!';
 
$ctx = stream_context_create();
//Especificamos la ruta al certificado .pem que hemos creado
stream_context_set_option($ctx, 'ssl', 'local_cert', '../certs/TestPushCK.pem');
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
 
// Abrimos conexión con APNS
$fp = stream_socket_client(
	'ssl://gateway.sandbox.push.apple.com:2195', $err,
	$errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);
 
if (!$fp) {
	exit("Error de conexión: $err $errstr" . PHP_EOL);
}
 
echo 'Conectado al APNS' . PHP_EOL;
 
// Creamos el payload
$body['aps'] = array(
	'alert' => $message,
	'sound' => 'bingbong.aiff',
	'badge' => 35
	);
 
// Lo codificamos a json
$payload = json_encode($body);
 
// Construimos el mensaje binario
$msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken) . pack('n', strlen($payload)) . $payload;
 
// Lo enviamos
$result = fwrite($fp, $msg, strlen($msg));
 
if (!$result) {
	echo 'Mensaje no enviado' . PHP_EOL;
} else { 
	echo 'Mensaje enviado correctamente' . PHP_EOL;
}
 
// cerramos la conexión
fclose($fp);

Tendrás que cambiar el token por el que te haya dado a tí el APNS (una forma rápida de copiarlo es abriendo en el Mac en navegador Safari mientras se ejecuta la aplicación y abrir la consola de desarrollo remota a través del menú Desarrollo → <nombre de tu iDispositivo> → index.html, y poniendo window.testPush abajo).

También te podrás fijar que hemos puesto como badge un número alto (35), para comprobar que efectivamente se establece como globo en el icono.

Ahora ya solo tenemos que ejecutar el script y ver cómo llega nuestro mensaje. En el terminal, dentro del directorio server, escribimos:

$ php server.php

Si hemos seguido todos los pasos indicados en este tutorial deberías ver el mensaje en tu terminal:
ios-push-screenshots

Referencias y enlaces de interés

  1. Antonio Maturana November 14, 2014

    Enhorabuena por el tutorial, muy completo y bien explicado. He conseguido que las notificaciones lleguen al dispositivo pero no se porque no aparece el badge en el icono cuando se reciben. Se te ocurre cual puede ser el motivo? Muchas gracias

    • Alfonso Marín November 17, 2014

      Hola, para establecer el badge asegúrate de llamar a la función window.plugins.pushNotification.setApplicationIconBadgeNumber() dentro de la función callback. Es lo he he hecho yo en la función onNotificationAPN de este tutorial.

  2. Ivan December 6, 2014

    Bueno lo primero felicitar al autor de este tutorial, ya que esta siendo de gran ayuda a muchos desarrolladores y comengtar que he realizado un tutorial donde junto a este y otro de android explico como solucionar pequeños problemas y sobretodo como realizar el envio de notificaciones de manera masiva y con varios tipos de mensajes. Os dejo el enlace por si a alguno le sirve…

    http://www.olacreativa.es/notificaciones-push-androidios/

  3. Ivan December 6, 2014

    Bueno lo primero felicitar al autor de este tutorial, ya que esta siendo de gran ayuda a muchos desarrolladores y comengtar que he realizado un tutorial donde junto a este y otro de android explico como solucionar pequeños problemas y sobretodo como realizar el envio de notificaciones de manera masiva y con varios tipos de mensajes. Os dejo el enlace por si a alguno le sirve…

    http://www.olacreativa.es/notificaciones-push-androidios/

  4. Cristian Alvarez December 29, 2014

    Muy buen aporte, gracias por este tutorial, me ha servido de gran ayuda para los desarrollos con PhoneGap en Xcode

  5. Jainer Nieto July 3, 2015

    Excelente aporte, segui cada uno de los pasos y me funciono perfecto. Gracias!!!!!!

  6. R. Eder Weiss Juárez July 15, 2015

    Segui todos los pasos, la aplicación phonegap ios me muestra el token, el problema lo he tenido en la parte del desarrollo de la aplicación servidor, he colocado el archivo server.php junto a mi archivo .pe, y cuando lo ejecuto me sale en siguiente error, agradeceria me pudieras orientar:

    Warning: stream_socket_client(): SSL operation failed with code 1. OpenSSL Error messages: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed in /Applications/XAMPP/xamppfiles/htdocs/PushChat/server.php on line 19

    Warning: stream_socket_client(): Failed to enable crypto in /Applications/XAMPP/xamppfiles/htdocs/PushChat/server.php on line 19

    Warning: stream_socket_client(): unable to connect to ssl://gateway.sandbox.push.apple.com:2195 (Unknown error) in /Applications/XAMPP/xamppfiles/htdocs/PushChat/server.php on line 19
    Error de conexión: 0

  7. Jesús Pérez January 20, 2016

    Muchísimas gracias por este tutorial tan completo. La verdad es que siguiéndolo a la primera pude hacer lo de los certificados!

  8. Rodrigo February 19, 2016

    Hola.
    He estado siguiendo los pasos y todo ha ido bien. Sin embargo, en el ejemplo pusiste un “badge” de 37 sólo para ver como funcionaba.
    Yo en principio pongo 1, pero quiero que el “badge” se actualice con cada notificación. Es decir, quiero que cuando llegue una notificación se ponga a 1, y si llega otra y no ha leído la anterior, que aparezca 2, y así sucesivamente.

    He intentado con la funciones “push.setApplicationIconBadgeNumber” dentro del “push.on(‘notification’) pero ese código sólo se ejecuta cuando la aplicación está abierta.

    ¿Cómo podría hacer que el “badge” se fuera actualizando? Creo que no he usado el mismo plugin, sino la última versión (ya que decía que la otra estaba desactualizada). El plugin usado ha sido phonegap-push-plugin.

    Gracias de antemano. Un saludo!

  9. Rodrigo February 21, 2016

    Hola.

    Publiqué un comentario hace dos días pero veo que ha desaparecido. No sé si está pendiente de aprobación o no (si es así, entonces elimina este último).

    El caso es que seguí todos los pasos y todo funciona al 100% (mucha gracias por el tutorial!) pero tengo una duda. En el ejemplo indicas de ejemplo un “badge” de 35, y yo quiero que ese badge se vaya incrementando cuando vayan llegando notificaciones nuevas y no haya leído las anteriores.

    He probado poniendo un incremento en la función “function onNotificationAPN” pero esa función sólo se ejecuta si la APP está abierta.

    ¿Cómo podría hacer esto? Gracias de antemano.

    Un saludo.

    • Alfonso Marín February 22, 2016

      Hola:

      Eso es algo que tienes que mantener en tu servidor, es decir, es el servidor quien “sabe” el número que debe mandar. Esto se traduce en que tu app debe informar al servidor que elementos se han procesado y por tanto cuanto debe disminuir el contador, ya sea al arrancar la aplicación o en el momento de procesar cada elemento.

      Y respecto al plugin, efectivamente tengo pensado hacer una pequeña actualización del tutorial con el nuevo.

      Saludos