# Result Paging in Chat Client SDKs

> \[!CAUTION]
>
> Programmable Chat has been deprecated and is no longer supported. Instead, we'll be focusing on the next generation of chat: Twilio Conversations. Find out more about the [EOL process here](https://www.twilio.com/en-us/changelog/programmable-chat-end-of-life-notice).
>
> If you're starting a new project, please visit the [Conversations Docs](/docs/conversations) to begin. If you've already built on Programmable Chat, please visit our [Migration Guide](/docs/conversations/migrating-chat-conversations) to learn about how to switch.

Twilio's Programmable Chat client SDKs use paging to improve performance when accessing potentially large collections of chat objects.

## Paginators

Public Channels, User Channels, and the Members list for each channel are exposed through Paginators. In JavaScript, Messages are also paged using Paginators. When requesting an initial set of any of these entities, your provided callback mechanism will receive a result indicating the success or failure of the operation as well as a paginator to access the items.

While the signature of the individual methods will vary by SDK platform, each paginator has the following accessors:

* A way to obtain the items.
* A Boolean property indicating if there are subsequent pages.
* A method taking the same callback mechanism as the original call to request the next page.

Result Paging: Obtain a List of Public Channel Descriptors

```js
// Get messages from the newest to oldest
channel
  .getMessages(20 /* by pages of 20 messages */)
  .then(function(messagesPage) {
    messagesPage.items.forEach(function(message) {
      // do stuff for each message
    });
    // note, that by default pages are in "backwards" order,
    // so you should ask for previous page, not the next one
    if (messagesPage.hasPrevPage) {
      return messagesPage.prevPage(); // here we can ask for following page
    }
  });

// Get messages from the message with id 3 to newest
channel.getMessages(20, 3, 'forward').then(function(messagesPage) {
  /* handle page the same way as above */
});

// Handle multiple pages
// Unfortunately, js has no asynchronous iterators yet, so there should be
// some boilerplate here. One of the way to go through all messages would be
// like this:
function processPage(page) {
  page.items.forEach(function(message) {
    /* do something for message */
  });
  if (page.hasNextPage) {
    page.nextPage().then(processPage);
  } else {
    // all messages read!
  }
}
channel.getMessage(10, null, 'forward').then(processPage);
```

```java
private void getChannelsPage(Paginator<ChannelDescriptor> paginator) {

    // Add paginator.getItems() to UI

    if (paginator.hasNextPage()) {
        paginator.requestNextPage(new CallbackListener<Paginator<ChannelDescriptor>>() {
            @Override
            public void onSuccess(Paginator<ChannelDescriptor> channelPaginator) {
                getChannelsPage(channelPaginator);
            }
        });
    }
}

// User Channels
channelsObject = myClient.getChatClient().getChannels();

// Kick first get
channelsObject.getUserChannels(new CallbackListener<Paginator<ChannelDescriptor>>() {
    @Override
    public void onSuccess(Paginator<ChannelDescriptor> channelPaginator) {
        getChannelsPage(channelPaginator);
    }
});

// Channel Members
channel.getMembers().getMembers(new CallbackListener<Paginator<Member>>() {
  @Override
  public void onSuccess(Paginator<Member> memberPaginator) {
    // use memberPaginator.getItems() in UI
  }
});
```

```objective-c
// If your subsequent pages will use the same completion
// handler, you can set the block as a variable to be
// referenced from within the block.
void __block (^_completion)();
TCHChannelDescriptorPaginatorCompletion completion = ^(TCHResult *result, TCHChannelDescriptorPaginator *paginator) {
    if (result.isSuccessful) {
        NSArray<TCHChannelDescriptor *> *items = [paginator items];
        // << consume the items in your UI >>

        // You can either obtain the next page immediately or wait
        // until the user scrolls to the bottom of the list before
        // requesting, depending on your use case.
        if (paginator.hasNextPage) {
            [paginator requestNextPageWithCompletion:completion];
        }
    } else {
        // report the error to the user
    }
};
_completion = completion;
[channelsList publicChannelsWithCompletion:completion];
```

```swift
func channelsCompletion(result: TCHResult?, paginator: TCHChannelDescriptorPaginator?) {
    let items = paginator?.items()

    if result!.isSuccessful() {
        // << consume the items in your UI >>

        // You can either obtain the next page immediately or wait
        // until the user scrolls to the bottom of the list before
        // requesting, depending on your use case.
      if paginator!.hasNextPage() {
        paginator?.requestNextPage(completion: completion)
      }
    } else {
      // report the error to the user
    }
  }

client.channelsList().publicChannelDescriptors(completion: channelsCompletion)
```

## Messages (Android, iOS)

The messages collection behaves a bit differently than channels and members since there is a temporal quality to how the objects are typically presented.

The messages collection objects offer the following ways to access items:

* `getLastMessages` fetches the specified number of messages, starting with the most recent in the collection.
* `getMessagesBefore` fetches messages before (and including) the anchor message index specified.
* `getMessagesAfter` fetches messages after (and including) the anchor message index specified.
* `messageWithConsumptionIndex` fetches the message with the specified index or, if that message is no longer available, the message directly before it.
* `messageWithIndex` fetches the message specified by the index, if available.

Result Paging: Messages

```java
// Fetch the initial messages
messagesObject.getLastMessages(BATCH_SIZE, new CallbackListener<List<Message>>()
{
  @Override
  public void onSuccess(List<Message> messagesArray) {
    // Display initial messages in your UI
  }
});

// ... user scrolls through messages list

// Fetch next set of messages when you get close to the
// end of your local messages
messagesObject.getMessagesBefore(firstMessage.getMessageIndex(), BATCH_SIZE,
                                 new CallbackListener<List<Message>>()
{
  @Override
  public void onSuccess(List<Message> messagesArray) {
    // Display latest messages in your UI
  }
});
```

```objective-c
// Fetch the initial messages
[self.channel.messages getLastMessagesWithCount:BATCH_SIZE
                                     completion:^(TCHResult *result, NSArray<TCHMessage *> *messages) {
                                        self.messages = messages;
                                        // << consume the initial items in your UI >>
                                      }];

// ... user scrolls through messages list

// Fetch next set of messages when you get close to the
// end of your local messages
[self.channel.messages getMessagesBefore:([firstMessage.index integerValue] - 1)
                               withCount:BATCH_SIZE
                              completion:^(TCHResult *result, NSArray<TCHMessage *> *messages) {
                                NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, messages.count)];
                                [self.messages insertObjects:messages
                                                   atIndexes:indexes];
                                 // << consume the latest items in your UI >>
                              }];
```

```swift
// Fetch the initial messages
channel.messages.getLastWithCount(BATCH_SIZE) { (result, messages) in
  self.messages = messages!
  // << consume the initial items in your UI >>
}

// ... user scrolls through messages list

// Fetch next set of messages when you get close to the
// end of your local messages
channel.messages.getBefore(UInt(self.messages.first!.index) - 1, withCount: BATCH_SIZE) { (result, messages) in
  self.messages.append(contentsOf: messages!)
  // << consume the latest items in your UI >>
}
```

Next: [Roles and Permissions](/docs/chat/permissions)
