Tutorial: Mobile Push Notifications

Serverless Database Mobile Push Notifications

[[service]] provides a "mobile wake-up" function that allows a mobile application to subscribe to a data node of the [[service]] database and to be notified when data under this node is updated even if the mobile application is stopped. In this case, the application is launched or woken up.

In usual settings, when data is updated somewhere in a [[service]] database, it is shared in real time with every instance of Android app that runs in foreground on every connected device. However this operation relies on websockets, which die as soon as apps go to background.

The "mobile wake-up" function overcomes this limitation by making it possible to wake-up a stopped or background Android app upon a data update in the [[service]] database. It can also optionally forward data to it.

With this feature, you choose the path of the data node in the [[service]] database that may wake up your Android app. This path covers updates on all its child data nodes. For example, if you subscribe to the root path, every change in the database will be notified.

Subscribing and unsubscribing are done using either methods of the [[service]] Android SDK or [[service]] specific REST requests. Then the waking up process uses Firebase Cloud Messaging (FCM, formerly Google Cloud Messaging) for Android devices and Apple Push Notification service (APNs) for Apple devices.

Actually, your app asks [[service]] to listen to some data node at a given path, and as soon as something happens under this path, [[service]] asks FCM to send an appropriate message to appropriate (subscribed) devices. Your device receives this message and has to handle it. Typically you can display it to the user through a notification or launch your app so that it resynchronizes itself using [[service]] SDK API and detects what is new and what to do.

You also need to provide what we call a "device id" to allow FCM to send the message to the right device. Retrieving this device id is done through FCM API on the device itself.

Mobile wake-up developer configuration

Prerequisites

Mobile wake-up is a functionality provided by Google for Android OS (FCM) and by Apple for iOS (APNs). [[service]] interconnects only with FCM. As FCM provides a relay service to APNs, APNs can also be used via [[service]] and FCM. In order to use FCM (or APNs through the FCM relay), you need to configure a Google key in [[console]].

To get such a key, first, go to Firebase console and create a project, then generate a new private key in project settings / Service account and download it.

In addition, if you want to use APNs, you need to follow this procedure.

Using [[console]]

Go to wake-up tab in [[console]], enable wake-up functionality, and upload private key (from FCM).

Add a wake-up subscription

In order to set up wake-up subscriptions from your mobile app code, you can use either the [[service]] Android SDK API or the low-level REST API (typically for iOS apps).

In both cases, you need the "device id" of the mobile device (referred to as "token" in the official FCM API). You can get it using FCM API:

String myDeviceId = FirebaseInstanceId.getInstance().getToken();
let myDeviceId : String = InstanceID.instanceID().token()

This section covers tasks you may have completed if you have already enabled other Firebase features for your app. For FCM specifically, you'll need to upload your APNs authentication key and register for remote notifications.

Here is an example [[snippet]]:

Webcom ref = new Webcom("[[baseUrl]]/base/<your-app>/some/webcom/path");
ref.wakeupSubscribe(myDeviceId, "value", new OnComplete() {
                @Override
                public void onComplete() {
                    Log.d(TAG, " wakeupSubscribe onComplete ");
                }

                @Override
                public void onError(final WebcomError error) {
                    Log.d(TAG, " wakeupSubscribe onError ", error.toException());
                }
            });
let ref : WCWebcom = WCWebcom(url : "file://io./base/samplechat")
let path : String = "contacts/orange/message"
ref?.wakeupSubscribe(deviceidToken: myDeviceId, tokenAuth: tokenAuth, path: path, mode: mode , completeCallback: { (data, error) in
           if (error != nil){
               print("wakeupSubscribe onError \(error.debugDescription) ")
           }else {
               print("wakeupSubscribe onComplete")
           }
})
curl -X PUT "[[baseUrl]]/base/<your-app>/.wakeup/some/webcom/path?id=<myDeviceId>"

In the Android version, the path of the subscription to register is given by the path associated to the Webcom object reference on which the wakeupSubscribe method is called.

The second parameter of this method is a subscription mode that controls the data to send back in future notifications. It can be either "value", "noData" (or null, this is the default value) or "childEvent" (see below).

The last parameter is a handler whose onComplete callback is called asynchronously once the wake-up subscription is successfully registered on the [[service]] server. If the registration fails on the server, then the onError callback is called instead.

With this method, the subscription is registered using the current authentication state maintained by the [[service]] SDK. The authentication state constraints the data nodes on which the subscriptions are allowed.

In the iOS version, the path of subscription to register is given by the params of the wakeupSubscribe method.

deviceidToken : is a DeviceID of the mobile device (referred to as "token" in the official FCM API). You can get it using FCM API.

token : An authentication token.

path : You choose the path of the data node in the Flexible Datasync database that may wake up your iOS app.

mode: is a subscription mode that controls the data to send back in future notifications. It can be either "value", "noData" (or null, this is the default value) or "childEvent" (see below

completeCallback: Block called once Wakeup subscribe is complete. On failure, return error object (Error).

In the REST version, the sub-path after .wakeup in the URI is the path of the [[service]] data node where the subscription takes place.

In the query part, only the id parameter is mandatory, which refers to the FCM device id. Other query parameters control the [[service]] authentication state to take into account for the subscription or the data to send back in future notifications.

Query parameter Type Mandatory
Optional
Description
token string O An authentication token. If not specified, the subscription is done without authentication.
id string M FCM device id (referred to as "token" in Android APIs) that uniquely identifies the mobile device
mode string O Subscription mode that controls the data sent back in notifications. Supported values are: "noData" (default value), "value" or "childEvent" (see below)

On success you will receive a 204 (no content) response. On error, you will receive an error response containing a JSON message with a status field giving the error reason (as a string).

REST status code JSON status Description
404 namespace_does_not_exist No [[service]] application found with this name
403 disabled Mobile wake-up feature is disabled for this application
403 permission_denied Security rules prevent read access to the data located at the subscribed data node (according to current data and the specified authentication token)
403 too_big The data located at the subscribed data node is too big and can no longer be subscribed (see weight limitations)

Notes:

  • data included in FCM and APNs notifications are limited to 4KB
  • A wake-up subscription is uniquely identified by a 3-tuple ([[service]] application name, path, device-id).

Refresh a subscription

A subscription must be refreshed before expiration. Expiration time is configured through wake-up settings on [[console]]. To refresh a subscription, you need to send the same request than the original subscription.

If a subscription expires, an FCM message for revocation will be sent to subscribed devices with status "expired".

Device id (e.g. FCM token) refresh

When receiving FCM notification "onTokenRefresh", you have to

  • wakeupSubscribe with new deviceId (FCM token),
  • wakeupUnsubscribe with former deviceId to clean it on [[service]] side.

Remove a wake-up subscription

Removing a subscription may be done like this [[snippet]]:

Webcom ref = new Webcom("[[baseUrl]]/base/<your-app>/some/webcom/path");
ref.wakeupUnsubscribe(myDeviceId, new OnComplete() {
                @Override
                public void onComplete() {
                    Log.d(TAG, " wakeup UNSubscribe onComplete ");
                }

                @Override
                public void onError(final WebcomError error) {
                    Log.d(TAG, " wakeup UNSubscribe onError ", error.toException());
                }
            });
let ref : WCWebcom = WCWebcom(url : "file://io./base/samplechat")
let path : String = "contacts/orange/message"
ref?.wakeupUnsubscribe(deviceidToken: deviceId, tokenAuth: tokenAuth, path: path, completeCallback: { (data, error) in
        if (error != nil){
               print("wakeupUnsubscribe onError \(error.debugDescription) ")
           }else {
               print("wakeupUnsubscribe onComplete")
      }
})
curl -X DELETE "[[baseUrl]]/base/<your-app>/.wakeup/some/webcom/path?id=<myDeviceId>"

In the Android version, like for adding a subscription, the path of the subscription to remove is retrieved from the path associated to the Webcom object reference on which the wakeupUnsubscribe is called. This method expects as first parameter the "device id" (it must be the same than the one used with the wakeupSubscribe method). The second parameter is a handler whose onComplete callback is called asynchronously once the wake-up subscription is successfully unregistered on the [[service]] server. If the unregistration fails on the server, then the onError callback is called instead.

In the iOS version , like for adding a subscription, the path of subscription to register is given by param of the wakeupunSubscribe method

"device id" , "token" & "path" (it must be the same than the one used with the wakeupSubscribe method).

completeCallback: Block called once Wakeup subscribe is complete. On failure, return error object (Error).

In the REST version, the sub-path after .wakeup in the URI is the path of the [[service]] data node where the subscription is unregistered. The available query parameters are:

Query parameter Type Mandatory
Optional
Description
id string M FCM device id (referred to as "token" in Android APIs) that uniquely identifies the mobile device
token string O An authentication token, which must have the same identity than the one used for subscription. If not specified, the unsubscription is done without authentication.

Receive and parse a wake-up message (sometimes called notification)

When data is written on a path subscribed for wake-up in the [[service]] database, [[service]] sends FCM data messages to wake up your application (see documentation on FCM messages).

For more details please refer to the official FCM documentation.

In "noData" mode

The device is just awaken and can reconnect through [[service]] API to read needed data and resynchronize.

The data payload sent by [[service]] has the following structure:

  • a t (type) field with string value "noData"
  • an app string field containing the [[service]] application name
  • an sp string field containing the subscription path within the [[service]] database

In "value" mode

The data contains the complete new value of the data node at subscription path.

The data payload sent by [[service]] has the following structure:

  • a t (type) field with string value "value"
  • an app string field containing the [[service]] application name
  • an sp string field containing the subscription path within the [[service]] database
  • a d (userData) field with a string value that contains a JSON encoded object (due to FCM limitations) representing the current data at the subscribed path. This field may be omitted if the payload size is too big (see limitations).
  • an s (status) field whose string value can be "OK" or "TooBig" if the payload is too big and userData has been removed (see limitations)

In "childEvent" mode

The data payload sent by [[service]] has the following structure:

  • a t (type) field with string value "childEvent"
  • an app string field containing the [[service]] application name
  • an sp string field containing the subscription path within the [[service]] database
  • an s (status) field whose string value can be "OK" or "TooBig" if the payload is too big and userData has been removed (see limitations)
  • an added field with a string value that contains a JSON encoded object representing the data of the added child nodes. This field may be omitted if the payload size is too big (see limitations).
  • a removed field with a string value that contains a JSON encoded array containing the keys of the removed children. This field may be omitted if the payload size is too big (see limitations).
  • a changed field with a string value that contains a JSON encoded object representing the data of the changed child nodes. This field may be omitted if the payload size is too big (see limitations).

Subscription revocation

When a subscription is revoked on the [[service]] server, a FCM data message is sent to each subscribed device with the following data payload structure:

  • a t (type) field with string value "revoke"
  • a r (reason) string field containing the status code of the revocation cause (see below)
  • an app string field containing the [[service]] application name
  • an sp string field containing the subscription path within the [[service]] database

Revocation causes are:

Status code Description
permission_denied Security rules prevent read access to the data located at the subscribed data node (according to current data and the specified authentication token)
namespace_does_not_exist The [[service]] application database has just been deleted
disabled Mobile wake-up feature has just been disabled for the [[service]] application
expired Subscription has expired
too_big The data located at the subscribed data node is too big and can no longer be subscribed (see weight limitations) because it would result in unreasonable performances

Size limitations

Data payload size limit

The data payload of wake-up messages is limited to 4KB on both FCM and APNs.

More precisely, the whole data payload of a wake-up message is defined by a JSON compact-serialized string. And the length of this string should not be more than 4093 bytes. For example, the following data payload is 114 bytes-long:

{"s":"OK","t":"value","app":"toto","sp":"/some/webcom/path","d":"{\"a\":\"WGj2WtQWVEjQGhQqb7r678xW3PwWmB4Z3zj\"}"}

If the length is 4064 bytes-long or more:

  • in "value" mode: the status field (s) is set to "TooBig" and the userData field (d) is removed
  • in "childEvent" mode:
    • if possible, the status field (s) is set to "OnlyKeys", both added and changed fields contain only keys with true as mapped values,
    • otherwise (the length of the data payload with only keys and true as value is 4064 bytes or more) the status field (s) is set to "TooBig" and added, removed and changed fields are empty.

When data is missing because of payload size limitation, the only way for application to retrieve it is to invoke explicitly read functions of the [[service]] SDK API.

Subscription path length limit

Subscription path length cannot be more than 2048 bytes.

Errors when sending notification to FCM servers

In case of error returned by FCM server. If it is recoverable ("QUOTA_EXCEEDED", "UNAVAILABLE", "INTERNAL", "UNSPECIFIED_ERROR"), it is trying again with exponential backoff starting after 1 second, and during 2 hours. If after that it still fails, the current pending notifcations are discarded but the subscription remains active for future notifications.

In case of other error, the subscription is revoked, and if possible, a "revoke" notification is sent.