Channels and Messages

In a Programmable Chat application, a Channel is where all the action happens. Whether a chat is between two users or two hundred, a Channel is where messages are sent, received, and archived for later viewing by offline clients.

Programmable Chat has two objects for working with channels - Channel Descriptors and Channels. A Channel Descriptor is a lightweight representation of the key attributes of a public or private channel. When requesting a list of channels, you will receive Channel Descriptors that serve as a snapshot in time for each channel. These channel attributes will not update if modified remotely, so you should use the descriptors only to assist your user in discovering and joining a channel.

Channel Descriptors can be used to obtain a full channel object or view the following information:

  • Channel SID
  • Friendly Name
  • Unique Name
  • Date Created
  • Created By
  • Date Updated
  • Channel Attributes
  • Messages and Members Count
  • Last Consumed Message Index (is available)
  • Status (if available)
  • Type (private or public)

A full Channel object allows you to join and interact with the channel. Let's dive into a few of the key techniques you'll need to employ while working with channels and messages in your application.

List Public Channel Descriptors

If your programmable chat instance has public channels that are discoverable by a user, you can fetch a list of Channel Descriptors for all public channels in the system:

Loading Code Samples...
Language
chatClient.getChannels().getPublicChannelsList(new CallbackListener<Paginator<ChannelDescriptor>>() {
  @Override
  public void onSuccess(Paginator<ChannelDescriptor> channelPaginator) {
    for (ChannelDescriptor channel : channelPaginator.getItems()) {
      Log.d(TAG, "Channel named: " + channel.getFriendlyName());
    }
  }
});
chatClient.getPublicChannelDescriptors().then(function(paginator) {
  for (i=0; i<paginator.items.length; i++) {
    var channel = paginator.items[i];
    console.log('Channel: ' + channel.friendlyName);
  }
});    
[[self.client channelsList] publicChannelDescriptorsWithCompletion:^(TCHResult *result, 
  TCHChannelDescriptorPaginator *paginator) {
  if ([result isSuccessful]) {
    for (TCHChannelDescriptor *channel in paginator.items) {
      NSLog(@"Channel: %@", channel.friendlyName);
    }
  }
}];
client.channelsList().publicChannelDescriptorsWithCompletion({ (result, paginator) in
  if (result.isSuccessful()) {
    for channel in paginator.items() {
        print("Channel: \(channel.friendlyName)")
    }
  }
})
List Public Channels

List User Channel Descriptors

If your programmable chat instance has user channels that the current user belongs to, you can fetch a list of User Channel Descriptors for this user which . To list public channels that have not yet been joined, see list public channels above.

Loading Code Samples...
Language
chatClient.getChannels().getUserChannelsList(new CallbackListener<Paginator<ChannelDescriptor>>() {
  @Override
  public void onSuccess(Paginator<ChannelDescriptor> channelPaginator) {
    for (ChannelDescriptor channel : channelPaginator.getItems()) {
      Log.d(TAG, "Channel named: " + channel.getFriendlyName());
    }
  }
});
chatClient.getUserChannelDescriptors().then(function(paginator) {
  for (i=0; i<paginator.items.length; i++) {
    var channel = paginator.items[i];
    console.log('Channel: ' + channel.friendlyName);
  }
});    
[[self.client channelsList] userChannelDescriptorsWithCompletion:^(TCHResult *result, 
  TCHChannelDescriptorPaginator *paginator) {
  if ([result isSuccessful]) {
    for (TCHChannelDescriptor *channel in paginator.items) {
      NSLog(@"Channel: %@", channel.friendlyName);
    }
  }
}];
client.channelsList().userChannelDescriptorsWithCompletion({ (result, paginator) in
  if (result.isSuccessful()) {
    for channel in paginator.items() {
        print("Channel: \(channel.friendlyName)")
    }
  }
})
List the Channels for a User
List User Channels

List the Channels for a User

Get A Channel from a Channel Descriptor

After you have retrieved a list of channel descriptors, you will typically want to get a specific Channel and interact with it directly. In the following example, you can see how. Note that this is not necessary for the Javascript Chat SDK as the client.getChannel() gets the channel descriptor and instantiates the channel internally for you.

Loading Code Samples...
Language
channelDescriptor.getChannel(new CallbackListener<Channel>() {
    @Override
    public void onSuccess(Channel channel) {
      Log.d(TAG, "Channel Status: " + channel.getStatus());
    }
});
[channelDescriptor channelWithCompletion:^(TCHResult *result, TCHChannel *channel) {
  if([result isSuccessful]) {
    NSLog(@"ChannelStatus: %@", channel.status);
  }
}]
channelDescriptor?.channel(completion:{ (result, channel) in
  if result!.isSuccessful() {
    print("Channel Status: \(String(describing: channel?.status))")
  }
})
Get A Channel from a Channel Descriptor

Create a Channel

Before you can start sending messages, you first need a Channel that can receive messages. Here is how you create a channel.

Loading Code Samples...
Language
mChatClient.getChannels().channelBuilder().
  .withFriendlyName("general")
  .withType(Channel.ChannelType.PUBLIC)
  .build(new CallbackListener<Channel>() {
      @Override
      public void onSuccess(Channel channel) {
          if (channel != null) {
              Log.d(TAG,"Success creating channel");
          }
      }

      @Override
      public void onError(ErrorInfo errorInfo) {
          Log.e(TAG,"Error creating channel: " + errorInfo.getErrorText());
      }
  });
// Create a Channel
chatClient
  .createChannel({
    uniqueName: 'general',
    friendlyName: 'General Chat Channel',
  })
  .then(function(channel) {
    console.log('Created general channel:');
    console.log(channel);
  });
// Create the general channel (for public use) if it hasn't been created yet
NSDictionary *options = @{
                         TCHChannelOptionFriendlyName: @"General Chat Channel",
                         TCHChannelOptionType: @(TCHChannelTypePublic)
                         };
[channels createChannelWithOptions:options completion:^(TCHResult *result, TCHChannel *channel) {
    if([result isSuccessful]) {
        NSLog(@"Channel created.");
    } else {
        NSLog(@"Channel NOT created.");
    }
}];
let options = [
    TCHChannelOptionFriendlyName: "General Channel",
    TCHChannelOptionType: TCHChannelType.public.rawValue
] as [String : Any]
client?.channelsList().createChannel(options: options, completion: { channelResult, channel in
    if (channelResult?.isSuccessful())! {
        print("Channel created.")
    } else {
        print("Channel NOT created.")
    }
})
Create a Channel

Join a Channel

Once you've created a channel, a user must join it to begin receiving or sending messages on that channel.

Loading Code Samples...
Language
private void joinChannel(final Channel channel) {
    Log.d(TAG, "Joining Channel: " + channel.getUniqueName());
    channel.join(new StatusListener() {
        @Override
        public void onSuccess() {
            Log.d(TAG, "Joined channel");
        }

        @Override
        public void onError(ErrorInfo errorInfo) {
            Log.e(TAG,"Error joining channel: " + errorInfo.toString());
        }
    });
}
// Join a previously created channel
client.on('channelJoined', function(channel) {
  console.log("Joined channel " + channel.friendlyName);
});

myChannel.join().catch(function(err) {
  console.error("Couldn't join channel " + channel.friendlyName + ' because ' + err);
});
// Where "client" is an authenticated client obtained with
// [TwilioChatClient chatClientWithToken:properties:delegate:]
// and the callback for chatClient:synchronizationStatusChanged: has been called with TCHClientSynchronizationStatusChannelsListCompleted
TCHChannel *channel = [[self.client channelsList] channelWithUniqueName:@"general"];
if (channel) {
    [channel joinWithCompletion:^(TCHResult *result) {
        if ([result isSuccessful]) {
            NSLog(@"Channel joined.");
        } else {
            NSLog(@"Channel NOT joined.");
        }
    }];
}
// Where "client" is an authenticated client obtained with
// TwilioChatClient(token:, properties:, delegate:)
// and the callback for chatClient(client:, synchronizationStatusChanged:) has been called with .completed
let channel = client.channelsList().channel(withSidOrUniqueName:"general")
if let channel = channel {
    channel.join(completion: { channelResult in
        if channelResult.isSuccessful() {
            print("Channel joined.")
        } else {
            print("Channel NOT joined.")
        }
    }
}
Join a Channel

Send Messages to a Channel

Once you're a member of a channel, you can send a message to it.

A message is a bit of data that is sent first to the Twilio backend where it is stored for later access by members of the channel. The message is then pushed out in real time to all channel members currently online. Only users subscribed to your channel will receive your messages.

Loading Code Samples...
Language
channel.getMessages().sendMessage("test", new StatusListener() {
    @Override
    public void onSuccess() {
      Log.d(TAG,"Message sent successfully");
    }

    @Override
    public void onError(ErrorInfo errorInfo) {
        Log.e(TAG,"Error sending message: " + errorInfo.toString());
    }
});
// Send a message to a previously created Channel
var msg = $('#chat-input').val();
myChannel.sendMessage(msg);
TCHMessageOptions *options = [[TCHMessageOptions new] withBody:@"test"];
[self.channel.messages sendMessageWithOptions:options completion:^(TCHResult *result, TCHMessage *message) {
    if ([result isSuccessful]) {
        NSLog(@"Message sent.");
    } else {
        NSLog(@"Message NOT sent.");
    }
}];
// Where "channel" is a TCHChannel
if let messages = channel.messages {
  let options = TCHMessageOptions().withBody("test")
  messages.sendMessage(with: options) { result, message in
    if result.isSuccessful() {
      print("Message sent.")
    } else {
      print("Message NOT sent.")
    }
  }
}
Sending Messages

Today, a message is just a string of text. Available in beta you can also send other media types, like images and binary data.

Get the Most Recent Messages from a Channel

With a channel object in hand, you can fetch the most recent messages from the channel. Use this to provide history within the channel. You can choose how many messages you want to retrieve.

Loading Code Samples...
Language
  channel.getMessages().getLastMessages(50, new CallbackListener<List<Message>>() {
    @Override
    public void onSuccess(List<Message> messages) {
      for (Message message : messages) {
        Log.d(TAG, "Message Body: " + message.getMessageBody());
      }
    }
  });
// Get Messages for a previously created channel
channel.getMessages().then(function(messages) {
  const totalMessages = messages.items.length;
  for (i = 0; i < totalMessages; i++) {
    const message = messages.items[i];
    console.log('Author:' + message.author);
  }
  console.log('Total Messages:' + totalMessages);
});
[self.channel.messages getLastMessagesWithCount:100 
  completion:^(TCHResult *result, NSArray<TCHMessage *> *messages) {
  for (TCHMessage *message in messages) {
    NSLog(@"Message body: %@", message.body);
  }
}];
channel.messages.getLastMessagesWithCount(100, completion: { (result, messages) in
  for message in messages {
    print("Message body: \(message.body)")
  }
})
Get Most Recent Messages

You can also be notified of any new incoming messages with an event handler. Use this handler to update your user interface to display new messages.

Loading Code Samples...
Language
private ChannelListener mDefaultChannelListener = new ChannelListener() {
  @Override
  public void onMessageAdded(final Message message) {
    Log.d(TAG, "Message added: " + message.getMessageBody());
  }
}
// Listen for new messages sent to a channel
myChannel.on('messageAdded', function(message) {
  console.log(message.author, message.body);
});
- (void) chatClient:(TwilioChatClient *)client channel:(TCHChannel *)channel 
    messageAdded:(TCHMessage *)message {
    
  NSLog(@"%@ said: %@", message.author, message.body);
}
extension ViewController: TwilioChatClientDelegate {
    // Called whenever a channel we've joined receives a new message
    func chatClient(client: TwilioChatClient!, channel: TCHChannel!,
                    messageAdded message: TCHMessage!) {
        print("\(message.author) said: \(message.body)")
    }
}
Listening for New Messages

Invite other Users to a Channel

Sometimes you might feel lonely in a channel. Rather than sending messages to yourself, invite a friend to come and chat! It doesn't matter if the channel is public or private - you are always able to invite another user to any channel you own.

Loading Code Samples...
Language
channel.getMembers().inviteByIdentity("Juan", new StatusListener() {
  @Override
  public void onSuccess() {
    Log.d(TAG,"User Invited!");
  }
});
// Invite another member to your channel
myChannel.invite('elmo').then(function() {
  console.log('Your friend has been invited!');
});
[self.channel.members inviteByIdentity:@"Juan" completion:^(TCHResult *result) {
    if([result isSuccessful]) {
        NSLog(@"User invited.");
    } else {
        NSLog(@"User NOT invited.");
    }
}];
channel.members.inviteByIdentity("Juan") { result in
    if result.isSuccessful() {
        print("User invited.")
    } else {
        print("User NOT invited.")
    }
}
Sending an Invite

Accept an Invitation to a Channel

Social acceptance is a great feeling. Accepting an invite to a channel means you too can partake in banter with other channel members.

Loading Code Samples...
Language
@Override
public void onChannelInvited(final Channel channel) {
    channel.join(new StatusListener() {
        @Override
        public void onSuccess() {
            Log.d(TAG, "Joined Channel: " + channel.getFriendlyName());
        }
    });
}
// Listen for new invitations to your Client
chatClient.on('channelInvited', function(channel) {
  console.log('Invited to channel ' + channel.friendlyName);
  // Join the channel that you were invited to
  channel.join();
});
- (void)chatClient:(TwilioChatClient *)client channelAdded:(TCHChannel *)channel {
    if(channel.status == TCHChannelStatusInvited) {
        [channel joinWithCompletion:^(TCHResult *result) {
            if ([result isSuccessful]) {
                NSLog(@"Successfully accepted invite.");
            } else {
                NSLog(@"Failed to accept invite.");
            }
        }];
    }
}
extension ViewController: TwilioChatClientDelegate {
  // Called whenever a channel is added
  func chatClient(client: TwilioChatClient!, channelAdded channel: TCHChannel!) {
    if(channel.status == TCHChannelStatus.Invited) {
      channel.joinWithCompletion() { channelResult in
        if channelResult.isSuccessful() {
            print("Successfully accepted invite.");
        } else {
            print("Failed to accept invite.");
        }
      }
    }
  }
}
Accepting an Invite

Get a List of Subscribed Channels

Listing subscribed channels lets you perform actions on them as a channel member (e.g., invite or display). This method only shows the channels where the current programmable chat user is a member. To list public channels that have not yet been joined, see list public channels above.

Loading Code Samples...
Language
List<Channel> channels = chatClient.getChannels().getSubscribedChannels();
for (Channel channel : channels) {
    Log.d(TAG, "Channel named: " + channel.getFriendlyName());
}
chatClient.getSubscribedChannels().then(function(paginator) {
  for (i=0; i<paginator.items.length; i++) {
    var channel = paginator.items[i];
    console.log('Channel: ' + channel.friendlyName);
  }
});    
NSArray<TCHChannel *> *channels = [[self.client channelsList] subscribedChannels];
for (TCHChannel *channel in channels) {
  NSLog(@"Channel: %@", channel.friendlyName);
}
let channels = client.channelsList().subscribedChannels()
for channel in channels {
    print("Channel: \(channel.friendlyName)")
}
Retrieve Channels

Subscribe for Channel Events

Channels are a flurry of activity. Members join and leave, messages are sent and received, and channel states change. As a member of a channel, you'll want to know the status of the channel. You may want to receive a notification when the channel is deleted or changed. Channel event listeners help you do just that.

These event listeners will notify your app when a channel's state changes. Once you receive the notification, you can perform the necessary actions in your app to react to it.

Loading Code Samples...
Language
@Override
public void onMessageAdded(Message message) {
  Log.d(TAG, "Message added: " + message.getMessageBody());
}

@Override
public void onMessageUpdated(Message message) {
  Log.d(TAG, "Message changed: " + message.getMessageBody());
}

@Override
public void onMessageDeleted(Message message) {
  Log.d(TAG, "Message deleted");
}
// A channel has become visible to the Client
chatClient.on('channelAdded', function(channel) {
  console.log('Channel added: ' + channel.friendlyName);
});
// A channel is no longer visible to the Client
chatClient.on('channelRemoved', function(channel) {
  console.log('Channel removed: ' + channel.friendlyName);
});
// A channel's attributes or metadata have changed.
chatClient.on('channelUpdated', function(channel) {
  console.log('Channel updates: ' + channel.sid);
});
- (void)chatClient:(TwilioChatClient *)client 
      channelAdded:(TCHChannel *)channel {
    NSLog(@"Channel added: %@", channel.friendlyName);
}

- (void)chatClient:(TwilioChatClient *)client 
           channel:(TCHChannel *)channel 
           updated:(TCHChannelUpdate)update {
    NSLog(@"Channel changed: %@", channel.friendlyName);
}

- (void)chatClient:(TwilioChatClient *)client 
      channelDeleted:(TCHChannel *)channel {
    NSLog(@"Channel deleted: %@", channel.friendlyName);
}
extension ViewController: TwilioChatClientDelegate {
    func chatClient(client: TwilioChatClient!, channelAdded channel: TCHChannel!) {
        print("Channel added: \(channel.friendlyName)")
    }
    
    func chatClient(client: TwilioChatClient!, channel channel: TCHChannel!, updated update: TCHChannelUpdate) {
        print("Channel changed: \(channel.friendlyName)")
    }
    
    func chatClient(client: TwilioChatClient!, channelDeleted channel: TCHChannel!) {
        print("Channel deleted: \(channel.friendlyName)")
    }
}
Handle Channel Events

Your event listeners will also notify your app when channel members perform some action (including when they leave, join, change, or start/stop typing). Using these listeners, you can provide real-time updates to your application users.

Loading Code Samples...
Language
@Override
public void onMemberJoined(Member member) {
  Log.d(TAG, "Member joined: " + member.getUserInfo().getIdentity());
}

@Override
public void onMemberUpdated(Member member) {
  Log.d(TAG, "Member changed: " + member.getUserInfo().getIdentity());
}

@Override
public void onMemberDeleted(Member member) {
  Log.d(TAG, "Member deleted: " + member.getUserInfo().getIdentity());
}

@Override
public void onTypingStarted(Member member) {
  Log.d(TAG, "Started Typing: " + member.getUserInfo().getIdentity());
}

@Override
public void onTypingEnded(Member member) {
  Log.d(TAG, "Ended Typing: " + member.getUserInfo().getIdentity());
}

@Override
public void onSynchronizationChanged(Channel channel) {

}
// Listen for members joining a channel
myChannel.on('memberJoined', function(member) {
  console.log(member.identity + 'has joined the channel.');
});
// Listen for members user info changing
myChannel.on('memberInfoUpdated', function(member) {
  console.log(member.identity + 'updated their info.');
});
// Listen for members leaving a channel
myChannel.on('memberLeft', function(member) {
  console.log(member.identity + 'has left the channel.');
});
// Listen for members typing
myChannel.on('typingStarted', function(member) {
  console.log(member.identity + 'is currently typing.');
});
// Listen for members typing
myChannel.on('typingEnded', function(member) {
  console.log(member.identity + 'has stopped typing.');
});
- (void)chatClient:(TwilioChatClient *)client
           channel:(TCHChannel *)channel
      messageAdded:(TCHMessage *)message {
    [self addMessages:@[message]];
}


- (void)chatClient:(TwilioChatClient *)client
           channel:(TCHChannel *)channel
      memberJoined:(TCHMember *)member {
    NSLog(@"Member joined: %@", member.identity);
}

- (void)chatClient:(TwilioChatClient *)client
           channel:(TCHChannel *)channel
            member:(TCHMember *)member 
           updated:(TCHMemberUpdate)update {
    NSLog(@"Member changed: %@", member.identity);
}

- (void)chatClient:(TwilioChatClient *)client
           channel:(TCHChannel *)channel
        memberLeft:(TCHMember *)member {
    NSLog(@"Member left: %@", member.identity);
}

- (void)chatClient:(TwilioChatClient *)client
typingStartedOnChannel:(TCHChannel *)channel
            member:(TCHMember *)member {
    NSLog(@"Member started typing: %@", member.identity);
}

- (void)chatClient:(TwilioChatClient *)client
typingEndedOnChannel:(TCHChannel *)channel
            member:(TCHMember *)member {
    NSLog(@"Member ended typing: %@", member.identity);
}
extension ViewController: TwilioChatClientDelegate {
    func chatClient(client: TwilioChatClient!, channel: TCHChannel!,
                    memberJoined member: TCHMember!) {
        print("Member joined: \(member.identity)")
    }
    
    func chatClient(client: TwilioChatClient!, channel: TCHChannel!,
                    member member: TCHMember!, updated update: TCHMemberUpdate) {
        print("Member changed: \(member.identity)")
    }
    
    func chatClient(client: TwilioChatClient!, channel: TCHChannel!,
                    memberLeft member: TCHMember!) {
        print("Member left: \(member.identity)")
    }
    
    func chatClient(client: TwilioChatClient!,
                    typingStartedOnChannel channel: TCHChannel!,
                                           member: TCHMember!) {
        print("Member started typing: \(member.identity)")
    }
    
    func chatClient(client: TwilioChatClient!,
                    typingEndedOnChannel channel: TCHChannel!,
                                         member: TCHMember!) {
        print("Member ended typing: \(member.identity)")
    }
    

}
Handle Member Events

Delete a Channel

Deleting a channel both deletes the message history and removes all members from it.

Loading Code Samples...
Language
channel.destroy(new StatusListener() {
  @Override
  public void onSuccess() {
    Log.d(TAG, "Successfully deleted channel");
  }
  
  @Override
  public void onError(ErrorInfo errorInfo) {
    Log.d(TAG, "Error deleting channel: " + errorInfo.getErrorText());
  }
});
// Delete a previously created Channel
myChannel.delete().then(function(channel) {
  console.log('Deleted channel: ' + channel.sid);
});
[self.channel destroyWithCompletion:^(TCHResult *result) {
    if([result isSuccessful]) {
        NSLog(@"Channel destroyed.");
    } else {
        NSLog(@"Channel NOT destroyed.");
    }
}];
channel.destroyWithCompletion { result in
    if result.isSuccessful() {
        print("Channel destroyed.")
    } else {
        print("Channel NOT destroyed.")
    }
}
Delete a Channel

You can only delete channels that you have permissions to delete. Deleting a channel means it cannot be retrieved at a later date for any reason. Delete with caution!

Now that you know all there is to know about channels, might we suggest learning more about the REST API? With the REST API, you can execute many of these same actions from your server-side code.

Next: Media Support in Chat

Need some help?

We all do sometimes; code is hard. Get help now from our support team, or lean on the wisdom of the crowd browsing the Twilio tag on Stack Overflow.

1 / 1
Loading Code Samples...
chatClient.getChannels().getPublicChannelsList(new CallbackListener<Paginator<ChannelDescriptor>>() {
  @Override
  public void onSuccess(Paginator<ChannelDescriptor> channelPaginator) {
    for (ChannelDescriptor channel : channelPaginator.getItems()) {
      Log.d(TAG, "Channel named: " + channel.getFriendlyName());
    }
  }
});
chatClient.getPublicChannelDescriptors().then(function(paginator) {
  for (i=0; i<paginator.items.length; i++) {
    var channel = paginator.items[i];
    console.log('Channel: ' + channel.friendlyName);
  }
});    
[[self.client channelsList] publicChannelDescriptorsWithCompletion:^(TCHResult *result, 
  TCHChannelDescriptorPaginator *paginator) {
  if ([result isSuccessful]) {
    for (TCHChannelDescriptor *channel in paginator.items) {
      NSLog(@"Channel: %@", channel.friendlyName);
    }
  }
}];
client.channelsList().publicChannelDescriptorsWithCompletion({ (result, paginator) in
  if (result.isSuccessful()) {
    for channel in paginator.items() {
        print("Channel: \(channel.friendlyName)")
    }
  }
})
chatClient.getChannels().getUserChannelsList(new CallbackListener<Paginator<ChannelDescriptor>>() {
  @Override
  public void onSuccess(Paginator<ChannelDescriptor> channelPaginator) {
    for (ChannelDescriptor channel : channelPaginator.getItems()) {
      Log.d(TAG, "Channel named: " + channel.getFriendlyName());
    }
  }
});
chatClient.getUserChannelDescriptors().then(function(paginator) {
  for (i=0; i<paginator.items.length; i++) {
    var channel = paginator.items[i];
    console.log('Channel: ' + channel.friendlyName);
  }
});    
[[self.client channelsList] userChannelDescriptorsWithCompletion:^(TCHResult *result, 
  TCHChannelDescriptorPaginator *paginator) {
  if ([result isSuccessful]) {
    for (TCHChannelDescriptor *channel in paginator.items) {
      NSLog(@"Channel: %@", channel.friendlyName);
    }
  }
}];
client.channelsList().userChannelDescriptorsWithCompletion({ (result, paginator) in
  if (result.isSuccessful()) {
    for channel in paginator.items() {
        print("Channel: \(channel.friendlyName)")
    }
  }
})
channelDescriptor.getChannel(new CallbackListener<Channel>() {
    @Override
    public void onSuccess(Channel channel) {
      Log.d(TAG, "Channel Status: " + channel.getStatus());
    }
});
[channelDescriptor channelWithCompletion:^(TCHResult *result, TCHChannel *channel) {
  if([result isSuccessful]) {
    NSLog(@"ChannelStatus: %@", channel.status);
  }
}]
channelDescriptor?.channel(completion:{ (result, channel) in
  if result!.isSuccessful() {
    print("Channel Status: \(String(describing: channel?.status))")
  }
})
mChatClient.getChannels().channelBuilder().
  .withFriendlyName("general")
  .withType(Channel.ChannelType.PUBLIC)
  .build(new CallbackListener<Channel>() {
      @Override
      public void onSuccess(Channel channel) {
          if (channel != null) {
              Log.d(TAG,"Success creating channel");
          }
      }

      @Override
      public void onError(ErrorInfo errorInfo) {
          Log.e(TAG,"Error creating channel: " + errorInfo.getErrorText());
      }
  });
// Create a Channel
chatClient
  .createChannel({
    uniqueName: 'general',
    friendlyName: 'General Chat Channel',
  })
  .then(function(channel) {
    console.log('Created general channel:');
    console.log(channel);
  });
// Create the general channel (for public use) if it hasn't been created yet
NSDictionary *options = @{
                         TCHChannelOptionFriendlyName: @"General Chat Channel",
                         TCHChannelOptionType: @(TCHChannelTypePublic)
                         };
[channels createChannelWithOptions:options completion:^(TCHResult *result, TCHChannel *channel) {
    if([result isSuccessful]) {
        NSLog(@"Channel created.");
    } else {
        NSLog(@"Channel NOT created.");
    }
}];
let options = [
    TCHChannelOptionFriendlyName: "General Channel",
    TCHChannelOptionType: TCHChannelType.public.rawValue
] as [String : Any]
client?.channelsList().createChannel(options: options, completion: { channelResult, channel in
    if (channelResult?.isSuccessful())! {
        print("Channel created.")
    } else {
        print("Channel NOT created.")
    }
})
private void joinChannel(final Channel channel) {
    Log.d(TAG, "Joining Channel: " + channel.getUniqueName());
    channel.join(new StatusListener() {
        @Override
        public void onSuccess() {
            Log.d(TAG, "Joined channel");
        }

        @Override
        public void onError(ErrorInfo errorInfo) {
            Log.e(TAG,"Error joining channel: " + errorInfo.toString());
        }
    });
}
// Join a previously created channel
client.on('channelJoined', function(channel) {
  console.log("Joined channel " + channel.friendlyName);
});

myChannel.join().catch(function(err) {
  console.error("Couldn't join channel " + channel.friendlyName + ' because ' + err);
});
// Where "client" is an authenticated client obtained with
// [TwilioChatClient chatClientWithToken:properties:delegate:]
// and the callback for chatClient:synchronizationStatusChanged: has been called with TCHClientSynchronizationStatusChannelsListCompleted
TCHChannel *channel = [[self.client channelsList] channelWithUniqueName:@"general"];
if (channel) {
    [channel joinWithCompletion:^(TCHResult *result) {
        if ([result isSuccessful]) {
            NSLog(@"Channel joined.");
        } else {
            NSLog(@"Channel NOT joined.");
        }
    }];
}
// Where "client" is an authenticated client obtained with
// TwilioChatClient(token:, properties:, delegate:)
// and the callback for chatClient(client:, synchronizationStatusChanged:) has been called with .completed
let channel = client.channelsList().channel(withSidOrUniqueName:"general")
if let channel = channel {
    channel.join(completion: { channelResult in
        if channelResult.isSuccessful() {
            print("Channel joined.")
        } else {
            print("Channel NOT joined.")
        }
    }
}
channel.getMessages().sendMessage("test", new StatusListener() {
    @Override
    public void onSuccess() {
      Log.d(TAG,"Message sent successfully");
    }

    @Override
    public void onError(ErrorInfo errorInfo) {
        Log.e(TAG,"Error sending message: " + errorInfo.toString());
    }
});
// Send a message to a previously created Channel
var msg = $('#chat-input').val();
myChannel.sendMessage(msg);
TCHMessageOptions *options = [[TCHMessageOptions new] withBody:@"test"];
[self.channel.messages sendMessageWithOptions:options completion:^(TCHResult *result, TCHMessage *message) {
    if ([result isSuccessful]) {
        NSLog(@"Message sent.");
    } else {
        NSLog(@"Message NOT sent.");
    }
}];
// Where "channel" is a TCHChannel
if let messages = channel.messages {
  let options = TCHMessageOptions().withBody("test")
  messages.sendMessage(with: options) { result, message in
    if result.isSuccessful() {
      print("Message sent.")
    } else {
      print("Message NOT sent.")
    }
  }
}
  channel.getMessages().getLastMessages(50, new CallbackListener<List<Message>>() {
    @Override
    public void onSuccess(List<Message> messages) {
      for (Message message : messages) {
        Log.d(TAG, "Message Body: " + message.getMessageBody());
      }
    }
  });
// Get Messages for a previously created channel
channel.getMessages().then(function(messages) {
  const totalMessages = messages.items.length;
  for (i = 0; i < totalMessages; i++) {
    const message = messages.items[i];
    console.log('Author:' + message.author);
  }
  console.log('Total Messages:' + totalMessages);
});
[self.channel.messages getLastMessagesWithCount:100 
  completion:^(TCHResult *result, NSArray<TCHMessage *> *messages) {
  for (TCHMessage *message in messages) {
    NSLog(@"Message body: %@", message.body);
  }
}];
channel.messages.getLastMessagesWithCount(100, completion: { (result, messages) in
  for message in messages {
    print("Message body: \(message.body)")
  }
})
private ChannelListener mDefaultChannelListener = new ChannelListener() {
  @Override
  public void onMessageAdded(final Message message) {
    Log.d(TAG, "Message added: " + message.getMessageBody());
  }
}
// Listen for new messages sent to a channel
myChannel.on('messageAdded', function(message) {
  console.log(message.author, message.body);
});
- (void) chatClient:(TwilioChatClient *)client channel:(TCHChannel *)channel 
    messageAdded:(TCHMessage *)message {
    
  NSLog(@"%@ said: %@", message.author, message.body);
}
extension ViewController: TwilioChatClientDelegate {
    // Called whenever a channel we've joined receives a new message
    func chatClient(client: TwilioChatClient!, channel: TCHChannel!,
                    messageAdded message: TCHMessage!) {
        print("\(message.author) said: \(message.body)")
    }
}
channel.getMembers().inviteByIdentity("Juan", new StatusListener() {
  @Override
  public void onSuccess() {
    Log.d(TAG,"User Invited!");
  }
});
// Invite another member to your channel
myChannel.invite('elmo').then(function() {
  console.log('Your friend has been invited!');
});
[self.channel.members inviteByIdentity:@"Juan" completion:^(TCHResult *result) {
    if([result isSuccessful]) {
        NSLog(@"User invited.");
    } else {
        NSLog(@"User NOT invited.");
    }
}];
channel.members.inviteByIdentity("Juan") { result in
    if result.isSuccessful() {
        print("User invited.")
    } else {
        print("User NOT invited.")
    }
}
@Override
public void onChannelInvited(final Channel channel) {
    channel.join(new StatusListener() {
        @Override
        public void onSuccess() {
            Log.d(TAG, "Joined Channel: " + channel.getFriendlyName());
        }
    });
}
// Listen for new invitations to your Client
chatClient.on('channelInvited', function(channel) {
  console.log('Invited to channel ' + channel.friendlyName);
  // Join the channel that you were invited to
  channel.join();
});
- (void)chatClient:(TwilioChatClient *)client channelAdded:(TCHChannel *)channel {
    if(channel.status == TCHChannelStatusInvited) {
        [channel joinWithCompletion:^(TCHResult *result) {
            if ([result isSuccessful]) {
                NSLog(@"Successfully accepted invite.");
            } else {
                NSLog(@"Failed to accept invite.");
            }
        }];
    }
}
extension ViewController: TwilioChatClientDelegate {
  // Called whenever a channel is added
  func chatClient(client: TwilioChatClient!, channelAdded channel: TCHChannel!) {
    if(channel.status == TCHChannelStatus.Invited) {
      channel.joinWithCompletion() { channelResult in
        if channelResult.isSuccessful() {
            print("Successfully accepted invite.");
        } else {
            print("Failed to accept invite.");
        }
      }
    }
  }
}
List<Channel> channels = chatClient.getChannels().getSubscribedChannels();
for (Channel channel : channels) {
    Log.d(TAG, "Channel named: " + channel.getFriendlyName());
}
chatClient.getSubscribedChannels().then(function(paginator) {
  for (i=0; i<paginator.items.length; i++) {
    var channel = paginator.items[i];
    console.log('Channel: ' + channel.friendlyName);
  }
});    
NSArray<TCHChannel *> *channels = [[self.client channelsList] subscribedChannels];
for (TCHChannel *channel in channels) {
  NSLog(@"Channel: %@", channel.friendlyName);
}
let channels = client.channelsList().subscribedChannels()
for channel in channels {
    print("Channel: \(channel.friendlyName)")
}
@Override
public void onMessageAdded(Message message) {
  Log.d(TAG, "Message added: " + message.getMessageBody());
}

@Override
public void onMessageUpdated(Message message) {
  Log.d(TAG, "Message changed: " + message.getMessageBody());
}

@Override
public void onMessageDeleted(Message message) {
  Log.d(TAG, "Message deleted");
}
// A channel has become visible to the Client
chatClient.on('channelAdded', function(channel) {
  console.log('Channel added: ' + channel.friendlyName);
});
// A channel is no longer visible to the Client
chatClient.on('channelRemoved', function(channel) {
  console.log('Channel removed: ' + channel.friendlyName);
});
// A channel's attributes or metadata have changed.
chatClient.on('channelUpdated', function(channel) {
  console.log('Channel updates: ' + channel.sid);
});
- (void)chatClient:(TwilioChatClient *)client 
      channelAdded:(TCHChannel *)channel {
    NSLog(@"Channel added: %@", channel.friendlyName);
}

- (void)chatClient:(TwilioChatClient *)client 
           channel:(TCHChannel *)channel 
           updated:(TCHChannelUpdate)update {
    NSLog(@"Channel changed: %@", channel.friendlyName);
}

- (void)chatClient:(TwilioChatClient *)client 
      channelDeleted:(TCHChannel *)channel {
    NSLog(@"Channel deleted: %@", channel.friendlyName);
}
extension ViewController: TwilioChatClientDelegate {
    func chatClient(client: TwilioChatClient!, channelAdded channel: TCHChannel!) {
        print("Channel added: \(channel.friendlyName)")
    }
    
    func chatClient(client: TwilioChatClient!, channel channel: TCHChannel!, updated update: TCHChannelUpdate) {
        print("Channel changed: \(channel.friendlyName)")
    }
    
    func chatClient(client: TwilioChatClient!, channelDeleted channel: TCHChannel!) {
        print("Channel deleted: \(channel.friendlyName)")
    }
}
@Override
public void onMemberJoined(Member member) {
  Log.d(TAG, "Member joined: " + member.getUserInfo().getIdentity());
}

@Override
public void onMemberUpdated(Member member) {
  Log.d(TAG, "Member changed: " + member.getUserInfo().getIdentity());
}

@Override
public void onMemberDeleted(Member member) {
  Log.d(TAG, "Member deleted: " + member.getUserInfo().getIdentity());
}

@Override
public void onTypingStarted(Member member) {
  Log.d(TAG, "Started Typing: " + member.getUserInfo().getIdentity());
}

@Override
public void onTypingEnded(Member member) {
  Log.d(TAG, "Ended Typing: " + member.getUserInfo().getIdentity());
}

@Override
public void onSynchronizationChanged(Channel channel) {

}
// Listen for members joining a channel
myChannel.on('memberJoined', function(member) {
  console.log(member.identity + 'has joined the channel.');
});
// Listen for members user info changing
myChannel.on('memberInfoUpdated', function(member) {
  console.log(member.identity + 'updated their info.');
});
// Listen for members leaving a channel
myChannel.on('memberLeft', function(member) {
  console.log(member.identity + 'has left the channel.');
});
// Listen for members typing
myChannel.on('typingStarted', function(member) {
  console.log(member.identity + 'is currently typing.');
});
// Listen for members typing
myChannel.on('typingEnded', function(member) {
  console.log(member.identity + 'has stopped typing.');
});
- (void)chatClient:(TwilioChatClient *)client
           channel:(TCHChannel *)channel
      messageAdded:(TCHMessage *)message {
    [self addMessages:@[message]];
}


- (void)chatClient:(TwilioChatClient *)client
           channel:(TCHChannel *)channel
      memberJoined:(TCHMember *)member {
    NSLog(@"Member joined: %@", member.identity);
}

- (void)chatClient:(TwilioChatClient *)client
           channel:(TCHChannel *)channel
            member:(TCHMember *)member 
           updated:(TCHMemberUpdate)update {
    NSLog(@"Member changed: %@", member.identity);
}

- (void)chatClient:(TwilioChatClient *)client
           channel:(TCHChannel *)channel
        memberLeft:(TCHMember *)member {
    NSLog(@"Member left: %@", member.identity);
}

- (void)chatClient:(TwilioChatClient *)client
typingStartedOnChannel:(TCHChannel *)channel
            member:(TCHMember *)member {
    NSLog(@"Member started typing: %@", member.identity);
}

- (void)chatClient:(TwilioChatClient *)client
typingEndedOnChannel:(TCHChannel *)channel
            member:(TCHMember *)member {
    NSLog(@"Member ended typing: %@", member.identity);
}
extension ViewController: TwilioChatClientDelegate {
    func chatClient(client: TwilioChatClient!, channel: TCHChannel!,
                    memberJoined member: TCHMember!) {
        print("Member joined: \(member.identity)")
    }
    
    func chatClient(client: TwilioChatClient!, channel: TCHChannel!,
                    member member: TCHMember!, updated update: TCHMemberUpdate) {
        print("Member changed: \(member.identity)")
    }
    
    func chatClient(client: TwilioChatClient!, channel: TCHChannel!,
                    memberLeft member: TCHMember!) {
        print("Member left: \(member.identity)")
    }
    
    func chatClient(client: TwilioChatClient!,
                    typingStartedOnChannel channel: TCHChannel!,
                                           member: TCHMember!) {
        print("Member started typing: \(member.identity)")
    }
    
    func chatClient(client: TwilioChatClient!,
                    typingEndedOnChannel channel: TCHChannel!,
                                         member: TCHMember!) {
        print("Member ended typing: \(member.identity)")
    }
    

}
channel.destroy(new StatusListener() {
  @Override
  public void onSuccess() {
    Log.d(TAG, "Successfully deleted channel");
  }
  
  @Override
  public void onError(ErrorInfo errorInfo) {
    Log.d(TAG, "Error deleting channel: " + errorInfo.getErrorText());
  }
});
// Delete a previously created Channel
myChannel.delete().then(function(channel) {
  console.log('Deleted channel: ' + channel.sid);
});
[self.channel destroyWithCompletion:^(TCHResult *result) {
    if([result isSuccessful]) {
        NSLog(@"Channel destroyed.");
    } else {
        NSLog(@"Channel NOT destroyed.");
    }
}];
channel.destroyWithCompletion { result in
    if result.isSuccessful() {
        print("Channel destroyed.")
    } else {
        print("Channel NOT destroyed.")
    }
}