Apple Swift Logo

Simple UICollectionView pagination with API and Realm database

If you’re using Realm database and a REST API, you may have encountered a pagination issue at some point. That’s what happened in the project I’m working on. Something was wrong with the pagination and I kept adding variables and ifs until I couldn’t remember what was doing what. Then I realized I needed a clean slate, and I created this little project.

So, this is a mockup to test a minimum viable pagination and preloading of data from a REST API to feed a UITableView or UICollectionView, using Realm.io database.
Just to make things clear, I am not introducing pagination into Realm’s API, nor am I paginating Realm results themselves.

A few words about Realm

Realm is a great database: easy to use, flexible, efficient, cross-platform, fully documented and very well maintained. In one word, it’s brilliant! For more info, have a look here.

One thing to know if you worked with other databases before: there is no pagination (or limit) in Realm’s current API, you always get all the results matching your query. This actually makes sense as you may not want to store all the possible dynamic content of the Earth on your device, but only what’s useful to you. Also, when there is any changes in the database, your application gets notified and you usually reload the content. It’s usually the point, right?

So I came across a pagination challenge: how to preload nicely (and quietly) the next batch of content so it would be available when you scroll to it?

The idea

The idea is simple: you need to trigger the preload when you reach a certain point (nothing new under the sun, that’s basic pagination) AND you need to prevent multiple (unwanted) preloads from happening.

If you miss the second point, the waterfall effect can be quite impressive to watch: you load the 1st batch of data that creates the cells, then it reaches the preload point that loads the next set of data that reloads the view and creates the cells, etc. Yep, infinite loop, our favorite!

The implementation

Let’s build a simple pagination mechanism together!

First, initialization:

let pageSize = 20 // that's up to you, really
let preloadMargin = 5 // or whatever number that makes sense with your page size
var lastLoadedPage = 0

In viewDidLoad, make sure to get your first batch of data, and to reload when the database is updated:

override func viewDidLoad() {
    super.viewDidLoad()

    // do stuff here

    notificationToken = realm.addNotificationBlock { [unowned self] note, realm in
        self.reloadData()
    }

    getData()
}

Then make sure you can get and reload data:

func getData(page: Int = 0) {
    lastLoadedPage = page
    // TODO: do the API call to get data
}

func reloadData() {
    // TODO: retrieve data from the database
    collectionView?.reloadSections(NSIndexSet(index: 0))
}

And here is the bit of code that checks if we need to load the next page of data:

override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {

    let nextPage: Int = Int(indexPath.item / pageSize) + 1
    let preloadIndex = nextPage * pageSize - preloadMargin

    // trigger the preload when you reach a certain point AND prevent multiple loads and updates
    if (indexPath.item >= preloadIndex && lastLoadedPage < nextPage) {
        getData(nextPage)
    }

    // TODO: configure cell
}

Nothing complicated, I just wanted to keep it clean and simple. The full demo project is on GitHub: RealmPagination.

Just one word before we leave: if you have auto-resizing cells, set the default size value as close to the final size as you can get. The reason is simple: when you reload your UITableView or UICollectionView, the scroll offset will be calculated based on the estimated cell size. So your scrollView may be off after the reload, and you may end up landing on the wrong cell.

Any thoughts or feedback on Realm or pagination? Please leave a comment below :)

Leave a Comment

You want to leave a comment? Sweet! Please do so, but keep in mind that all comments are moderated and rel="nofollow" is in use. So no fake comment or advertisement will be approved. And don't worry, your email address won't be published. Thanks!

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>