Warning: You are viewing an old version (1.1) of this documentation. We recommend you view the latest version 1.2.
REST Client Library API

Presence

Presence enables clients to be aware of other clients that are currently “present” on a channel. Each member present on a channel has a unique self-assigned client identifier and system-assigned connection identifier, along with an optional payload that can be used to describe the member’s status or attributes. Presence allows you to quickly build apps such as chat rooms and multiplayer games by automatically keeping track of who is present in real time across any device.

Presence

Using the Ably REST API it is possible to obtain the set of members currently present on a channel, or obtain the presence history for the channel, if persistence is enabled for that channel.

Since the Ably REST API is stateless, and REST clients do not have realtime connections to the Ably service, it is not possible to enter or leave a channel via the REST API. Find out more about using presence with the Realtime API.

A single clientIdclient_idClientId may be present multiple times on the same channel via different client connections. As far as Ably is concerned, these are different members of the presence set for the channel, however they will be differentiated by their unique connectionIdConnectionIdconnection_id. For example, if a client with ID “Sarah” is connected to a chat channel on both a desktop and a mobile device simultaneously, “Sarah” will be present twice in the presence member set with the same client ID, yet will have two unique connection IDs. A member of the presence set is therefore unique by the combination of the clientIdclient_idClientId and connectionIdConnectionIdconnection_id strings.

If you would prefer to just dive into code and see some examples of how to use presence via the REST API, then we recommend you take a look at our REST tutorials.

Presence states and events

Whenever a member enters or leaves a channel, or updates their member data, a presence event is emitted to all presence subscribers on that channel. Subscribing to presence events makes it incredibly easy to build an app that shows, in real time, any changes to clients connected to Ably and present on a channel.

The following presence events are emitted:

:enterPresenceAction.ENTERAction.ENTEREnterenter
A new member has entered the channel
:leavePresenceAction.LEAVEAction.LEAVELeaveleave
A member who was present has now left the channel. This may be a result of an explicit request to leave or implicitly when detaching from the channel. Alternatively, if a member’s connection is abruptly disconnected and they do not resume their connection within a minute, Ably treats this as a leave event as the client is no longer present
:updatePresenceAction.UPDATEAction.UPDATEUpdateupdate
An already present member has updated their member data. Being notified of member data updates can be very useful, for example, it can be used to update the status of a user when they are typing a message
:presentPresenceAction.PRESENTAction.PRESENTPresentpresent
When subscribing to presence events on a channel that already has members present, this event is emitted for every member already present on the channel before the subscribe listener was registered

View a realtime presence states and events example

Member data

In addition to the clientIdclient_idClientId for members on a channel, it is also possible to include data when entering a channel. Clients can update their data at any point which will be broadcasted to all presence subscribers as a :updatePresenceAction.UPDATEPresenceAction.UpdateAction.UPDATEupdate event.

See the Realtime Presence Member data documentation for more info.

Presence member list

The Presence object exposes a getGet method allowing a client to retrieve an array of all members currently present on the channel. In the REST client library this method directly queries Ably’s REST presence API. No presence state is cached in the library itself, unlike in the Realtime client library.

channel.presence.get(function(err, membersPage) {
  console.log(membersPage.items.length + ' presence members in first page');
  if(membersPage.hasNext()) {
    membersPage.next(function(err, nextPage) { ... });
  }
});
channel.presence.get(function(err, membersPage) {
  console.log(membersPage.items.length + ' presence members in first page');
  if(membersPage.hasNext()) {
    membersPage.next(function(err, nextPage) { ... });
  }
});
members_page = channel.presence.get
puts "#{members_page.items.length} presence members in first page"
if members_page.has_next?
  next_page = members_page.next
end
members_page = channel.presence.get()
print str(len(members_page.items)) + ' members present'
if members_page.has_next():
  next_page = members_page.next()
$membersPage = $channel->presence->get();
echo(count($membersPage->items) . ' presence members in first page');
if ($membersPage->hasNext()) {
  $nextPage = $membersPage.next();
}
PaginatedResult<PresenceMessage> membersPage = channel.presence.get(null);
System.out.println(membersPage.items().length + " members in first page");
if(membersPage.hasNext()) {
  PaginatedResult<PresenceMessage> nextPage = membersPage.next();
  System.out.println(nextPage.items().length + " members on 2nd page");
}
PaginatedResult<PresenceMessage> membersPage = await channel.Presence.GetAsync();
Console.WriteLine(membersPage.Items.Count + " members in first page");
if(membersPage.HasNext)
{
  PaginatedResult<PresenceMessage> nextPage = await membersPage.NextAsync();
  Console.WriteLine(nextPage.Items.Count + " members on 2nd page");
}
[channel.presence get:^(ARTPaginatedResult<ARTPresenceMessage *> *membersPage, ARTErrorInfo *error) {
  NSLog(@"%lu members in first page", [membersPage.items count]);
  if (membersPage.hasNext) {
    [membersPage next:^(ARTPaginatedResult<ARTPresenceMessage *> *nextPage, ARTErrorInfo *error) {
      NSLog(@"%lu members on 2nd page", [nextPage.items count]);
    }];
  }
}];
channel.presence.get { membersPage, error in
  let membersPage = membersPage!
  print("\(membersPage.items.count) in first page")
  if membersPage.hasNext {
    membersPage.next { nextPage, error in
      print("\(nextPage!.items.count) members on 2nd page")
    }
  }
}
page, err := channel.Presence.Get(nil)
fmt.Println("%d messages in first page\n", len(page.PresenceMessages()))
if page.hasNext() {
  page2, err := page.Next()
  fmt.Println("%d messages on 2nd page!\n", len(page2.PresenceMessages()))
}

Batch presence

It is common for the presence of multiple channels to be needed. If you wish to obtain the presence of multiple channels within a single operation, you can make use of the REST batch API.

Presence History

The Presence object exposes a historyHistory method allowing a client to retrieve historical presence events on the channel.

History provides access to instantaneous “live” history as well as the longer term persisted history for presence channels. If persisted history is enabled for the channel, then presence events will typically be stored for 24 – 72 hours. If persisted history is not enabled, Ably retains the last two minutes of presence event history in memory.

The following example retrieves the first two pages of historical presence events published.

var presence = channel.presence;
presence.history(function(err, eventsPage) {
  if(err) {
    console.log('Unable to get presence history; err = ' + err.message);
  } else {
    console.log(eventsPage.items.length + ' presence events received in first page');
    if(eventsPage.hasNext()) {
      eventsPage.next(function(err, nextPage) { ... });
    }
});
var presence = channel.presence;
presence.history(function(err, eventsPage) {
  if(err) {
    console.log('Unable to get presence history; err = ' + err.message);
  } else {
    console.log(eventsPage.items.length + ' presence events received in first page');
    if(eventsPage.hasNext()) {
      eventsPage.next(function(err, nextPage) { ... });
    }
});
events_page = channel.presence.history
puts "#{events_page.items.length} presence events received in first page"
if events_page.has_next?
  next_page = events_page.next
  puts "#{next_page.items.length} presence events received on second page"
end
$eventsPage = $channel->presence->history();
echo(count($eventsPage->items) . ' presence events received in first page');
if ($eventsPage.hasNext()) {
  $nextPage = $eventsPage->next();
  echo(count($nextPage->items) . ' presence events received in second page');
}
events_page = channel.presence.history()
print str(len(events_page.items)) + " presence events received"
if events_page.has_next():
  next_page = events_page.next()
PaginatedResult<PresenceMessage> eventsPage = channel.presence.history(null);
System.out.println(eventsPage.items().length + " presence events received in first page");
if(eventsPage.hasNext()) {
  PaginatedResult<PresenceMessage> nextPage = eventsPage.next();
  System.out.println(nextPage.items().length + " presence events received in 2nd page");
}
PaginatedResult<PresenceMessage> eventsPage = await channel.Presence.HistoryAsync();
Console.WriteLine(eventsPage.Items.Count + " presence events received in first page");
if (eventsPage.HasNext)
{
  PaginatedResult<PresenceMessage> nextPage = await eventsPage.NextAsync();
  Console.WriteLine(nextPage.Items.Count + " presence events received in 2nd page");
}
[channel.presence history:^(ARTPaginatedResult<ARTPresenceMessage *> *eventsPage, ARTErrorInfo *error) {
  NSLog(@"%lu presence events received in first page", [eventsPage.items count]);
  if (eventsPage.hasNext) {
    [eventsPage next:^(ARTPaginatedResult<ARTPresenceMessage *> *nextPage, ARTErrorInfo *error) {
      NSLog(@"%lu presence events received in 2nd page", [nextPage.items count]);
    }];
  }
}];
channel.presence.history { eventsPage, error in
  let eventsPage = eventsPage!
  print("\(eventsPage.items.count) presence events received in first page")
  if eventsPage.hasNext {
    eventsPage.next { nextPage, error in
      print("\(nextPage!.items.count) presence events received in 2nd page")
    }
  }
}
page, err := channel.Presence.History(nil)
fmt.Println("%d messages in first page\n", len(page.PresenceMessages()))
if page.hasNext() {
  page2, err := page.Next()
  fmt.Println("%d messages on 2nd page!\n", len(page2.PresenceMessages()))
}

See the presence history documentation for further details of the supported query parameters.

API Reference

View the Presence API Reference.


Need help?

If you need any help with your implementation or if you have encountered any problems, do get in touch. You can also quickly find answers from our knowledge base, and blog.