Bolts – BFTask, a promise of a future result or error


Tap on time to skip ahead


Let’s start with looking at BFTask, which again is basically a promise. I want to talk about how the promises are used from the perspective of this initial view controller, which is called the Champion View Controller. It displays in alphabetical order all the champions with what’s called the splash screen image for that champion, it’s just a square image. I query a local database for all the champions, their names and the url of the splash image. We’re going to see this from the perspective of that view controller how I use promises to query the data, get back results and display it.


Let’s navigate in the Xcode to the view controller that I just showed you. That is, the NIOChampionCollectionViewController. Let’s look at the implementation here. The thing I want to focus on is getting the data for the view. This begins with the viewDidLoad method which gets called. This line here is where we query the champions, so let’s go look at this method.


You’ll notice that this method here, if we weed out all the stuff that doesn’t matter, we get a query task, which is a command. We set some properties on it. We tell it what we want to query, what columns we want back and how we want it sorted. I want it to query the Champions, I want the champion name, the url of the splash image and I wanted the data to be sorted in name order ascending. I execute this command, called query task, and I called it’s run async method, and we know from before that this returns a BFTask, which is a promise.


I’m going to skip over a lot of things here that I’ll cover in another video. This is the continuation. This says, “run this query and when you get a result back, continue with this block.” So it runs this continuation when the asynchronous operation of running the query completes. Here what I’m doing is I’m stopping the animated spinner, I’m hiding a loading label and then I check to see if there’s an error or if I had a successful result. If I had a successful result, I get back a cursor with data in it. What happens here is that I just iterate through my cursor. The point is that this query ran asynchronously, gave me back a promise, and I continued here with this code when it completed later.


Let’s follow this through to the query task and let’s see how this works with the promise. I’m going to go over here and actually look at the implementation. You’ll see that the runAsync method returns a promise, but it simply just delegates the call to something called a content resolver and passes all these parameters to it.


Let’s jump over there and look at this. This also declares that it returns a promise. What happens here is- I’m going to skip this executor stuff for now. For now, just know that this causes this block right here, this block of code, to run on a different thread. Immediately, this line right here creates a new promise that gets returned immediately to the caller. The UI immediately gets a promise but it jumps this work over to a back room thread. What’s happening now is that at some point when the queue drains and this work actually runs on this back room thread, is this code tries to find a content provider that can fulfill the query for this URI, which is against the champion table. If it doesn’t find one, it creates a new promise, which it returns, that resolves or fails with an error.


Here is a common use for BFTask, which is a promise, and that is Create a new promise that resolves to an error. That will cause this request chain to stop and an error will be returned to the UI in its continuation. If it does find a content provider, it makes asynchronous request to that content provider to perform that query and it gets back a cursor. What it does now, it also- that content provider could have generated an error itself and it determines if there was an error, return a different promise that resolves to the database error, otherwise return a successful promise that resolves to the result. In either case, this promise that came here has a continuation on it which for now just understand that this jumps the handling of the response onto a different thread. Not the thread that this ran on, but a different thread. And it simply just returns this task that came in, which is the promise.


That’s kind of a lot to get your head around but it’s actually quite simple. We run some code on a background thread, return a promise immediately, and when that code actually runs, we return the result of that as a promise. Eventually that makes itself back to the UI on the main thread and the data gets displayed in the UI.


Let’s just jump back to the UIViewController here just to see what happens. Again, I run the query asynchronously, I have the continuation that runs on the main thread and I simply take the response, which is a cursor, that gets the result from the promise and just determines if I have rows in the cursor then I want to reload my UICollectionView based upon the data in the cursor.


I encourage you to look through this example, how the view uses a promise to resolve an asynchronous query result that gets run in the background, resolved on the main thread and the data gets displayed in the UI. This pattern is repeated over and over in this application. The key concepts here are: understanding how to create a promise with an error or a result, and then the continuations that you specify on your promises. As a reminder, all the continuations declare as an argument to that continuation the promise that was resolved, either with an error or a successful completion.