Build a Realtime Updating Endless Feed with Firebase

Hanno Weiß
4 min readJan 2, 2022

Lists are the bread and butter of many apps. Just take a look at Instagram, Reddit or Amazons feed. We want to accomplish a similar feed that lazy loads more content as the user scrolls down and that supports realtime updates. For example when your app shows a like counter, it would update without a reload after another user likes the post.

The sample code is for iOS but the concept also works for any supported Firebase platform like Android or Flutter.

Take a look at the video to get a better idea about what’s coming next.

A prototype to show a realtime updating endless scrolling feed

The Query

At first it seems like a straight forward idea to write a query and attach a snapshot listener to it like this

A simple realtime listener

With that, we will always get the realtime updates and always show the newest 10 documents. This works great until our list is longer than the limit of 10. What happens as soon as the 11 document appears?

As soon as the 11 document is added to the top of the list, all documents shifts one down. In our realtime listener response is 1 document added and 1 document deleted.

That’s weird because we just added one document and never deleted anything. The reason is that the realtime listener only observes the documents that matches the query. Take a look at the graphic below.

Realtime updates only happens on query snapshot dataset

One solution is to remove the limit in the query. The drawbacks are

  • More reads on the database and we pay for each read
  • Slower loading since you load the complete collection
  • Don’t scale when the collection has thousands or millions of documents

Getting realtime updates

I highly recommend to always save two timestamps in each document, even though you don’t need them (right now).

  • createdAt: Timestamp
  • updatedAt: Timestamp

We create two queries. One for the endless scrolling feature and another for getting realtime updates. This is how the realtime snapshot listener looks like

The realtime snapshot listener using updatedAt

For that, we utilise updatedAt and set the limit to 1. That means we only observe the newest document in the whole collection. To get it to work, we need to always update the updatedAt field in the document when we change some data. It’s best to use FieldValue.serverTimestamp(). Some users might manipulate the time on their phone, set it to a future time and break the realtime update system.

Update the list

When the data arrives from the snapshot listener we have to examine the current state of our list. There are 3 options that can happen:

  1. The updated document is new
  2. The updated document is displayed in our list, aka we have it in our data source
  3. The updated document is old and not in our list

In case 1, just put it on the top of the list. Case 2 have to replace the current displayed data with the new data. We ignore case 3 because we might lazy load it anyways later, when the user scrolls further down.

Support endless scrolling

As soon as the user scrolls down and hit the end of the list, we want to load the next badge of data. To use that we use the following query

A Firestore query to page through a collection

You might notice, that we do not attach a realtime listener here. It’s a plain “boring” query. One awesome part is, that the start(after: works fine with the timestamp we save in each document.

Push the result of the query to the end of our list, reload the table and we are good to go.

The Code

I want to highlight some parts of the whole code below

  • The most of it is just boilerplate for a normal UITableView
  • I’m using SnapKit to create the constraints. It’s a really neat dependency and makes the whole autolayout stuff easy
  • You have to setup a Firebase project to run the code
  • The loadData function uses the new async/await syntax. I was quite surprised after I saw that Firebase already supports it. You need Xcode 13.2 and the latest deployment target is iOS 13. For once Apple backported something 🥳
  • The loadMorePending fixes a bug where the bouncing at the bottom of the table didn’t work.

--

--

Hanno Weiß

proud father of two amazing little kids, software developer, germany cologne