iosobjective-cparse-platformpaginationscroll-paging

Pagination With Dynamic Content


I'm trying to figure out the proper way to paginate results using the Parse SDK for iOS. The problem I'm having is that the content I'm trying to paginate has the potential to change frequently.

For example:

There is an app with a bunch of objects stored on a server, and the request to get these objects is sorted with newest first and paginated in case there are a lot of objects. When the user scrolls, as they are approaching the last object on the current page, the next page gets requested.

Say the user scrolls to page two, then a new object is added to the list of objects, and the user then scrolls up another page. When the next page is requested, they will get a duplicate of the last message on the second page, and since page 0 has already been loaded, the new object won't be displayed.

How can something like this be handled?

How would the implementation change if the objects were sorted oldest first?

Thanks in advance for the help.

Edit: request pseudo code

- (void)fetchObjectsForPage:(NSUInteger)page completion:(void (^)(NSArray *_Nullable objects, PageInfo *_Nullable pageInfo, NSError *_Nullable error))completion{
    PFQuery *query = [SomeObject query];
    [query orderByAscending:@"updatedAt"];
    [query setLimit:50];
    [query setSkip:50 * page];
    [query findObjectsInBackgroundWithBlock:^{NSArray *_Nullable objects, NSError *_Nullable error){
        ...
        >>> Do error checking and return array along with paging info (total number of objects, page size) or error in completion block.
    }];
}

It's not the request I'm having trouble with, it's figuring out how to properly handle paging in the table when a new object gets added.


Solution

  • What you can do is the following:

    1. Create a query that will return 50 rows (like you did) order by updatedAt
    2. When you get result take the last item from the list and save the item updatedAt in some global property
    3. In the next call do not use setOffset but get the next 50 rows and add another condition of where updatedAt is greater than the updatedAt of your global object. This way you make sure that you will not have duplicate records in your list

    At the end your code should look like the following:

    PFQuery *query = [PFQuery queryWithClassName:@"SomeClassName"];
    [query orderByAscending:@"updatedAt"];
    [query setLimit:50];
    
    if (self.lastItemUpdatedAt){
        [query whereKey:@"updatedAt" greaterThan:self.lastItemUpdatedAt];
    }
    
    [query findObjectsInBackgroundWithBlock:^(NSArray * _Nullable objects, NSError * _Nullable error) {
    
        if (objects.count > 0){
            self.lastItemUpdatedAt = [[objects lastObject] updatedAt];
        }
    
    }];
    

    Now if you use updatedAt there is a chance that you will still get duplicates because let's assume that someone change the object so the updatedAt will also be changed (so it will be greater than the updatedAt that you saved) in this case you can use createdAt instead of updatedAt since createdAt will never be changed