Using PFQueryTableViewController with Parse Local Datastore in Swift

by Jeremy Sabath

Parse’s PFQueryTableViewController gives your app a database-integrated UITableView right out of the box. And that’s great! But in many cases you can’t be sure that your users will always have an internet connection. While PFQueryTableViewController is great, it doesn’t do so well when not connected to the internet. For a recent project, we came up with an effective way to use the PFQueryTableViewController with Parse’s awesome local datastore functionality to create a more robust user experience.

Sad empty list

For a recent project at Fresh Tilled Soil, we elected to use Parse to manage app data. It’s extremely easy to set up, it’s fast, it’s reliable, and it has a strong iOS SDK. Included in the SDK is the ParseUI framework which includes some “Parsified” subclasses of native iOS UI elements to make Parse integration even easier. One of the more popular subclasses is the PFQueryTableViewController. It provides a simple way to populate a list with Parse objects and gives you some nice things for free, like asynchronous image loading, pull-to-refresh, and pagination. If you aren’t set up with Parse and a PFQueryTableViewController, try this tutorial and then continue here (20-30 min). We’ll wait 🙂

OK, now that you have a basic PFQueryTableViewController up and running, let’s get it working with local datastore. To provide the best user experience, you want your app to get data from Parse whenever it can and store it locally, so that when the app loses its internet connection, it can still rely on the local datastore to provide app functionality. To do this, we’ll adapt a solution I encountered on Stack Overflow that almost gets it right.

First, add a convenience method called baseQuery with the query you want to use to get data from Parse. For more information on constructing Parse queries, check out the documentation.

func baseQuery() -> PFQuery {
  var query = PFQuery(className: "MyClassName") // Replace with the class name for the Parse data you're interested in.
  query.whereKey(MyKey, equalTo: MyValue) // Just like with a regular PFQuery you can filter and sort.
  return query

Then, update your queryForTable method to return baseQuery().fromLocalDataStore

override func queryForTable() -> PFQuery {
    return self.baseQuery().fromLocalDatastore()

This is the method that gets called when the view loads, and when the user pulls to refresh, so we need to employ a little trickery to get the functionality we want.

Create another helper function called refreshLocalDataStoreFromServer() and set it up as follows:

func refreshLocalDataStoreFromServer() {
  self.baseQuery().findObjectsInBackgroundWithBlock { ( parseObjects: [AnyObject]?, error: NSError?) -> Void in
    if error == nil {
      println("Found \(parseObjects!.count) parseObjects from server")
      // First, unpin all existing objects
      PFObject.unpinAllInBackground(self.objects, block: { (succeeded: Bool, error: NSError?) -> Void in
          if error == nil {
            // Pin all the new objects
            PFObject.pinAllInBackground(parseObjects, block: { (succeeded: Bool, error: NSError?) -> Void in
              if error == nil {
                // Once we've updated the local datastore, update the view with local datastore
                self.shouldUpdateFromServer = false
              } else {
                println("Failed to pin objects")
      } else {
        println("Couldn't get objects")

We’ll call this method every time we load objects from the local data store. When we refer to “pinning” objects, we mean storing them in the local datastore. The PFQueryTableViewController keeps track of the objects it’s managing, so we can easily unpin them from the local datastore and replace them with the updated data from the server as soon as possible. By now you may be seeing the full picture. When the view loads, it populates the table view with objects returned by the query in queryForTable. Then, once those objects have loaded, we need to hit the server to get the latest data and save it locally. To do that, we’ve got a little more code to add.

You’ll notice that in the refreshLocalDataStoreFromServer() method we reference an instance variable called shouldUpdateFromServer. We use this Bool to ensure that every time we load from the local datastore, we hit the server once and then stop. Otherwise, scary recursive loops happen. And now is not the time for recursive loops. Create this instance variable and initialize it to “true”. That way, the app will hit the server immediately on launch.

var shouldUpdateFromServer:Bool = true

Override the objectsDidLoad() method with the following:

override func objectsDidLoad(error: NSError?) {
  // If we just updated from the server, do nothing, otherwise update from server.
  if self.shouldUpdateFromServer {
  } else {
    self.shouldUpdateFromServer = true

This delegate method gets called when the objects have been loaded, so it’s a perfect place to kick off a follow-up query to the server if necessary.

And that’s it! If you run the app now and try it out, you should see in the console that the app loads objects from the local datastore, updates the local datastore with the results from the server, and then reloads the table view. This addition to PFQueryTableViewController functionality helps protect users in the event that they lose their data connection. Don’t leave your users out in the proverbial cold! Protect their experience with this simple extension.

At Fresh Tilled Soil, we obsess over creating the best product experiences out there. If you’re interested in improving the user experience of your product, get in touch! We’d love to help.

About Fresh Tilled Soil

Fresh Tilled Soil is a Boston-based user interface and experience design firm focused on human centered digital design