---
title: "Swift Closures and Firebase - Handling Asynchronous Data"
description: "Swift Closures and Firebase - Handling Asynchronous Data"
published: "2018-07-02"
last_updated: "2018-07-02"
authors:
  - "Jen Person (Developer Relations Engineer)"
categories:
  - "Firestore"
  - "iOS"
  - "Swift"
canonical_url: "https://firebase.blog/posts/2018/07/swift-closures-and-firebase-handling"
---

# Swift Closures and Firebase - Handling Asynchronous Data

*Published July 2, 2018 by Jen Person (Developer Relations Engineer)*

---
Firebase has an impressive feature list for getting your apps up and running quickly. I love that I can go from idea to working app in a matter of hours! But for developers who haven't had much experience with asynchronous programming, it's easy to get bogged down in the details. Heck, I've been working with Firebase for two years and I still find myself pausing to think about how I want to handle asynchronous calls. What will my view look like while waiting to populate data? How should I set up my data class so that it can alert the view controller about updates? After lots of practice and exploring options, I feel like I finally have an answer that works for me.

Now I'm not going to get into the _what_ and _why_ of asynchronous programming with Firebase because Doug Stevenson already addressed this in [this great blog post](https://medium.com/google-developers/why-are-firebase-apis-asynchronous-callbacks-promises-tasks-e037a6654a93). Instead, I want to get into how to get a function that uses an async callback working the way you want it to in Swift.

![screen-shot-2018-05-24-at-11-28-51-amimage1](https://firebasestorage.googleapis.com/v0/b/first-class-blog.appspot.com/o/blog-assets%2F2018%2F07%2Fswift-closures-and-firebase-handling%2Foptimized%2Fscreen-shot-2018-05-24-at-11-28-51-amimage1.jpg?alt=media)

Take this example function I have below from this photo app I've created. It queries Cloud Firestore for 10 documents in a collection called "cats". Once the Cat data is downloaded, I want to use it to populate a UITableView with information about the cat photos.

```swift
// ViewController.swift

var dbRef = Firestore.firestore().collection("cats")  
var cats = [Cat]()

func downloadCats() {
  // order the posts by timestamp
  let query = dbRef.order(by: "timestamp", descending: true).limit(to: 10)
  query.getDocuments { snapshot, error in
    print(error ?? "No error.")
    // iterate through the documents and create Cat objects
    guard let snapshot = snapshot else { 
      return
    }
    for doc in snapshot.documents {
      let cat = Cat(snapshot: doc)
      self.cats.append(cat)
    }
  }
  print(cats) // 1 what will this print?
  tableView.reloadData()
}
```

What will be printed at "1"? Since the call to `getDocuments` is asynchronous, the closure will be run when the download of data is completed sometime in the future. Commands that I write following the closure will be run immediately -- they do not wait for the work in the closure to be complete. So chances are, in the scenario above, that `print` statement is going to print an empty array`.` The `UITableView` will reload data immediately, so any `Cat` objects that were supposed to populate the view will not appear. That's not the behavior we want. So what can we do?

One simple option is to move the functionality I want into the closure, like so:

```swift
// ViewController.swift

func downloadCats() {
  let query = dbRef.order(by: "timestamp", descending: true).limit(to: 10)
  query.getDocuments { snapshot, error in
    print(error ?? "No error.")
    for doc in snapshot!.documents {
      let cat = Cat(snapshot: doc)
      self.cats.append(cat)
    }
    print(cats) // 1 what will this print now?
    tableView.reloadData()
  }
}
```

What is printed at "1" now? We should now see the full array of `Cat` objects, and when `tableView.reloadData()` is called, the `UITableView` can populate with the data of those `Cat`s, assuming you've set up your `UITableViewDataSource` for it. This is a viable method for handling data in the closure, but it means you'll have to include this functionality in your `UIViewController`. As you add more functionality, such as different kinds of queries, Auth, Cloud Storage, and callable Cloud Functions, you'll quickly have a _Massive View Controller_ on your hands.

I like to keep this functionality separate from my view controller whenever possible. I can do this by wrapping `getDocuments()` in a function with a completion handler. If I include a completion handler in this function, I can call `completion()` when the array of Cats is populated.

```swift
// CatManager.swift

var dbRef = Firestore.firestore().collection("cats")
var cats = [Cat]()


//  function includes an completion handler, marked with @escaping
func downloadCats(completion: @escaping () -> Void) {
   let query = dbRef.order(by: "timestamp", descending: true).limit(to: 10)
    query.getDocuments { snapshot, error in
      print(error ?? "No error.")
      self.cats = []
      guard let snapshot = snapshot else { 
          completion()
          return
      }
      for doc in snapshot.documents {
        let cat = Cat(snapshot: doc)
        self.cats.append(cat)
      }
      completion()
    }
  }
}
```

Then inside the view controller, I can call my function like this:

```swift
// ViewController.Swift

func initializeCats() {
  activityIndicator.startAnimating()
  CatManager.sharedInstance.downloadCats() {
    // Data for UITableView is populated from the CatManager singleton
    self.tableView.reloadData()
    self.activityIndicator.stopAnimating()
  }
}
```

Now I'm controlling the view from the view controller, reloading the `tableView` and indicating when the `activityIndicator` should run. The data is being handled in the `CatManager` class. This does, however, require a singleton, so while it may prevent a massive view controller from developing, singletons come with their own [host of issues](https://cocoacasts.com/are-singletons-bad).

Instead of passing void in the completion, I can pass some useful values: an array of `Cat`s and an optional `error`.

```swift
// CatDownloader.swift
// Use instead of CatManager

func downloadCats(completion: @escaping ([Cat], Error) -> Void) {
   var catArray = [Cat]()
   let query = dbRef.order(by: "timestamp", descending: true).limit(to: 10)
    query.getDocuments { snapshot, error in
      if let error = error {
        print(error)
        completion(catArray, error)
        return
      }
      for doc in snapshot!.documents {
        let cat = Cat(snapshot: doc)
        catArray.append(cat)
      }
      completion(catArray, nil)
    }
  }
}
```

With this function, since I pass the error in the closure, the view controller can be made aware of an error and display it to the user. Here I made a function called `alert()` that displays a UIAlertController. I can also create an instance of `CatDownloader` in the class because I don't need to depend on a single `CatManger` to keep track of the `Cat` array.

```swift
// ViewController.swift

// create an instance of CatDownloader
let catDownloader = CatDownloader()
//…

func initializeCats() {
  activityIndicator.startAnimating()
  catDownloader.downloadCats() { catArray, error in
    self.activityIndicator.stopAnimating()
    if let error = error {
       self.alert(title: "Error", message: error.localizedDescription)
       return
    }
    self.cats = catArray
    self.tableView.reloadData()  
  }
}

func alert(title: String, message: String) {
  let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
  let action = UIAlertAction(title: "OK", style: .default, handler: nil)
  alertController.addAction(action)
  navigationController?.present(alertController, animated: true, completion: nil)
}
```

You may want to display a custom error message to the user instead of the error description, but you can see how the closure made it easy to get the info I needed to the right place in my app. You can use this same configuration for other Firebase features that incorporate closures, including the Realtime Database, Authentication, and ML Kit.

And there you have it! Now go forth and write your own functions with closures to help you update your views. What other examples would you like to see? Do you have any cool solutions you've found that you'd like to share? Reach out to me on Twitter at [@ThatJenPerson](https://twitter.com/ThatJenPerson) to tell me all about it!
