Push Notifications - Device activation and subscription

Every device that will receive push notifications must activate itself with the local operating system or framework, and hook into the push notification services that the underlying platform provides. This functionality is platform specific and varies considerably across not just platforms, but also across the push services that operate on those platforms such as GCM and FCM, both of which are available on the Android platform.

The Ably client libraries aim to abstract away this complexity and platform specific behaviour by providing a consistent API for device activation, maintenance of the device registration, subscription to Ably channels for receiving push notifications, and of course for receiving the native push notifications themselves.

The client libraries also provide a set of admin functionality that is typically used server-side (in a trusted environment) to manage all devices and push notification delivery and subscriptions. You can find out more in the push admin documentation.

In this section, we will run you through all of the features that a push notification device has available to it.

Prerequisites

The push notification service is currently in beta. Before you get started, please take a look at the push service beta notice.

Before you can configure your devices to receive push notifications, you must first enable push in your Ably app by adding the third party push service credentials and/or certificates to your app push dashboard. These credentials are then used by Ably to authenticate with the respective third party push service (such as APNs) and delivery all queued notifications.

If you have not already done sone, you can sign up for a free account with Apple’s Push Notification service and Google’s Cloud Messaging service.

Platform installation

Whilst platform installation is platform specific, all subsequent Ably API calls are not. Be sure to choose a language above that you wish to see the documentation and code examples in.

Note: To use push notifications you need to use our push-enabled client libraries. Download a push-enabled client library

Before you can activate your push device or receive push notifications, you must first plug in Ably to the underlying OS or platform. Once Ably is plugged in, all subsequent API interactions you have will be with the Ably Realtime library API which is as consistent as possible across all platforms. By providing a consistent API interface across all platforms, we can ensure implementation is simpler and more predictable regardless of the platform you are integrating Ably with.

Installation however is platform specific and as such, instructions for each platform and service is provided below:

Install Ably for Google Cloud Messaging on Android

Within your Android application, you will need to have a class inheriting from IntentService, which your implementation of InstanceIDListenerService invokes on its onTokenRefresh method. We need that class to inherit from AblyRegistrationIntentService and just call its parent’s onHandleIntent method. This method needs you to pass a sender ID (getString(R.string.gcm_defaultSenderId)) and an AblyRealtime instance, configured with the authentication setup and other options you need. Your class should end up looking more or less like this:

public class MyRegistrationIntentService extends AblyRegistrationIntentService {
    @Override
    protected void onHandleIntent(Intent intent) {
        this.onHandleIntent(intent, getString(R.string.gcm_defaultSenderId), getAblyRealtime());
    }

    private static AblyRealtime getAblyRealtime() {
        ClientOptions options = new ClientOptions();
        // Set up options; API key or authCallback, etc.
        // This is most likely shared with other logic in your app to connect to Ably
        try {
            return new AblyRealtime(options);
        } catch (AblyException e) {
            throw new RuntimeException(e);
        }
    }
}

Install Ably for Google Firebase Messaging on Android

Within your Android application, you will need to have a class inheriting from FirebaseInstanceIdService. We need that class to inherit from AblyFirebaseInstanceIdService and just call its parent’s onTokenRefresh method. This method needs you to pass an AblyRealtime instance, configured with the authentication setup and other options you need. Your class should end up looking more or less like this:

public class MyFirebaseInstanceIDService extends AblyFirebaseInstanceIdService {
    @Override
    public void onTokenRefresh() {
        super.onTokenRefresh(getAblyRealtime());
    }

    private static AblyRealtime getAblyRealtime() {
        ClientOptions options = new ClientOptions();
        // Set up options; API key or authCallback, etc.
        // This is most likely shared with other logic in your app to connect to Ably
        try {
            return new AblyRealtime(options);
        } catch (AblyException e) {
            throw new RuntimeException(e);
        }
    }
}

Install Ably for Apple Push Notifications on iOS

You should now have a couple of methods: application(_:didRegisterForRemoteNotificationsWithDeviceToken:)application:didRegisterForRemoteNotificationsWithDeviceToken: and application(_:didFailToRegisterForRemoteNotificationsWithError:)application:​did​Fail​To​Register​For​Remote​Notifications​With​Error:​. ARTPush has two corresponding methods that you should call from yours, passing to them also an ARTRealtime instance, configured with the authentication setup and other options you need.

// In your UIApplicationDelegate class:
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    [ARTPush didRegisterForRemoteNotificationsWithDeviceToken:deviceToken realtime:[self getAblyRealtime]];
}

- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error;
    [ARTPush didFailToRegisterForRemoteNotificationsWithError:error realtime:[self getAblyRealtime]];
}

- (ARTRealtime *)getAblyRealtime {
    ARTClientOptions *options = [[ARTClientOptions alloc] init];
    // Set up options; API key or auth URL, etc.
    return [[ARTRealtime alloc] initWithOptions: options];
}
// In your UIApplicationDelegate class:
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    ARTPush.didRegisterForRemoteNotifications(withDeviceToken: deviceToken, realtime: self.getAblyRealtime())
}

func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
    ARTPush.didFailToRegisterForRemoteNotificationsWithError(error, realtime: self.getAblyRealtime())
}

func getAblyRealtime() -> ARTRealtime {
    var options = ARTClientOptions()
    // Set up options; API key or auth URL, etc.
    return ARTRealtime(options: options)
}

Activating push on your device

Activating a device for push notifications and registering it with Ably is commonly performed entirely from the device. However, it is possible to separate the concerns such that activation with the underlying platform is performed on the device, and registration of that activated device with Ably is performed using your own servers. This latter pattern is more commonly used when you want to minimize the capabilities assigned to an untrusted device. Find out how to register the device from your servers

In the following example, we will both activate the device with the underlying platform and register the device with Ably from the device itself.

Activate the device for push with push.activate

If you want to to start receiving push notifications from Ably (e. g. from your main activityyour UIApplicationDelegate), you need to first call AblyRealtime.push.activateARTRealtime.push.activate which will register the device for push by doing the following on your behalf:

  • Ensure the Ably client is authenticated
  • Generate a unique identifier for this device and store this in local storage
  • Activate the device for push notifications with the underlying OS or platform and obtain a unique identifier for the device as a push recipient. For example, in GCM this is described as a registration token, and in APNs this is described as a device token
  • Register the local device with Ably using the device’s unique identifier, platform specific details such as form factor and OS, and the push recipient details to receive push notifications. This in turns ensure Ably can reach this device and deliver push notifications
  • Store the updateToken provided in the response from Ably in local storage so that subsequent requests to Ably to update push recipient details are secure

Please note that the effects of calling activate outlive the current process. Once called, the device will stay registered even after the application is closed, and up until deactivate is called. activate is idempotent: calling it again when device is already activated has the sole effect of calling its callback.

AblyRealtime ably = getAblyRealtime();
ably.push.activate(context);
ARTRealtime *ably = [self getAblyRealtime];
[ably.push activate];
let ably = self.getAblyRealtime()
ably.push.activate()

Please bear in mind that in order for the client to register itself automatically with Ably, it needs to be authenticated and have the required push-subscribe capability. If you would prefer to delegate registration of the push device to your own servers and not allow devices to register themselves directly with Ably, then see the section how to register devices from your server.

Register for callback from activate

Once activate is called, the aforementioned activation and registration process kicks off in the background. Once completed, a callback will be invoked if registered. We recommend you set this up callback so that you will be notified when push activation has succeeded or failed. Once the device has successfully been activated, you can then start subscribing for push notifications on channels and receiving push notifications via Ably.

When the activation process is completed, Ably will send a broadcast through the application’s LocalBroadcastManager. You should listen for a broadcast with action io.ably.broadcast.PUSH_ACTIVATEcall your application’s (void)didActivateAblyPush:(nullable ARTErrorInfo *)errordidActivateAblyPush(error: ARTErrorInfo?) method as follows:

LocalBroadcastManager.getInstance(context.getApplicationContext()).registerReceiver(new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        ErrorInfo error = IntentUtils.getErrorInfo(intent);
        if (error != null) {
            // Handle error
            return;
        }
        // Subscribe to channels / listen for push etc.
    }
}, new IntentFilter("io.ably.broadcast.PUSH_ACTIVATE"));

ably.push.activate(context);
// Add a delegate method to your UIApplicationDelegate class:
- (void)didActivateAblyPush:(nullable ARTErrorInfo *)error {
    if (error) {
        // Handle error
        return;
    }
    // Subscribe to channels / listen for push etc.
}

// Call activate, which will call the delegate method when done:
[ably.push activate];
// Add a delegate method to your UIApplicationDelegate class:
func didActivateAblyPush(error: ARTErrorInfo?) {
    if let error = error {
        // Handle error
        return
    }
    // Subscribe to channels / listen for push etc.
}

// Call activate, which will call the delegate method when done:
ably.push.activate()

Subscribe for push notifications

Before you subscribe to a channel for push, make sure its a channel rules is configured to explicitly enable push notifications. By default, push notifications on channels are disabled.

There are two ways a device can be subscribed to a channel: directly by its device ID, or indirectly by its associated client ID.

Subscribing by device ID

A device ID uniquely identifies a device within Ably’s services and is assigned automatically at the time the device is activated.

If your client has the push-subscribe capabilities, you can do the following:

[[realtime.channels get:@"pushenabled:foo"].push subscribeDevice:^(ARTErrorInfo *_Nullable error) {
    // Check error.
}];
realtime.channels.get("pushenabled:foo").subscribeDevice { error
    // Check error.
}
realtime.channels.get("pushenabled:foo").push.subscribeDevice(context);

// or

realtime.channels.get("pushenabled:foo").push.subscribeDeviceAsync(context, new CompletionListener() {
    @Override
    public void onSuccess() {}

    @Override
    public void onError(ErrorInfo errorInfo) {
        // Handle error.
    }
});

If your client doesn’t have the push-subscribe permissions, you should communicate the device ID to your server so that it can subscribe on the device’s behalf. You can find your unique device ID at ARTRealtime.device.idAblyRealtime.device().id. The server must then use the push admin API to subscribe the device.

Subscribing by client ID

When a device is registered, it can be associated with a client ID. AblyRealtime.push.activate takes the client ID from the AblyRealtime instance.

You can subscribe all devices associated with a client ID to a channel in a single operation i.e. a subscription by client ID. New device registrations associated to that client ID will also be subscribed to the channel, and if a device registration is no longer associated with that client ID, it will also stop being subscribed to the channel (unless it’s also subscribed directly by device ID).

To subscribe your AblyRealtime instance’s client ID to a channel:

[[realtime.channels get:@"pushenabled:foo"].push subscribeClient:^(ARTErrorInfo *_Nullable error) {
    // Check error.
}];
realtime.channels.get("pushenabled:foo").subscribeClient { error
    // Check error.
}
realtime.channels.get("pushenabled:foo").push.subscribeClient();

// or

realtime.channels.get("pushenabled:foo").push.subscribeClientAsync(new CompletionListener() {
    @Override
    public void onSuccess() {}

    @Override
    public void onError(ErrorInfo errorInfo) {
        // Handle error.
    }
});

Alternatively, if you want to subscribe a different client ID not currently associated with the currently authenticated realtime instance, you can use the admin API.

Push capabilities

These are the capabilities necessary to perform push operations:

  • push-subscribe: Register new devices, deregister them, and subscribe and unsubscribe them to channels for push notifications.
  • push-admin: Register, update and deregister any device registration, and subscribe and unsubscribe to channels for push notifications. Publish push notification using the POST /push/publish endpoint (AblyRealtime.push.admin.publish method).

Typically, client devices subscribing for push will either have push-subscribe privileges or delegate operations to a server with push-admin privileges.

Activating devices from your server

The default for AblyRealtime.push.activate is to register the device with Ably directly from the device, but you can instead delegate that to your server.

For this, your UIApplicationDelegate must implement these methods:

- (void)ablyPushCustomRegister:(nullable ARTErrorInfo *)error deviceDetails:(nullable ARTDeviceDetails *)deviceDetails
                                callback:(void (^ _Nonnull)(ARTUpdateToken * _Nonnull, NSError * _Nullable))callback {
    if (error) {
        // Handle error.
        return;
    }

    [self registerThroughYourServer:deviceDetails callback:callback];
}

- (void)ablyPushCustomDeregister:(nullable ARTErrorInfo *)error deviceId:(nullable ARTDeviceId *)deviceId
                                  callback:(void (^ _Nullable)(NSError * _Nullable))callback {
    if (error) {
        // Handle error.
        return;
    }

    [self deregisterThroughYourServer:deviceId callback:callback];
}

For this, you need to communicate back and forth with the Ably library via the application’s LocalBroadcastManager.

First, make sure you pass true as activate ’s useCustomRegisterer (and deactivate ’s useCustomDeregisterer) parameter.

ably.push.activate(context, true);
ably.push.deactivate(context, true);

The Ably library will then broadcast a io.ably.broadcast.PUSH_REGISTER_DEVICE action when it needs you to register from your server, and io.ably.broadcast.PUSH_DEREGISTER_DEVICE when it needs you to deregister. You must configure a listener to those actions in your application’s AndroidManifest.xml, and from it answer back with a PUSH_UPDATE_TOKEN or PUSH_DEVICE_DEREGISTERED, like this:

<receiver android:name=".MyAblyBroadcastReceiver" >
  <intent-filter>
     <action android:name="io.ably.broadcast.PUSH_REGISTER_DEVICE" />
     <action android:name="io.ably.broadcast.PUSH_DEREGISTER_DEVICE" />
  </intent-filter>
</receiver>
public class MyAblyBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        AblyRealtime ably = getAblyRealtime();
        String action = intent.getAction();

        if (action.equals("io.ably.broadcast.PUSH_REGISTER_DEVICE")) {
            DeviceDetails device = ably.device(context);
            boolean isNew = intent.getBooleanExtra("isNew", false);

            Intent response = new Intent();

            try {
                String updateToken = registerThroughYourServer(device, isNew);
                response.putExtra("updateToken", updateToken);
            } catch(AblyException e) {
                IntentUtils.addErrorInfo(intent, e.errorInfo);
            }

            LocalBroadcastManager.getInstance(context.getApplicationContext()).sendBroadcast(intent);
        } else if (action.equals("io.ably.broadcast.PUSH_REGISTER_DEVICE")) {
            DeviceDetails device = ably.device(context);

            Intent response = new Intent();

            try {
                deregisterThroughYourServer(device.id);
            } catch(AblyException e) {
                IntentUtils.addErrorInfo(intent, e.errorInfo);
            }

            LocalBroadcastManager.getInstance(context.getApplicationContext()).sendBroadcast(intent);
        }
    }
}

API reference
Documentation

Get started now with our free plan

It includes 3m messages per month, 100 peak connections, 100 peak channels, and loads of features.

Create your free account