Push Notifications - Publishing Notifications

Ably provides two models for delivering push notifications to devices. Channel-based broadcasting provides automatic fan-out capabilities for push notifications, and direct publishing provides a means to deliver individual notifications to devices.

Channel-based broadcasting

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

The model for delivering push notifications to devices over channels is intentionally very similar to how “normal” messages are delivered using Ably’s pub/sub channel. For example, a “normal” message published on an Ably channel is broadcasted immediately to all subscribers of that channel. When broadcasting push notifications on channels however, the process is the same with the exception that the subscribers (devices receiving push notifications) are registered in advance using our API and the message itself must contain an extra push notification payload that specifies the optional visual format and optional data payload of the native push notification.

Therefore, the process for delivering push notifications to devices using channel-based broadcasting is as follows:

  1. Subscribe one or more devices to one or more channels
  2. Publish a message on those channels with a push notification payload

Please note that a push notification published on a channel will only be delivered to a device if:

  • the extra push notification payload is included in the published message
  • a channel rule is configured explicitly enabling push notifications on that channel
  • the device is subscribed to the channel
  • the push notification payload is compatible with the subscribed push notification device

Channel-based push notification example

Push notifications are sent as special payloads alongside a normal Ably message in the extras field. The extras field is an object and must contain a push attribute object with the push payload details.

ARTMessage *message = [[ARTMessage allocWithName:@"example" data:@"realtime data"]];
message.extras = @{
    @"push": @{
        @"notification": @{
            @"title": @"Hello from Ably!",
            @"body": @"Example push notification from Ably."
        },
        @"data": @{
            @"foo": @"bar",
            @"baz": @"qux"
        }
    }
};
[[realtime.channels get:@"pushenabled:foo"] publish:@[message] ^(ARTErrorInfo *_Nullable error) {
    // Check error.
}];
var message = ARTMesssage(name: "example", data: "realtime data")
message.extras = [
    "push": [
        "notification": [
            "title": "Hello from Ably!",
            "body": "Example push notification from Ably."
        ],
        "data": [
            "foo": "bar",
            "baz": "qux"
        ]
    ]
]
realtime.channels.get("pushenabled:foo").publish([message]) { error
    // Check error.
}
Message message = new Message("example", "realtime data");
message.extras = io.ably.lib.util.JsonUtils.object()
    .add("push", io.ably.lib.util.JsonUtils.object()
        .add("notification", io.ably.lib.util.JsonUtils.object()
            .add("title", "Hello from Ably!")
            .add("body", "Example push notification from Ably."))
        .add("data", io.ably.lib.util.JsonUtils.object()
            .add("foo", "bar")
            .add("baz", "qux")));

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

    @Override
    public void onError(ErrorInfo errorInfo) {
        // Handle error.
    }
});
extras = {
  push: {
    notification: {
      title: 'Hello from Ably!',
      body: 'Example push notification from Ably.'
    },
    data: {
      foo: 'bar',
      baz: 'qux'
    }
  }
}

channel = realtime.channels.get('pushenabled:foo')
channel.publish('example', 'data', extras: extras) do
  # message published successfully
end
var extras = {
  push: {
    notification: {
      title: 'Hello from Ably!',
      body: 'Example push notification from Ably.'
    },
    data: {
      foo: 'bar',
      baz: 'qux'
    }
  }
};

var channel = realtime.channels.get('pushenabled:foo');
channel.publish({ name: 'example', data: 'data', extras: extras }, function(err) {
  if (err) {
    console.log('Unable to publish message with push notification; err = ' + err.message);
    return;
  }
  console.log('Message with push notification published');
});
var extras = {
  push: {
    notification: {
      title: 'Hello from Ably!',
      body: 'Example push notification from Ably.'
    },
    data: {
      foo: 'bar',
      baz: 'qux'
    }
  }
};

var channel = realtime.channels.get('pushenabled:foo');
channel.publish({ name: 'example', data: 'data', extras: extras }, function(err) {
  if (err) {
    console.log('Unable to publish message with push notification; err = ' + err.message);
    return;
  }
  console.log('Message with push notification published');
});

Direct publishing

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

Ably provides a REST API that allows native push notifications to be delivered directly to:

  • Devices identified by their unique device ID
  • Devices identified by their assigned clientId
  • Devices identified by their native recipient attributes such as their unique registrationToken in the case of GCM, deviceToken in the case of APNS, or targetUrl and encryptionKey in the case of a Web device. This means is particularly useful when migrating to Ably with existing push notification target devices.

See the push admin publish documentation for the client library API details, and the raw push publish REST API documentation for information on the underlying direct publishing endpoint used by the client libraries.

Publish to a device ID example

var recipient = {
  device_id: 'xxxxxxxxxxx'
};
var notification = {
  notification: {
    title: 'Hello from Ably!'
  }
};

realtime.push.admin.publish(recipient, data, function(err) {
  if (err) {
    console.log('Unable to publish push notification; err = ' + err.message);
    return;
  }
  console.log('Push notification published');
});
var recipient = {
  device_id: 'xxxxxxxxxxx'
};
var notification = {
  notification: {
    title: 'Hello from Ably!'
  }
};

realtime.push.admin.publish(recipient, data, function(err) {
  if (err) {
    console.log('Unable to publish push notification; err = ' + err.message);
    return;
  }
  console.log('Push notification published');
});
recipient = {
  device_id: 'xxxxxxxxxxx'
}
notification = {
  notification: {
    title: 'Hello from Ably!'
  }
}

realtime.push.admin.publish(recipient, data) do
  # push notification published
end

Publish to a client ID example

var recipient = {
  client_id: 'bob'
};
var notification = {
  notification: {
    title: 'Hello from Ably!'
  }
};

realtime.push.admin.publish(recipient, data, function(err) {
  if (err) {
    console.log('Unable to publish push notification; err = ' + err.message);
    return;
  }
  console.log('Push notification published');
});
var recipient = {
  client_id: 'bob'
};
var notification = {
  notification: {
    title: 'Hello from Ably!'
  }
};

realtime.push.admin.publish(recipient, data, function(err) {
  if (err) {
    console.log('Unable to publish push notification; err = ' + err.message);
    return;
  }
  console.log('Push notification published');
});
recipient = {
  client_id: 'bob'
}
notification = {
  notification: {
    title: 'Hello from Ably!'
  }
}

realtime.push.admin.publish(recipient, data) do
  # push notification published
end

Publish direct to a native recipient example

recipient = {
  transport_type: 'apns',
  device_token: 'xxxxxxxxxx'
}
notification = {
  notification: {
    title: 'Hello from Ably!'
  }
}

realtime.push.admin.publish(recipient, data) do
  # push notification published
end
var recipient = {
  transport_type: 'apns',
  device_token: 'xxxxxxxxxx'
};
var notification = {
  notification: {
    title: 'Hello from Ably!'
  }
};

realtime.push.admin.publish(recipient, data, function(err) {
  if (err) {
    console.log('Unable to publish push notification; err = ' + err.message);
    return;
  }
  console.log('Push notification published');
});
var recipient = {
  transport_type: 'apns',
  device_token: 'xxxxxxxxxx'
};
var notification = {
  notification: {
    title: 'Hello from Ably!'
  }
};

realtime.push.admin.publish(recipient, data, function(err) {
  if (err) {
    console.log('Unable to publish push notification; err = ' + err.message);
    return;
  }
  console.log('Push notification published');
});

Push payload structure

A push notification payload has a generic structure as follows:

{
  "notification": {
    "title": <string, title to display at the notification>,
    "body": <string, text below title on the expanded notification>,
    "icon": <string, platform-specific>,
    "sound": <string, platform-specific>,
    "collapseKey": <string, platform-specific, used to group notifications together>
  },
  "data": {
    <key string>: <value string>,
    ...
  }
}

Depending on the transport (APNs, F/GCM, etc.), the following transformations are made automatically by Ably to make each field compatible with the target push notification transport:

Ably field F/GCM APNs Web
notification.title notification.title aps.alert.title notification.title
notification.body notification.body aps.alert.body notification.body
notification.icon notification.icon Discarded. notification.icon
notification.sound notification.sound aps.alert.sound notification.sound
notification.collapseKey collapse_key aps.thread-id notification.collapseKey
data data Merged into root object. data

So for example, a push payload in a message published to Ably as follows:

{
  "notification": {
    "collapseKey": "chat"
  }
}

would be delivered in raw format to GCM as:

{
  "collapse_key": "chat"
}

and would be delivered in raw format to APNs as:

{
  "aps.thread-id": "chat"
}

and would be delivered in raw format to a web push target as:

{
  "notification": {
    "collapseKey": "chat"
  }
}

Additionally, you can set transport-specific attributes which will get merged into the root object resulting from generic mapping explained above only when pushing to the selected transport. This way, you can:

  • Override the generic value for that field.
  • Add specific fields that are not supported in the generic structure.

To do this, alongside notification and data, add an object whose field is one of:

  • fcm, for FCM and GCM.
  • apns, for APNs.
  • web, for Web Notifications.

Here’s an example of push payload that overrides the default title for APNs iOS and sets the FCM Android-specific color field:

{
  "notification": {
    "title": "Hello from Ably!",
    "body": "Example push notification from Ably."
  },
  "data": {
    "foo": "bar",
    "baz": "qux"
  },
  "apns": {
    "aps": {
      "alert": {
        "title": "Hello to iOS from Ably!"
      }
    }
  },
  "fcm": {
    "notification": {
      "color": "#d3d3d3"
    }
  }
}

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