Codifica una aplicación en tiempo real con NativeScript: Notificaciones Push
() translation by (you can also view the original English article)
NativeScript es un framework para crear aplicaciones móviles nativas multiplataforma usando XML, CSS y JavaScript. En esta serie vamos a probar algunas de las geniales cosas que puedes hacer con una aplicación de NativeScript: geolocalización e integración con Google Maps, bases de datos SQLite, integración con Firebase y notificaciones push. En el proceso estaremos construyendo una aplicación de acondicionamiento físico con capacidades en tiempo real que usará cada una de estas características.
En este tutorial aprenderás lo sencillo que es añadir notificaciones push a tu aplicación de NativeScript con el Cloud Messaging Service (servicio de mensajería en la nube) de Firebase.
Lo que crearás
Retomando el tutorial anterior, ahora vas a añadir notificaciones push a la aplicación. Una notificación será desencadenada cuando el usuario rompa su récord actual o cuando uno de sus amigos le quite el primer lugar.
Configurando el proyecto
Si has seguido el tutorial anterior sobre Firebase, simplemente puedes usar el mismo proyecto y crear las funciones que añadiremos en este tutorial. De lo contrario, puedes crear un nuevo proyecto y copiar los archivos de inicio en la carpeta app de tu proyecto.
1 |
tns create fitApp --appid "com.yourname.fitApp" |
Después de eso también necesitas instalar los complementos de geolocalización, de Google Maps, de SQLite y de Firebase:
1 |
tns plugin add nativescript-geolocation |
2 |
tns plugin add nativescript-google-maps-sdk |
3 |
tns plugin add nativescript-sqlite |
4 |
tns plugin add nativescript-plugin-firebase |
Una vez instalados, necesitas configurar el complemento de Google Maps. Puedes obtener las instrucciones completas sobre cómo hacer esto leyendo la sección Instalando el complemento de Google Maps en el tutorial anterior.
A continuación instala la biblioteca fecha para darle formato a las fechas:
1 |
npm install --save fecha |
Después de esto también necesitas configurar el complemento de Firebase. Asegúrate de leer las siguientes secciones del tutorial anterior para lograr que la aplicación se ejecute:
- Ejecutando el proyecto
- Configurando una aplicación de Firebase
- Configurando una aplicación de Facebook
- Instalando el complemento de Firebase
- Configurando la integración con Facebook
Dado que ya hemos configurado el complemento de Firebase en la publicación anterior, solamente hay un poco de trabajo que debes llevar a cabo para configurar las notificaciones push.
Primero tienes que volver a configurar el complemento yendo al interior del directorio node_modules/nativescript-plugin-firebase y ejecutando npm run config
. Esta vez selecciona tanto la autenticación como la mensajería de Facebook.
Una vez hecho lo anterior, abre el archivo firebase.nativescript.json en el directorio raíz de tu proyecto y asegúrate de que messaging
esté configurado con el valor true
:
1 |
{
|
2 |
"using_ios": false, |
3 |
"using_android": true, |
4 |
"remote_config": false, |
5 |
"messaging": true, |
6 |
"crash_reporting": false, |
7 |
"storage": false, |
8 |
"facebook_auth": true, |
9 |
"google_auth": false, |
10 |
"admob": false, |
11 |
"invites": false |
12 |
}
|
A continuación abre app/App_Resources/Android/AndroidManifest.xml y agrega los siguientes servicios dentro de <application>
. Esto habilita el servicio de mensajería de Firebase para la aplicación:
1 |
<application ...> |
2 |
|
3 |
<service android:name="org.nativescript.plugins.firebase.MyFirebaseInstanceIDService"> |
4 |
<intent-filter>
|
5 |
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/> |
6 |
</intent-filter>
|
7 |
</service>
|
8 |
<service android:name="org.nativescript.plugins.firebase.MyFirebaseMessagingService"> |
9 |
<intent-filter>
|
10 |
<action android:name="com.google.firebase.MESSAGING_EVENT"/> |
11 |
</intent-filter>
|
12 |
</service>
|
13 |
|
14 |
</application>
|
Ejecutando el proyecto
Puedes arrancar el proyecto ejecutando tns run android
. Pero dado que ésta aplicación aprovechará la funcionalidad de geolocalización, te recomiendo que uses un emulador de GPS para configurar y cambiar tu ubicación rápidamente. Puedes leer sobre cómo lograr lo anterior en la sección Ejecutando la aplicación en el tutorial anterior.
Si aparecen errores de compilación, puedes eliminar la plataforma y volver a ejecutar la aplicación:
1 |
tns platform remove android |
2 |
tns run android |
Configurando Cloud Functions de Firebase
Vas a usar Cloud Functions de Firebase para crear un servidor que enviará las notificaciones push. Ésta característica de Firebase se usa para ejecutar código de fondo cada vez que ocurra un evento específico en las herramientas de Firebase que estés usando. Por ejemplo, cuando se guarde nueva información en la base de datos en tiempo real, o cuando se agregue a un nuevo usuario a través del servicio de autenticación de Firebase. Para esta aplicación usarás desencadenadores HTTP para enviar notificaciones push cuando la aplicación móvil haga una solicitud a un punto final específico.
Para usar Cloud Functions de Firebase primero necesitas instalar el paquete firebase-tools
globalmente:
1 |
npm install -g firebase-tools |
A continuación crea una nueva carpeta que albergará el código del servidor. Ésta debe estar afuera de tu carpeta app. Dentro de esa carpeta instala el paquete firebase-functions
:
1 |
npm install firebase-functions@latest --save |
Una vez que esté instalado, inicia sesión en Firebase ejecutando firebase login
. Esto abre una nueva pestaña de navegación que te permite iniciar sesión con tu cuenta de Google. Lleva a cabo todo el proceso y acepta todos los permisos que se soliciten.
Una vez que hayas iniciado sesión puedes arrancar las funciones de Firebase para un proyecto de Firebase específico:
1 |
firebase init functions |
Este proceso te preguntará si quieres configurar un proyecto predeterminado o no. Selecciona el proyecto de Firebase que creaste en el tutorial anterior:



A continuación se te preguntará si quieres instalar las dependencias. Dí que sí.
Una vez que las dependencias hayan sido instaladas, deberás ver un archivo llamado firebase.json y una carpeta llamada functions dentro del directorio. El archivo con el que estarás trabajando es functions/index.js. Abre dicho archivo y verás lo siguiente:
1 |
const functions = require('firebase-functions'); |
2 |
|
3 |
// // Create and Deploy Your First Cloud Functions
|
4 |
// // https://firebase.google.com/docs/functions/write-firebase-functions
|
5 |
//
|
6 |
// exports.helloWorld = functions.https.onRequest((request, response) => {
|
7 |
// response.send("Hello from Firebase!");
|
8 |
// });
|
Descomenta la función helloWorld
y podrás ver a los desencadenadores HTTP en acción.
1 |
exports.helloWorld = functions.https.onRequest((request, response) => { |
2 |
response.send("Hello from Firebase!"); |
3 |
});
|
Ejecuta lo siguiente para desplegar la función en la nube:
1 |
firebase deploy --only functions
|
Una vez que dicho proceso haya finalizado, éste deberá mostrarte el URL en el que la función haya sido desplegada:



Accede a dicho URL desde tu navegador para ver la salida "Hello from Firebase!".
Añadiendo el código del servidor
Ahora estás listo para añadir el código necesario para implementar las notificaciones push. Primero agregarás el código para el componente del servidor y luego el código para la aplicación.
Abre el archivo functions/index.js y elimina su contenido.
Creando la función de Firebase
Importa los paquetes de Firebase que necesitarás:
1 |
const functions = require('firebase-functions'); // for listening to http triggers |
2 |
const admin = require('firebase-admin'); // for accessing the realtime database |
3 |
admin.initializeApp(functions.config().firebase); // initialize the Firebase Admin SDK |
Crea la función init_push
. Toma en cuenta que el desencadenador HTTP es invocado para cualquier método request, así que tienes que filtrar para el método request que quieras procesar. En este caso solamente queremos procesar solicitudes POST
. Esperamos que la aplicación envíe id
, steps
y friend_ids
como los datos solicitados.
1 |
exports.init_push = functions.https.onRequest((request, response) => { |
2 |
|
3 |
if(request.method == 'POST'){ |
4 |
|
5 |
var id = request.body.id; // ID of the user who made the request (Firebase Auth ID) |
6 |
var steps = parseInt(request.body.steps); // latest steps, not recorded yet |
7 |
var friend_ids = request.body.friend_ids.split(','); |
8 |
|
9 |
friend_ids.push(id); // also include the ID of the current user |
10 |
|
11 |
// next: add code for getting the user and friends data
|
12 |
}
|
13 |
|
14 |
});
|
Obteniendo los datos del usuario y de los amigos
A continuación consulta la base de datos de Firebase para verificar si el ID del usuario existe. Esto sirve como una manera de asegurar el punto final para que no cualquiera pueda desencadenar notificaciones push (desde luego, una aplicación real debería tener una seguridad de fondo mucho mejor, de manera que los usuarios no puedan falsificar sus propios datos o los de otra persona).
Si el usuario existe, consulta la base de datos nuevamente para que devuelva a todos los usuarios. Toma en cuenta que actualmente Firebase no proporciona una manera de devolver registros en base a un arreglo de IDs, por lo que tendremos que filtrar los datos relevantes nosotros mismos:
1 |
admin.database().ref('/users') |
2 |
.orderByChild('id') |
3 |
.limitToFirst(1) |
4 |
.equalTo(id) |
5 |
.once('value').then(snapshot => { |
6 |
|
7 |
var user_data = snapshot.val(); |
8 |
|
9 |
if(user_data){ |
10 |
// get all users from the database
|
11 |
admin.database().ref('/users') |
12 |
.once('value').then(snapshot => { |
13 |
// next: add code for getting the current user's data and their friends data
|
14 |
});
|
15 |
}
|
16 |
});
|
A continuación recorre con un ciclo los resultados devueltos de Firebase y crea un nuevo arreglo que almacene friends_data
. Una vez hecho esto, ordena el arreglo de acuerdo al número de pasos por cada usuario. Aquél que tenga el mayor número de pasos tiene el primer índice.
1 |
var friends_data = []; |
2 |
var current_user_data = null; |
3 |
var notification_data = {}; |
4 |
var has_notification = false; |
5 |
|
6 |
var users = snapshot.val(); |
7 |
for(var key in users){ |
8 |
var user_id = users[key].id; |
9 |
|
10 |
if(friend_ids.indexOf(user_id) != -1 && id != user_id){ // the current user's friends |
11 |
friends_data.push(users[key]); |
12 |
}else if(id == user_id){ // the current user |
13 |
current_user_data = users[key]; |
14 |
}
|
15 |
}
|
16 |
|
17 |
// sort in descending order by the number of steps
|
18 |
var sorted_friends_data = friends_data.sort(function(a, b) { |
19 |
return b.steps - a.steps; |
20 |
});
|
21 |
|
22 |
// next: add code for constructing the notification payload
|
Construye la carga útil de notificaciones
Ahora estamos listos para determinar quién recibirá la notificación y construir la carga útil de notificaciones. ¿Quién está en primer lugar? ¿es el usuario actual o uno de sus amigos?. Dado que el usuario actual también habrá superado su propio récord al romper el récord general de quien esté en primer lugar, solamente necesitamos verificar si ese récord ha sido superado.
1 |
if(steps > sorted_friends_data[0].steps){ |
2 |
// notify friend who was overtaken
|
3 |
var diff_steps = steps - sorted_friends_data[0].steps; |
4 |
notification_data = { |
5 |
payload: { |
6 |
title: 'One of your friends beat your record', |
7 |
body: 'Too bad, your friend ' + current_user_data.user_name + ' just overtook you by ' + diff_steps + ' steps' |
8 |
},
|
9 |
device_token: sorted_friends_data[0].device_token |
10 |
};
|
11 |
has_notification = true; |
12 |
|
13 |
}else if(steps > current_user_data.steps){ |
14 |
// notify current user
|
15 |
var diff_steps = steps - current_user_data.steps; |
16 |
notification_data = { |
17 |
payload: { |
18 |
title: 'You beat your record!', |
19 |
body: 'Congrats! You beat your current record by ' + diff_steps + ' steps!' |
20 |
},
|
21 |
device_token: current_user_data.device_token |
22 |
};
|
23 |
has_notification = true; |
24 |
}
|
25 |
|
26 |
// next: add code for sending push notification
|
Enviando la notificación
Finalmente envía la notificación:
1 |
if(has_notification){ |
2 |
|
3 |
var payload = { |
4 |
notification: notification_data.payload |
5 |
};
|
6 |
|
7 |
// send push notification
|
8 |
admin.messaging().sendToDevice(notification_data.device_token, payload).then(function(res) { |
9 |
|
10 |
response.send(JSON.stringify({'has_notification': true})); // inform the app that a notification was sent |
11 |
})
|
12 |
.catch(function(error) { |
13 |
response.send(JSON.stringify(error)); // send the push notification error to the app |
14 |
});
|
15 |
|
16 |
}else{ |
17 |
response.send(JSON.stringify({'has_notification': false})); // inform the app that a notification was not sent |
18 |
}
|
Actualizando el código de la aplicación
Anteriormente configuraste la aplicación para que pudiera recibir notificaciones push. En esta ocasión vas a añadir código para que tu aplicación pueda procesar esas notificaciones push y mostrarlas al usuario.
Recibiendo notificaciones push
Lo primero que necesitas hacer para recibir notificaciones push es actualizar la función firebase.init()
para incluir una función escucha y así recibir el token del dispositivo:
1 |
onPushTokenReceivedCallback: function(token) { |
2 |
// temporarily save it to application settings until such time that
|
3 |
// the user logs in for the first time
|
4 |
applicationSettings.setString('device_token', token); |
5 |
},
|
Ésta función solamente se ejecuta una vez, por lo que tienes que guardar el token localmente usando las configuraciones de la aplicación. Más adelante, ésto nos permitirá obtener el token del dispositivo cuando el usuario inicie sesión por primera vez. Si aún recuerdas el tutorial anterior, estamos guardando los datos del usuario en Firebase la primera vez que inicia sesión.
A continuación puedes añadir la función escucha para cuando se reciban las notificaciones. Ésta mostrará un cuadro de alerta que usa el título y el cuerpo del mensaje como contenido:
1 |
onMessageReceivedCallback: function(message) { |
2 |
dialogs.alert({ |
3 |
title: message.title, |
4 |
message: message.body, |
5 |
okButtonText: "ok" |
6 |
});
|
7 |
},
|
Guardando el token del dispositivo en Firebase
Cloud Messaging de Firebase requiere el token del dispositivo al enviar una notificación push a un dispositivo específico. Dado que ya estamos usando Firebase, solamente guardaremos el token del dispositivo junto con los datos del usuario. Para eso necesitas editar el código que almacena los datos del usuario con el fin de incluir el token del dispositivo que obtuvimos anteriormente:
1 |
if(firebase_result.value == null){ |
2 |
|
3 |
var device_token = applicationSettings.getString('device_token'); |
4 |
|
5 |
var user_data = { |
6 |
'uid': fb_result.uid, |
7 |
'user_name': fb_result.name, |
8 |
'profile_photo': fb_result.profileImageURL, |
9 |
'device_token': device_token |
10 |
};
|
11 |
|
12 |
}
|
Desencadenando notificaciones push
Las notificaciones push se desencadenan cuando una de dos cosas ocurren:
- cuando el usuario rompe su récord actual
- cuando uno de los amigos del usuario rompe su récord y sube al primer lugar
El primer caso es sencillo, así que no hay necesidad de llevar a cabo una configuración adicional. Pero para el segundo se necesita un poco de trabajo. Primero tienes que editar el código para cuando el estado de la autenticación cambie. Justo después de extraer los IDs de los amigos del resultado de Facebook tienes que guardarlos usando las configuraciones de la aplicación.
1 |
// extracting the friend IDs from the Facebook result
|
2 |
var friends_ids = r.data.map(function(obj){ |
3 |
return obj.id; |
4 |
});
|
5 |
|
6 |
// save the friend IDs
|
7 |
applicationSettings.setString('friends_ids', JSON.stringify(friends_ids)); |
8 |
|
9 |
friends_ids.push(user[user_key].id); |
A continuación actualiza el código para cuando el usuario deje de darle seguimiento a su caminata. Justo después del código que se usa para construir los datos del usuario con el fin de actualizarlo, obtén los IDs de los amigos a partir de las configuraciones de la aplicación e inclúyelos en el objeto que contiene los datos de solicitud para desencadenar la notificación push.
1 |
// construct the user data for updating the user's distance and steps
|
2 |
var user_key = applicationSettings.getString('user_key'); |
3 |
var user = applicationSettings.getString('user'); |
4 |
var user_data = JSON.parse(user); |
5 |
user_data[user_key].distance = total_distance; |
6 |
user_data[user_key].steps = total_steps; |
7 |
|
8 |
// get friend IDs
|
9 |
var friends_ids = JSON.parse(applicationSettings.getString('friends_ids')); |
10 |
|
11 |
var request_data = { |
12 |
'id': user_data[user_key].id, |
13 |
'friend_ids': friends_ids.join(','), |
14 |
'steps': total_steps |
15 |
};
|
Lleva a cabo la solicitud al punto final de Cloud Functions de Firebase que creaste anteriormente. Los datos del usuario serán actualizados en la base de datos de Firebase únicamente hasta que se devuelva una respuesta exitosa.
1 |
http.request({ |
2 |
url: "https://us-central1-pushapp-ab621.cloudfunctions.net/init_push", |
3 |
method: "POST", |
4 |
headers: { "Content-Type": "application/json" }, |
5 |
content: JSON.stringify(request_data) |
6 |
}).then(function (response) { |
7 |
|
8 |
var statusCode = response.statusCode; |
9 |
if(statusCode == 200){ |
10 |
// update the user's data on Firebase
|
11 |
firebase.update( |
12 |
'/users', |
13 |
user_data
|
14 |
);
|
15 |
|
16 |
}
|
17 |
|
18 |
}, function (e) { |
19 |
console.log('Error occurred while initiating push: ', e); |
20 |
});
|
Verificando las notificaciones push
Puedes probar el envío de notificaciones push desinstalando primero la aplicación del emulador o dispositivo. Esto nos permite desencadenar correctamente a la función para obtener el token del dispositivo. Asegúrate de añadir console.log
para mostrar dicho token:
1 |
onPushTokenReceivedCallback: function(token) { |
2 |
applicationSettings.setString('device_token', token); |
3 |
console.log('device token: ', device_token); // <-- add this |
4 |
}
|
Cuando el token del dispositivo sea mostrado en la consola de NativeScript, cópialo, haz clic en el menú Database (Base de datos) en el panel de control de la aplicación de Firebase y agrégalo como un token de dispositivo para todos los usuarios de la aplicación. Usa device_token
como nombre de la propiedad.
Para desencadenar la notificación push puedes usar curl para hacer una solicitud POST
al punto final de la función de Firebase:
1 |
curl -X POST -H "Content-Type:application/json" YOUR_FIREBASE_FUNCTION_ENDPOINT -d '{"id":"ID OF A FIREBASE USER", "steps":NUMBER_OF_STEPS, "friend_ids":"COMMA,SEPARATED,FIREBASE,USER_IDs"}' |
Si no tienes curl instalado, puedes usar la aplicación Postman para enviar la solicitud. Usa la siguiente configuración para la solicitud:
- Método de solicitud:
POST
- URL: El punto final de tu función de Firebase
- Clave de los encabezados:
Content-type
- Valor de los encabezados:
application/json
- Cuerpo:
1 |
{"id":"ID OF A FIREBASE USER", "steps":NUMBER_OF_STEPS, "friend_ids":"COMMA,SEPARATED,FIREBASE,USER_IDs"} |
Una vez desencadenada la notificación, verás una salida similar a la siguiente:



Si la aplicación no está abierta actualmente, verás el mensaje en el área de notificaciones:



Conclusión
¡Felicitaciones! Finalmente has terminado la aplicación de acondicionamiento físico. En el transcurso de cuatro tutoriales has construido una aplicación de NativeScript que usa Google Maps, SQLite, la base de datos en tiempo real de Firebase y Cloud Messaging de Firebase. Ahora tienes una muy buena base para crear aplicaciones de NativeScript que usen esas tecnologías.
Para obtener más información sobre NativeScript u otras tecnologías móviles multiplataforma ¡asegúrate de consultar algunos de nuestros otros cursos y tutoriales aquí en Envato Tuts+!.