An API I am writing has about 2000 records, returned in JSON via a simple RESTful API I have written.
To reduce issues with lots of data, I want to use pagination so that I only return say the first 10 or first 20 per request via like an offset
or limit
or page
, etc.
But my question is how does the iOS UITableView
know when to get the next page of results?
I really am unsure of how to do this. The user could be scrolling superfast and so the API might not have enough time to retrieve 20 or 50 records at a time.
Another issue related to this is, lets say the user scrolls down on the UITableView, then up and then back down again -- how do you prevent the API from firing multiple times for the same rows?
Thanks
Seems to be you aren't thinking in terms of MVC . Your UITableView has very little do with paging and webrequest. It's just concerned about its datasource not pages.
Restuful API Design :
Assume your web Request is designed as follow :
/getRecorods?pageSize=20&pageNo=2
This will return you an Array of JSON. In addition to that its helpful to have a count parameter and a link to next page. This helps in parsing and sync with web server.
how do you prevent the API from firing multiple times for the same rows?
A simple flag is sufficient to avoid loading multiple pages. Just make sure that flag is accessed in main thread. The actual webrequest needs to go in background thread.
Below is the code you need to put into your UITableViewController which loads the data
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//call http Util here to load the data
httpUtil.delegate = self;
//This retrieves post for first page always
currentPageNumber = 1;
[httpUtil getRecords:currentPageNumber];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
int retValue = 0;
if(recordsArray != nil){
retValue = [recordsArray count];
}
return retValue;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
}
// Configure the cell using recordsArray objectAtIndex
return cell;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
if(self.tableView.contentOffset.y >= (self.tableView.contentSize.height - self.tableView.bounds.size.height)) {
//NSLog(@" scroll to bottom!");
if(isPageRefresing == NO){ // no need to worry about threads because this is always on main thread.
isPageRefresing = YES;
[self showMBProfressHUDOnView:self.view withText:@"Please wait..."];
currentpagenumber = currentpagenumber +1;
[httpUtil getRecords:currentpagenumber];
}
}
}
// you can get pageNo from tag
// make sure this is called in main thread
-(void)didFinishRecordsRequest:(NSArray *)results forPage:(NSInteger)pageNo{
if(pageNo == 1){
recordsArray = [results mutableCopy];
}
else{
[recordsArray addObjectsFromArray:results];
}
isPageRefresing = NO;
[self.tableView reloadData];
}
-(void)didFailedChalkBoardRequestWithError:(NSError *)error{
//If Since subsequent refresh calls(for page 2 or page 3 fails)
//then undo the current page number
currentpagenumber--;
isPageRefresing = NO;
}
// HTTP Utility class
-(void)getRecords:(NSInteger)pageNumber{
NSString *serverUrl = [NSString stringWithFormat:@"http://yourwebsite.com/page/%d /?json",pageNumber];
NSLog(@"fetching Stories data from server WITH URL %@",serverUrl);
NSURL *url = [NSURL URLWithString:serverUrl];
storiesRequest = [ASIHTTPRequest requestWithURL:url];
storiesRequest.tag = pageNumber;
[storiesRequest setDelegate:self];
[storiesRequest startAsynchronous];
}
For Swift
func scrollViewDidScroll(scrollView: UIScrollView) {
if collectionViewGif.contentOffset.y >= self.collectionViewGif.contentSize.height - self.collectionViewGif.frame.size.height
{
offset = offset + 1
self.fetchGifWithPaination(defaultText)
}
}