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

Channels and Messages

The Ably Realtime service organises the message traffic within applications into named channels. Channels are the “unit” of message distribution; clients attach to channels to subscribe to messages, and every message published to a unique channel is broadcast by Ably to all subscribers. This scalable and resilient messaging pattern is commonly called pub/sub.

Getting started

The Ably Realtime client library provides a straightforward API for publishing and subscribing to messages on a channel. If the channel does not exist at the time the client is attached, a channel will be created in the Ably system immediately.

var realtime = new Ably.Realtime('xVLyHw.6sjaDQ:dw-Cm1SWO8RbwvW7un-skc-csxdKtQFtkzhmcRCbcuY');
var channel = realtime.channels.get('fox-can-log');
channel.subscribe(function(message) {
  alert('Received: ' + message.data);
});
channel.publish('example', 'message data');
var Ably = require('ably');
var realtime = new Ably.Realtime('xVLyHw.6sjaDQ:dw-Cm1SWO8RbwvW7un-skc-csxdKtQFtkzhmcRCbcuY');
var channel = realtime.channels.get('fox-can-log');
channel.subscribe(function(message) {
  console.log("Received: "  message.data);
});
channel.publish("example", "message data");
realtime = Ably::Realtime.new('xVLyHw.6sjaDQ:dw-Cm1SWO8RbwvW7un-skc-csxdKtQFtkzhmcRCbcuY')
channel = realtime.channels.get('fox-can-log')
channel.subscribe do |message|
  puts "Received: #{message.data}"
end
channel.publish 'example', 'message data'
AblyRealtime realtime = new AblyRealtime("xVLyHw.6sjaDQ:dw-Cm1SWO8RbwvW7un-skc-csxdKtQFtkzhmcRCbcuY");
Channel channel = realtime.channels.get("fox-can-log");
channel.subscribe(new MessageListener() {
  @Override
  public void onMessage(Message message) {
    System.out.println("New messages arrived. " + message.name);
  }
});
channel.publish("example", "message data");
AblyRealtime realtime = new AblyRealtime("xVLyHw.6sjaDQ:dw-Cm1SWO8RbwvW7un-skc-csxdKtQFtkzhmcRCbcuY");
IRealtimeChannel channel = realtime.Channels.Get("fox-can-log");
channel.Subscribe(message => {
  Console.WriteLine($"Message: {message.Name}:{message.Data} received");
});
channel.Publish("example", "message data");
ARTRealtime *realtime = [[ARTRealtime alloc] initWithKey:@"xVLyHw.6sjaDQ:dw-Cm1SWO8RbwvW7un-skc-csxdKtQFtkzhmcRCbcuY"];
ARTRealtimeChannel *channel = [realtime.channels get:@"fox-can-log"];
[channel subscribe:^(ARTMessage *message) {
    NSLog(@"Received: %@", message.data);
}];
[channel publish:@"example" data:@"message data"];
let realtime = ARTRealtime(key: "xVLyHw.6sjaDQ:dw-Cm1SWO8RbwvW7un-skc-csxdKtQFtkzhmcRCbcuY")
let channel = realtime.channels.get("fox-can-log")
channel.subscribe { message in
    print("Received: \(message.data)")
}
channel.publish("example", data: "message data")

Channels

In order to publish, subscribe to, or be present on a channel, you must first obtain a channel instance and then attach to that channel. In most instances, as a convenience, it is unnecessary to explicitly attach a channel as it will implicitly attached when performing any operation on the channel such as publishing or subscribing.

Obtaining a channel instance

A Channel object is a reference to a single channel. A channel instance is obtained from the channels collection of the Realtime::ClientRealtimeARTRealtimeAblyRealtime instance, and is uniquely identified by its unicode string name. Find out more about channel naming

var channel = realtime.channels.get('channelName');
var channel = realtime.channels.get('channelName');
Channel channel = realtime.channels.get("channelName");
IRealtimeChannel channel = realtime.Channels.Get("channelName");
channel = realtime.channels.get('channelName')
ARTRealtimeChannel *channel = [realtime.channels get:@"channelName"];
let channel = realtime.channels.get("channelName")

Setting channel options and encryption

A set of channel options may also be passed to configure a channel for encryption. Find out more about symmetric message encryption.

Ably.Realtime.Crypto.generateRandomKey(function(err, key) {
  var options = { cipher: { key: key } };
  var channel = realtime.channels.get('channelName', options);
});
Ably.Realtime.Crypto.generateRandomKey(function(err, key) {
  var options = { cipher: { key: key } };
  var channel = realtime.channels.get('channelName', options);
});
CipherParams params = Crypto.getDefaultParams(key);
ChannelOptions options = new ChannelOptions();
options.encrypted = true;
options.cipherParams = params;
Channel channel = realtime.channels.get("channelName", options);
byte[] key = Crypto.GenerateRandomKey();
CipherParams cipherParams = Crypto.GetDefaultParams(key);
ChannelOptions channelOpts = new ChannelOptions(cipherParams);
IRealtimeChannel encryptedChannel = realtime.Channels.Get("channelName", channelOpts);
key = Ably::Util::Crypto.generate_random_key
options = { cipher: { key: key } }
channel = realtime.channels.get('channelName', options)
NSData *key = [ARTCrypto generateRandomKey];
ARTChannelOptions *options = [[ARTChannelOptions alloc] initWithCipherKey:key];
ARTRealtimeChannel *channel = [realtime.channels get:@"channelName" options:options];
let key = ARTCrypto.generateRandomKey()
let options = ARTChannelOptions(cipherKey: key)
let channel = realtime.channels.get("channelName", options: options)

Channel lifecycle

Channels are not pre-configured or provisioned by Ably in advance; they are created on demand when clients attach, and remain active until such time that there are no remaining attached clients. Within the dashboard for your app however, you can pre-configure one or more channel namespaces (i.e. name prefixes), and associate different attributes and access rights with those namespaces. Find out more about channel namespaces.

The following example explicitly attaches to a channel, which results in the channel being provisioned in Ably’s global realtime cluster. This channel will remain available globally until there are no more clients attached to the channel:

realtime.channels.get('chatroom').attach(function() {
  console.log('"chatroom" exists and is now available globally in every data centre');
});
realtime.channels.get('chatroom').attach(function() {
  console.log('"chatroom" exists and is now available globally in every data centre');
});
realtime.channels.get('chatroom').attach do |channel|
  puts "'chatroom' exists and is now available globally in every data centre"
end
Channel channel = realtime.channels.get("chatroom");
channel.on(new ChannelStateListener() {
  @Override
  public void onChannelStateChanged(ChannelState state, ErrorInfo reason) {
    switch (state.current) {
      case attached: {
        System.out.println("'chatroom' exists and is now available globally");
      }
    }
  }
});
IRealtimeChannel channel = realtime.Channels.Get("chatroom");
channel.Attach((success, error) => {
  Console.WriteLine("'chatroom' exists and is now available globally");
});
[[realtime.channels get:@"chatroom" options:options] attach:^(ARTErrorInfo *error) {
  NSLog(@"'chatroom' exists and is now available globally in every data centre");
}];
realtime.channels.get("chatroom").attach { error in
  print("'chatroom' exists and is now available globally in every data centre")
}

Clients attach to a channel in order to participate on that channel in any way (either to publish, subscribe, or be present on the channel).

Implicit attach

Although the attach operation can be initiated explicitly by a client, it is more common for the client to perform a publish or subscribe operation, and the client library will initiate the attach if the channel is not already attached. The client library allows clients to begin publishing messages to a channel as soon as the channel has been created, and messages are queued until such time as the attach has succeeded or failed.

var channel = realtime.channels.get('chatroom');
channel.subscribe('action', function(message) { // implicit attach
  console.log('Message received '' + message.data);
});
channel.publish('action', 'boom!');
var channel = realtime.channels.get('chatroom');
channel.subscribe('action', function(message) { // implicit attach
  console.log('Message received '' + message.data);
});
channel.publish('action', 'boom!');
channel = realtime.channels.get('chatroom')
channel.subscribe('action') do |message| # implicit attach
  puts "Message received: #{message}";
end
channel.publish 'action', 'boom!'
Channel channel = realtime.channels.get("chatroom");
/* Implicit attach when subscribing */
channel.subscribe(new MessageListener() {
  @Override
  public void onMessage(Message message) {
    System.out.println("Message received: " + message.data);
  }
});
channel.publish("action", "boom!");
IRealtimeChannel channel = realtime.Channels.Get("chatroom");
channel.Subscribe(message => Console.WriteLine("Message received: " + message.Data));
channel.Publish("action", "boom");
ARTRealtimeChannel *channel = [realtime.channels get:@"chatroom" options:options];
[channel subscribe:@"action" callback:^(ARTMessage *message) {
    NSLog(@"Message received: %@", message.data);
}]
[channel publish:@"action" data:@"boom!"];
let channel = realtime.channels.get("chatroom")
channel.subscribe("action") { message in
    print("Message received: \(message.data)")
}
channel.publish("action", data: "boom!")

Channel states

A channel can exist in any of the following states:

initializedInitialized
A Channel object having this state has been initialized but no attach has yet been attempted
attachingAttaching
An attach has been initiated by sending a request to Ably. This is a transient state; it will be followed either by a transition to attached or failed
attachedAttached
Attach has succeeded. In the attached state a client may publish and subscribe to messages, or be present
detachingDetaching
A detach has been initiated on the attached Channel by sending a request to Ably. This is a transient state; it will be followed either by a transition to detached or failed
detachedDetached
The Channel, having previously been attached, has been detached
failedFailed
An indefinite failure condition. This state is entered if a Channel error has been received from the Ably service (such as an attempt to attach without the necessary access rights)

Listening for state changes

The Channel object is an EventEmitter and emits an event whose name is the new state whenever there is a channel state change. As with all events from an EventEmitter in the Ably library, this within the listener function is a reference to an event object whose event property is the name of the event that fired. This allows a listener to listen for all events with a single registration and know, which event fired when it is entered.

channel.on('attached', function() {
  console.log('channel ' + channel.name + ' is now attached');
});
channel.on('attached', function() {
  console.log('channel ' + channel.name + ' is now attached');
});
channel.on(:attached) do |channel_state_change|
  puts "channel #{channel.name} is now attached"
end
channel.on(ChannelState.attached, new ChannelStateListener() {
  @Override
  public void onChannelStateChanged(ChannelState state, ErrorInfo reason) {
    System.out.println("channel " + channel.name + " is now attached");
  }
});
IRealtimeChannel channel = realtime.Channels.Get("chatroom");
channel.On(ChannelState.Attached, args => {
  Console.WriteLine("channel " + channel.Name + " is now attached");
});
[channel on:ARTChannelEventAttached callback:^(ARTErrorInfo *error) {
  NSLog(@"channel %@ is now attached", channel.name);
}];
channel.on(.attached) { error in
  print("channel \(channel.name) is now attached")
}

Alternatively a listener may be registered so that it receives all state change events.

var myListener = function() {
  console.log('channel state is ' + this.event);
});
channel.on(myListener);
var myListener = function() {
  console.log('channel state is ' + this.event);
});
channel.on(myListener);
channel.on do |channel_state_change|
  puts "channel state is #{channel_state_change.current}"
end
channel.on(new ChannelStateListener() {
  @Override
  public void onChannelStateChanged(ChannelState state, ErrorInfo reason) {
    System.out.println("channel state is " + ChannelState.values()[state]);
  }
});
channel.On(args => Console.WriteLine("channel state is " + channel.State));
ARTEventListener *listener = [channel on:^(ARTErrorInfo *error) {
    NSLog(@"channel state is %@", channel.state);
}];
let listener = channel.on { error in
    print("channel state is \(channel.state)")
}

Previously registered listeners can be removed individually or all together.

/* remove the listener registered for a single event */
channel.off('attached', myListener);

/* remove the listener registered for all events */
channel.off(myListener);
/* remove the listener registered for a single event */
channel.off('attached', myListener);

/* remove the listener registered for all events */
channel.off(myListener);
# remove the listener proc registered for a single event
channel.off(:attached, &my_proc)

# remove the listener proc registered for all events
channel.off(&my_proc)
/* remove the listener registered for a single event */
channel.off(ChannelState.attached, channelStateListener);

/* remove the listener registered for all events */
channel.off(channelStateListener);
// remove the listener registered for a single event
channel.Off(ChannelState.Attached, channelStateListener);

// remove the listener registered for all events
channel.Off(channelStateListener);
// remove the listener registered for a single event
[channel off:ARTChannelEventAttached listener:listener];

// remove the listener registered for all events
[channel off:listener];
// remove the listener registered for a single event
channel.off(.attached, listener: listener)

// remove the listener registered for all events
channel.off(listener)

Handling channel failures

Channel attach and detach operations are asynchronous – after initiating an attach request, the client will wait for a response from Ably that confirms that the channel is established on the service and then trigger a state change event.

There are cases where an attach will fail deterministically, such as if the client doesn’t have the necessary rights to access the channel.

realtime.channels.get('private:chatroom').attach(function(err) {
  if (err) {
    console.error('Attach failed: ' + err);
  }
});
realtime.channels.get('private:chatroom').attach(function(err) {
  if (err) {
    console.error('Attach failed: ' + err);
  }
});
promise = realtime.channels.get('private:chatroom').attach
promise.errback do |error|
  puts "Attach failed: #{error}"
end
Channel channel = realtime.channels.get("private:chatroom");
channel.on(new ChannelStateListener() {
  @Override
  public void onChannelStateChanged(ChannelState state, ErrorInfo reason) {
    switch (state.current) {
      case failed: {
        System.out.println("Attach failed: " + reason.message);
      }
    }
  }
});
IRealtimeChannel privateChannel = realtime.Channels.Get("private:chatroom");
privateChannel.Attach((_, error) => {
    if (error != null)
    {
        Console.WriteLine("Attach failed: " + error.Message);
    }
});
[[realtime.channels get:@"private:chatroom"] attach:^(ARTErrorInfo *error) {
    if (error) {
        NSLog(@"Attach failed: %@", error);
    }
}];
realtime.channels.get("private:chatroom").attach { error in
    if let error = error {
        print("Attach failed: \(error)")
    }
}

Connection state change side effects on channels

Once a channel becomes attached, it will remain attached until any of the following occurs:

  • An explicit request to detach the channel is made;
  • The channel moves into the failedFailed state following an unrecoverable error sent from Ably. If for example permissions were revoked for that client on that channel, the channel would move to the failedFailed state;
  • The connection enters the suspendedSuspended, failedFailed or closedClosed state;
  • The connection is not resumed fully as part of the connection state recovery process

Ably provides automatic message continuity for channels, even when connections are disconnected and later resumed. See connection state recovery for more details on how connections and channel state recovery allows you, as a developer, to publish and receive messages without having to worry about changing network conditions. However, there are times when connection recovery is not possible such as being disconnected from Ably for more than two minutes. When this occurs, the channels that are attached can no longer provide guaranteed message continuity. We wanted developers to be aware of this discontinuity, and as such, our client libraries will automatically detach all attached channels that have lost continuity. It is then up to the developer to decide what action to take such as using the history API to retrieve old messages, or simply to reattach the channel and continue if message loss is not important.

For customers who want to ensure that all channels are attached when a connection is available, regardless of whether messages may have been missed, we recommend the following approach:

  • Add a listener on the connection object for all connectedConnected events
  • Reattach all channels you have created. If a channel is already attached, and you call the channel#attachchannel#Attach method, the channel will simply remain attached. It is therefore safe to simply attach all channels every time the connection becomes connected

The following JavaScript example demonstrates a trivial way to ensure all channels are attached when the client becomes connected or the channel becomes detached:

function reattach(channel) {
  channel.attach();
  /* Example when using presence on the channel */
  channel.presence.enter();
}

/* Connection state changes can trigger channels to become detached
   Automatically re-attach channels, nothing will happen if already attached */
client.connection.on('connected', function() {
  for (let channelName in client.channels.all) {
    let channel = client.channels.get(channelName);
    reattach(channel);
  }
});

/* If a channel becomes detached due to channel failure (some edge cases exist)
   You should explicitly re-attach the channel if you are connected */
channel.on('detached', function() {
  if (client.connection.state === 'connected') {
    reattach(channel);
  }
});

Channel namespaces

One or more channel namespaces, or channel name prefixes, may be configured for an app in your dashboard. When a channel is created whose name is prefixed with one of the namespaces, the channel assumes certain configured attributes associated with that namespace. For example, a channel namespace named “private” would match channels named “private”, “private:chat”, “private:chat:mike”.

Namespace-prefixed channel names are delimited by a single colon :; the first component of the channel name (from the start up to and including the last character before the colon) is the namespace. A channel name may validly contain a colon even if the namespace component does not correspond to a namespace; also, a channel may contain multiple colons and only the component up to the first colon will be matched with a namespace. The only restriction on channel names is that a channel name may not start with a colon :, an open square bracket [ and it may not be empty.

Namespaces are defined and configured via the application dashboard settings. The namespace attributes that can be configured are:

  • Persisted messages – If enabled, all messages within this namespace will be stored according to the storage rules for your account. You can access stored messages via the history API
  • Require identification – if enabled, clients will not be permitted to subscribe to matching channels unless they are both authenticated and identified (they have an assigned client ID). Anonymous clients are not permitted to join these channels. Find out more about authenticated and identified clients
  • Require TLS – if enabled, only clients who have connected to Ably over TLS will be allowed to join the channel

Key or token capabilities can also specify access rights based on channel namespace, find out more about authentication

Messages

Each message published has an optional event name propertymemberattribute and a data propertymemberattribute carrying the payload of the message. Various primitive and object types are portably defined and supported in all clients, enabling clients to be interoperable despite being hosted in different languages or environments.

The supported payload types are Strings, JSON objects and arrays, buffers containing arbitrary binary data, and Null objects. Client libraries detect the supplied message payload and encode the message appropriately.

Subscribing to messages

The name propertymemberattribute of published messages does not affect the distribution of a channel message to clients but may be used as a subscription filter, allowing a client to register a listener that only sees a subset of the messages received on the channel. When subscribing, a message listener can subscribe to see all messages on the channel or only a subset whose name matches a given name string.

A client can subscribe to all messages on a channel by passing a listener function to the subscribe method. The listener is passed a Message object for each message received.


A client can register for messages on a channel by implementing MessageListener and calling the subscribe(MessageListener listener) or subscribe(String name, MessageListener listener) method. The listener is passed an array of one or more Message objects when messages are received.


A client can subscribe to all messages on a channel by passing a block to the subscribe method. The block is passed a Message object for each message are received.


A client can subscribe to all messages on a channel by passing a lambda expression to the Subscribe method. The lambda is passed a Message object for each message are received.
channel.subscribe(function(message) {
  console.log('message received for event ' + message.name);
  console.log('message data:' + message.data);
});
channel.subscribe(function(message) {
  console.log('message received for event ' + message.name);
  console.log('message data:' + message.data);
});
channel.subscribe(new MessageListener() {
  @Override
  public void onMessage(Message message) {
    System.out.println("Message received: " + message.data);
  }
});
channel.Subscribe(message =>
{
    Console.WriteLine($"message received for event {message.Name}");
    Console.WriteLine($"message data: {message.Data}");
});
channel.subscribe do |message|
  puts "message received for event #{message.name}"
  puts "message data: #{message.data}"
end
[channel subscribe:^(ARTMessage *message) {
    NSLog(@"message received for event %@", message.name);
    NSLog(@"message data: %@", message.data);
}];
channel.subscribe { message in
    print("message received for event \(message.name)")
    print("message data: \(message.data)")
}

Alternatively a listener may be registered so that it is called only for messages having a specific event name.

channel.subscribe('myEvent', function(message) {
  console.log('message received for event ' + message.name);
  console.log('message data:' + message.data);
});
channel.subscribe('myEvent', function(message) {
  console.log('message received for event ' + message.name);
  console.log('message data:' + message.data);
});
channel.subscribe("myEvent", new MessageListener() {
  @Override
  public void onMessage(Message message) {
    System.out.println("Message received: " + message.data);
  }
});
channel.Subscribe("myEvent", message =>
{
    Console.WriteLine($"message received for event {message.Name}");
    Console.WriteLine($"message data: {message.Data}");
});
channel.subscribe('myEvent') do |message|
  puts "message received for event #{message.name}"
  puts "message data: #{message.data}"
end
channel.subscribe("myEvent") { message in
    print("message received for event \(message.name)")
    print("message data: \(message.data)")
}
[channel subscribe:@"myEvent" callback:^(ARTMessage *message) {
    NSLog(@"message received for event %@", message.name);
    NSLog(@"message data: %@", message.data);
}];

Previously registered listeners can be removed individually or all together.

/* remove the listener registered for a single event */
channel.unsubscribe('myEvent', myListener);

/* remove the listener registered for all events */
channel.unsubscribe(myListener);
/* remove the listener registered for a single event */
channel.unsubscribe('myEvent', myListener);

/* remove the listener registered for all events */
channel.unsubscribe(myListener);
/* remove a single listener */
channel.unsubscribe(myListener);

/* remove the listener registered for all events */
channel.unsubscribe("myEvent", myListener);
/* remove a single listener */
channel.Unsubscribe(myHandler);

/* remove the listener registered for all events */
channel.Unsubscribe("myEvent", myHandler);
# remove the listener proc registered for a single event
channel.unsubscribe("myEvent", &my_proc)

# remove the listener proc registered for all events
channel.unsubscribe(&my_proc)
// remove the listener registered for a single event
[channel unsubscribe:@"myEvent" listener:listener];

// remove the listener registered for all events
[channel unsubscribe:listener];
// remove the listener registered for a single event
channel.unsubscribe("myEvent", listener: listener)

// remove the listener registered for all events
channel.unsubscribe(listener)

Publishing messages

Channels expose a publishPublish method whereby a client can publish either a single message or an array of messages to a channel. A listener optionally passed in to the publishPublish method enables the client to know whether or not the operation succeeded.

channel.publish('event', 'This is my payload', function(err) {
  if(err) {
    console.log('Unable to publish message; err = ' + err.message);
  } else {
    console.log('Message successfully sent');
  }
});
channel.publish('event', 'This is my payload', function(err) {
  if(err) {
    console.log('Unable to publish message; err = ' + err.message);
  } else {
    console.log('Message successfully sent');
  }
});
deferrable = channel.publish('event', 'This is my payload') do
  puts 'Messages successfully sent'
end
deferrable.errback do |err|
  puts "Unable to publish messages; err = #{err}"
end
channel.publish("event", "This is my payload", new CompletionListener() {
  @Override
  public void onError(ErrorInfo reason) {
    System.out.println("Unable to publish message; err = " + reason.message);
  }
  @Override
  public void onSuccess() {
    System.out.println("Message successfully sent");
  }
});
channel.Publish("event", "payload", (success, error) =>
{
  if (error != null) {
    Console.WriteLine("Unable to publish message. Reason: " + error.Message);
  } else {
    Console.WriteLine("Message published successfully");
  }
});
[channel publish:@"event" data:@"This is my payload" callback:^(ARTErrorInfo *error) {
  if (error) {
    NSLog(@"Unable to publish message; err = %@", error.message);
  } else {
    NSLog(@"Message successfully sent");
  }
}];
channel.publish("event", data: "This is my payload") { error in
  if let error = error {
    print("Unable to publish message; err = \(error.message)")
  } else {
    print("Message successfully sent")
  }
}

Channels also expose an async version PublishAsync of the Publish call which resumes execution once the message is confirmed received. It is purely for convenience.

Result result = await channel.PublishAsync("event", "payload");
if(result.IsFailure) {
  Console.WriteLine("Unable to publish message. Reason: " + result.Error.Message);
} else {
  Console.WriteLine("Message published successfully");
}

Retrieving message history

Channels expose a historyHistory method providing a means for clients to obtain messages previously sent on the channel. Channel history can be used to return continuous message history up to the exact point a realtime channel was attached.

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

The following example retrieves the first two pages of historical messages published up until the point the channel was attached.

channel.attach(function() {
  channel.history({ untilAttach: true }, function(err, resultPage) {
    if(err) {
      console.log('Unable to get channel history; err = ' + err.message);
    } else {
      console.log(resultPage.items.length + ' messages received in first page');
      if(resultPage.hasNext()) {
        resultPage.next(function(err, nextPage) { ... });
      }
    }
  });
});
channel.attach(function() {
  channel.history({ untilAttach: true }, function(err, resultPage) {
    if(err) {
      console.log('Unable to get channel history; err = ' + err.message);
    } else {
      console.log(resultPage.items.length + ' messages received in first page');
      if(resultPage.hasNext()) {
        resultPage.next(function(err, nextPage) { ... });
      }
    }
  });
});
Param[] options = new Param[]{ new Param("untilAttach", "true") };
PaginatedResult<Message> resultPage = channel.history(options);
System.out.println(resultPage.items().length + " messages received in first page");
if(resultPage.hasNext()) {
  PaginatedResult<Message> nextPage = resultPage.next();
  System.out.println(nextPage.items().length + " messages received in second page");
}
var history = await channel.HistoryAsync(untilAttach: true);
Console.WriteLine($"{history.Items.Count} messages received in the first page");
if (history.HasNext)
{
  var nextPage = await history.NextAsync();
  Console.WriteLine($"{nextPage.Items.Count} messages received in the second page");
}
channel.attach do
  channel.history(until_attach: true) do |result_page|
    puts "#{result_page.items.length} messages received in first page"
    if result_page.has_next?
      result_page.next { |next_page| ... }
    end
  end
end
[channel attach:^(ARTErrorInfo *error) {
    ARTRealtimeHistoryQuery *query = [[ARTRealtimeHistoryQuery alloc] init];
    query.untilAttach = true;
    [channel history:query callback:^(ARTPaginatedResult<ARTMessage *> *resultPage, ARTErrorInfo *error) {
        NSLog(@"%lu messages received in first page", (unsigned long)[resultPage.items count]);
        if (resultPage.hasNext) {
            [resultPage next:^(ARTPaginatedResult<ARTMessage *> *nextPage, ARTErrorInfo *error) {
                // ...
            }];
        }
    } error:nil];
}];
channel.attach { error in
    let query = ARTRealtimeHistoryQuery()
    query.untilAttach = true
    try! channel.history(query) { resultPage, error in
        let resultPage = resultPage!
        print("\(resultPage.items.count) messages received in first page")
        if resultPage.hasNext {
            resultPage.next { nextPage, error in
                // ...
            }
        }
    }
}

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

Presence

Channels expose a presencePresence member which a client can use to obtain channel presence information and to enter and leave the presence channel itself. See the presence documentation for details.

API Reference

View the Channels and Channel 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.