by Cesare Rocchi

Podrover Diaries: Table views with load more at the bottom

Podrover Diaries is a series about my adventures in building Podrover, a service to track, collect and share your podcast reviews. Subscribe to the RSS feed or join my newsletter to stay up to date with upcoming adventures.

“Why there isn’t a built-in component to make this?”

I remember this question rolling in my head as I was building this allegedly simple feature in Podrover.

You have seen this pattern many times:

  • load elements from a service
  • show elements in a table view
  • allow the user to load more elements
  • display new elements at the bottom of the list

In my case elements are podcast reviews, sorted chronologically.

Load more reviews in Podrover

Design-wise there’s two main ways to implement this:

  1. Implicit: the last cell at the bottom is empty. When it is shown, it triggers the “load next batch of reviews” operation

  2. Explicit: The last cell shows a button that allows the user to explicitly trigger the load more action

I am all for saving clicks and taps, but I went with the second solution. The rationale is based on two observations:

  1. I am not in the position to assume that scrolling to the bottom means “I want more”
  2. This is not a social media app, where the goal is to keep the user engaged as much as possible

The goal of Podrover is indeed to save you time, not to keep you longer in front of a screen.

When I built Neater (which adopted the implicit strategy) I had to deal with a bunch of edge cases, mostly when the “fetch from network service” operation failed.
Here’s the rundown:

  • the only way to trigger load more is the “last cell is shown on screen” event
  • the load more operation fails
  • you have to adjust the scroll position of the table view so that the last cell is hidden again

Otherwise (depending on the implementation) you end up with a loop or without the possibility to trigger the load action again. None of which is the one you want.

Moreover, when a network call fails, you’ll likely show an error via an alert view or a status bar message, which happens while the scroll position is adjusted. It’s too many changes at once, perception-wise,

So I decided to go with the load more button, the best fit in this situation.

Implementation details

UIKit already includes UIRefreshControl, and it works quite well. I think there’s room for a load more component. At the moment the implementation is quite clunky:

  • add one extra cell at the bottom to show “load more” (but don’t add it if the result set is empty)
  • keep track of the state of the “load more” action
    • show button if there’s more to load
    • show “there’s no more” icon if it’s the last page
    • show spinner if the “load more” action is ongoing
  • if load operation is successful remove last cell, add new elements and add load more at the bottom ( or “there’s no more” icon)
  • if there’s a failure
    • show error message
    • in the last cell hide spinner and show button again

There must be simpler way :) I didn’t find one yet, but let me know if you did.

I didn’t look for a third party library, I am trying to keep a minimal number of dependencies.

Conclusion

Rarely I found that the simplest solution to implement is also the most fit. It’s pretty rare for my customers to browse ALL their podcast reviews at once. They usually stop at the third page at most (60 most recent reviews). It’s two extra taps, not the end of the world. Side effect: I had time to focus on other details and I saved some headaches :)

P.S. If you go the implicit way, use willDisplayCell: to detect when the last cell is displayed. Using scrollViewDidEndDragging: is overkill and complicated. Here’s a sample

- (void)tableView:(UITableView *)tableView 
  willDisplayCell:(UITableViewCell *)cell    
forRowAtIndexPath:(NSIndexPath *)indexPath {

  if (indexPath.row == reviews.count) {
    if (/* load more operation NOT ongoing */) {
      // trigger load more here
    }
  }
	
}

P.S.2 A web API should always return metadata to tell the client if there’s more results to load. It’s just a boolean property. My trick is:

  • assuming the page length is 20
  • run a query requesting 21 elements
  • if the result contains 21, remove the last and set the property to true
  • otherwise set it to false

Handy uh :)