How To Use Blocks in iOS 5 Tutorial – Part 2

This is a blog post by iOS Tutorial Team member Adam Burkepile, a full-time Software Consultant and independent iOS developer. Check out his latest app Pocket No Agenda, or follow him on Twitter. Welcome back to our tutorial series on using blocks in iOS 5 – with some Storyboard/Interface Builder practice along the way! In […]

Version

Other, Other, Other

This is a blog post by iOS Tutorial Team member Adam Burkepile, a full-time Software Consultant and independent iOS developer. Check out his latest app Pocket No Agenda, or follow him on Twitter.

Welcome back to our tutorial series on using blocks in iOS 5 – with some Storyboard/Interface Builder practice along the way!

In the first part of the series, we used iOS 5.0 Storyboards to set up the view and wound up with a nice-looking interface, close to what you see to the right.

In this second and final part of the series, we finally get to blocks! We’ll talk about what blocks are, how their syntax works, how you use them, and cover tons of examples.

We’ll show you how you can use blocks with NSArrays, for UIView animations, Grand Central Dispatch, and much more!

So unblock your schedule and keep reading for some blocks practice and fun!

Getting Started: An Intro to Blocks

Blocks are a new feature that was introduced in iOS 4.0 and Mac OSX 10.6. Blocks can greatly simplify code. They can help you reduce code, reduce dependency on delegates, and write cleaner, more readable code.

Even so, Blocks are a feature that remains unused by some developers who may not totally grasp how to use them. But they are definitely a tool that you, as an Objective-C programmer, will want to have in you toolbox and know how to use.

Let’s look at the “Who, What, Where, Why, and When” of Blocks.

What Are These ‘Block’ Things and Why Are They So Important?

Why do I need these fancy block things?

At its core, a Block is a chunk of code that can be executed at some future time.

Blocks are first-class functions, which is a fancy way of saying that Blocks are regular Objective-C objects. Since they’re objects, they can be passed as parameters, returned from methods and functions, and assigned to variables.

Blocks are called closures in other languages such as Python, Ruby and Lisp, because they encapsulate state when they are declared. A block creates a const copy of any local variable that is referenced inside of its scope.

Before blocks, whenever you wanted to call some code and have it call you back later, you would typically use delegates or NSNotificationCenter. That worked fine, except it spreads your code all over – you start a task in one spot, and handle the result in another.

Blocks are nice because they keep your code related to handling a task all in one place, as you’ll see soon.

Who Are Blocks For?

YOU! Blocks are for everyone! Seriously though, Blocks are for everyone and everyone WILL use Blocks. Blocks are the future, so you might as well learn them now. Many built-in framework methods are being rewritten or augmented with Block-based versions of existing functionality.

This is how the Block is actually created. Notice that this has a different structure than the Block declaration. It begins with the ^ symbol and is followed by the parameters, which must be named at this point, and must match the type and order of the parameter list of the Block declaration to which it’s assigned. This is followed by the actual code.

When you define Blocks, the return type is optional and can be inferred from the return type in the code. If there are multiple return statements, they must all be of the same type (or cast to the same type).

Here’s an example of a Block definition:

^(int number1, int number2){ return number1+number2 }

If we put the Block declaration and definition example together, we get a full statement:

The "stop" variable in the above method might not make much sense to you. But it will become clearer when you look at the Block-based approach for the same method. The Blocks approach provides for a "stop" variable which allows you to stop the loop processing at any point and we're simply duplicating that functionality here to provide equivalent code to the Blocks-based approach.

In the Block-specific code above, you might wonder what the "stop" variabl is. This is simply a variable that can be set to YES from within the block to stop further processing. This parameter is specified as part of the block to be used by the enumerateObjectsUsingBlock method.

The above is a pretty trivial example and it might be admittedly difficult to see the advantage of Blocks in this case. But there are two things I want to point out about the Blocks approach:

Simplicity. Using Blocks, we have access to the object, the object index in the array, and a stop variable, all without having to write any code. This means less code, which means less chance of a coding error (not that we make any coding errors).

Speed. There might be a slight speed advantage to using the Block method over the fast-enumeration method. This (possible) speed advantage is so minute in this case it is barely worth mentioning, but in more complex cases, the advantage becomes significant. (Source)

Example: UIView Animation

Let's take a simple animation that operates on a single UIView. It changes the view's alpha to 0 and moves the view down and to the right 50 points by 50 points, then removes the UIView from the superview. Easy, right?

If we look at these two methods, there are three advantages that stand out to me:

Simplifies code. With blocks, we don't have to do things like declare an entirely separate method for completion callback, or call the beginAnimations/commitAnimations lines.

Keeps code together. With blocks, we don't have to start the animation in one place, and have a callback method in another place. All of our code relating to animations is in one spot, which makes it easier to read and write.

Apple says so. This is an instance of Apple having used Blocks to rewrite functionality that existed prior to Blocks, and now the official Apple recommendation is to transition over to the Block-based method, if possible. (Source)

When Do You Use Blocks?

I think that the best advice that can be given here is to use Blocks when they are most appropriate. There might be times that you want to continue using old methods either because you want to maintain backwards compatibility or because you are more familiar with the older way of doing things. But each time you come to such a decision point, think whether Blocks would simplify your life and whether you can substitute a Blocks-based method instead. Then do what works best for you.

Of course, you might find yourself needing to use Blocks more and more in the future simply because many frameworks, both third-party and Apple's own, are being written and re-written to use Blocks. So start using Blocks now so that you're armed to face the future.

Back to the iOS Diner: Setting Up Model Classes

You'll be picking up right where you left off in Part One. If you didn't do Part One or just need a fresh start, you can download the project in its current state here.

Open the project in Xcode and switch to the Project Navigator. Right-click on the iOSDiner folder and select New Group. Name it “Models.”

Right-click on the new Models folder and click New File. Select Objective-C Class. Name the class “IODItem” and make it a subclass of NSObject.

For the file location, select the iOSDiner folder, and select New Folder to create a matching Models folder in the file system. Make sure the newly created Models folder is selected and then hit Create. This will create .h and .m files for the IODItem class.

Repeat this file creation process for a class called IODOrder. Right-click on the Models folder and click New File. Select Objective-C Class. Name the class “IODOrder” and make it a subclass of NSObject.

Make sure the Models folder is selected and then click Create.

Now you have all the classes and files you need set up. Time to get coding!

Setting Up Basic Properties in IODItem

Select IODItem.h. The first thing you're going to do is add the NSCopying protocol to the class.

Protocols are a way of setting up a contract of the methods the class will implement. Basically, if a class implements a certain protocol, then the class would need to implement certain methods that are required or optional, as defined for that particular protocol. To implement the NSCopying protocol, change IODItem.h to look like this:

@interface IODItem : NSObject <NSCopying>

Next, add the properties for the item's attributes. An item will have a name/title, price, and an image file. Add the following properties underneath the previous line. The complete .h file should look like this now:

Now switch over to IODItem.m and add the synthesizers for the properties by adding the following below the @implementation IODItem line:

@synthesize name;
@synthesize price;
@synthesize pictureFile;

If you build the code right now and look at it, you'll see a warning.

This warning is a reference to the NSCopying protocol that you added above. Remember how I said the protocol might define required methods? Well, the NSCopying protocol requires that -(id)copyWithZone:(NSZone *)zone be implemented. Since you haven't done that, the class is incomplete – hence the warning!

Setting Up Basic Properties in IODOrder

Next, we are going to work on the other class, IODOrder. This class will represent the order and the operations that go along with an order: adding an item, removing an item, calculating the total for the order, and printing out an overview of the order.

Switch to IODOrder.h and before the @interface section, add the following to let the IODOrder class know there is such a class as IODItem.

@class IODItem;

Inside the @interface section, add the following property:

@property (nonatomic,strong) NSMutableDictionary* orderItems;

This is the dictionary that will hold the items ordered by the user. Switch to IODOrder.m and import the IODItem class header at the top of the file:

#import "IODItem.h"

Next synthesise properties below the @implementation IODOrder line:

@synthesize orderItems;

Setting Up Basic Properties in IODViewController

Switch to IODViewController.h to add an instance variable and two properties. Replace the existing "@interface IODViewController : UIViewController" line with the following:

The currentItemIndex variable will keep track of which item the user is currently browsing in the inventory. Inventory is pretty self-explanatory; it's an array of the IODItems that we got from the web service. Order is an instance of the IODOrder class that stores the items currently in the user's order.

Hey, your first Block! Let's take a closer look at this code to see exactly what it's doing:

First, we define the array that will hold the return objects and an error pointer.

We use a regular NSData object to download the data from the web service, and then pass that NSData object into the iOS's new JSON data service to decode the raw data into Objective-C object types (NSArrays, NSDictionaries, NSStrings, NSNumbers, etc).

Next, we use the enumerateObjectsUsingBlock: method that we discussed earlier to convert the objects from regular NSDictionaries to IODItem class objects. We call the enumerateObjectsUsingBlock: method on the jsonInventory array and enumerate over it with a Block that casts the object passed to the Block as an NSDictionary object, uses that dictionary object to create a new IODItem, and finally adds that new item to the return inventory array.

Finally, we return the inventory array. Note that we return a copy of the array instead of returning it directly, because we don't want to return a mutable version. The copy method creates an immutable version you can safely return.

Now switch back to the IODItem.h and add the prototype for the method:

+ (NSArray*)retrieveInventoryItems;

Dispatch Queues and Grand Central Dispatch

Another concept that would be useful to learn is dispatch queues. Switch to IODViewController.m and add the following inside the @implementation block, just below the @synthesize statements.

dispatch_queue_t queue;

Then, inside the viewDidLoad method, add this line to the end of the method:

queue = dispatch_queue_create("com.adamburkepile.queue",nil);

The first parameter for the dispatch_queue_create method is the queue name. You can name it however you want, but it has to be unique to the entire system. This is why Apple recommends a reverse DNS-style name.

You need to release the queue when you deallocate the view controller. Even though you're using ARC with this project, ARC doesn't manage dispatch queues, so you need to manually release it. But remember that with ARC enabled, you don't have to call [super dealloc] inside of the dealloc method. So add the following to the end of your code:

-(void)dealloc {
dispatch_release(queue);
}

Now let's put that queue to use. Add the following three lines to viewDidAppear below the existing code:

Something's not quite right, is it? You're using the retrieveInventoryItems method that you defined on IODItem to call the web service, return the inventory items and assign them to the inventory array.

Remember, there was a five-second delay in that PHP web service script from Part One. But when we run the program, it doesn't say “Loading Inventory...” and then wait for five seconds before saying “Inventory Loaded.” It seems to start up and then say “Inventory Loaded” after five seconds without saying “Loading Inventory....”!

The problem is this: the call to the web service is blocking and freezing the main thread and won't allow it to change the label text. If only there was a separate queue you could use for that long operation that didn't interfere with the main thread...

OH WAIT! WE MADE A SEPARATE QUEUE! This is where Grand Central Dispatch and Blocks can help us solve a problem very simply. Using Grand Central Dispatch, we can assign work (in the form of Blocks) to be done on our other queue that is separate and doesn't block the main thread.

Note that we use two different blocks here, that have a void return value and no parameters.

Give it another run and everything should work perfectly.

Did you wonder why we made that second call to dispatch_async to set the text label? When you set the label text, you're updating a UI element, and anything that updates anything on the UI has to be executed on the main thread. So we make another call with dispatch_async, but this time we get the main queue and execute our Block on the main queue.

This method of jumping or nesting queues from a background queue to the main queue is quite common when an operation takes a long time and there is a subsequent action which involves updating a user interface element.

Let's examine what this function does. But in order to do that, I need to explain why all of this is necessary. The IODOrder object contains a property called orderItems, which is a dictionary of key-values pairs. The key will be an IODItem and the value will be a NSNumber, specifying how many of that particular item has been ordered.

This is all fine in theory, but a little quirk of the NSDictionary class is that when you assign something as a key, it doesn't actually assign the object, it makes a copy of the object and uses that as the key. This means the object you use as the key must conform to the NSCopying protocol (which is why you had to implement NSCopying on IODItem).

The fact that the key in the orderItems dictionary and the IODItem in the inventory array are not technically the same object (even though they have the same properties) means that you can't perform a simple search for the key. Instead, you must compare the name and price of each object to determine if they are the same item. That's what the above function does: it compares the properties of the keys to find one that matches the one being searching for.

With that said, here's what the code does:

Here you look at all the keys in the orderItems dictionary and use the indexesOfObjectsPassingTest: method to find the keys that match the name and price. This is another example of a Block method. Notice the BOOL after the ^. This is the return type. This particular method works on an array and uses the Block to compare two objects to return the indexes of all the objects that pass any specific test specified by the the Block.

This simply takes the returned indexes and returns the first one.

Return nil if a matching key object isn't found.

Don't forget to add the method prototype to the IODOrder.h:

- (IODItem*)findKeyForOrderItem:(IODItem*)searchItem;

Now switch to IODViewController.m and add the following method to the end of the file:

This is the longest of the three helper methods, but it's quite simple when you look at it. This method looks at the various states the program can be in and determines if the buttons should be enabled or disabled.

For example, if the currentItemIndex is 0, the previous item button is disabled because you can't go back farther than the first item. If the orderItems count is 0, then the total order button is disabled, because there's nothing to calculate the total for.

Add the prototypes for these two methods to IODViewController.h:

- (void)updateCurrentInventoryItem;
- (void)updateInventoryButtons;

All right! Equipped with these helper methods, it's time to make some magic happen. Go back to viewDidAppear in IODViewController.m and add the following above section #1:

HEY! Nom nom, Hamburger... I'd like to see some other food though, so let's get those other buttons working.

The ibaLoadNextItem: and ibaLoadPreviousItem: methods have already been stubbed out in IODViewController when you created the action in the storyboard. So let's add the following code to the stubs to implement those methods in IODViewController.m:

With the aid of the helper methods you created above, switching items is a simple matter of changing the currentItemIndex and refreshing the onscreen information. What could be easier? Now you have an entire buffet of food to choose from!

Compile and test how easy it is to flip through the whole menu of yummy food.

Add and Remove Current Item

Unfortunately, you have a menu but the waiter isn't taking orders. Or, in other words, the add/remove item buttons don't work. Time to change that.

You need another helper method in the IODOrder class, so switch over to IODOrder.m and add the following method:

This is simply the getter for the orderItems property. If orderItems has been assigned something, it returns that object. If it hasn't been assigned anything, it creates a new dictionary and assigns that to orderItems, and then returns it.

Next you're going to work on the orderDescription method. This method will provide the string used when the app prints on the chalkboard. Add the following code to IODOrder.m:

This is the string for the order description. Each item in the order will be appended to this string.

This chunk of code takes an array composed of the keys in the orderItems dictionary and uses a Block method, sortedArrayUsingComparator:, to sort those keys by name.

This code then uses that sorted array of keys to call that same old enumerateObjectsUsingBlock: method that by now you know and love. For each key, you convert it to an IODItem, get the value (the quantity in the order), and add that string to the orderDescription string.

Finally, you return the orderDescription string, but again you return a copy of it so that it's an immutable version.

Switch to IODOrder.h and add the prototypes for those two methods:

- (NSMutableDictionary *)orderItems;
- (NSString*)orderDescription;

Now that you can get the current order string from the order object, switch back to IODViewController.m and add a method to call it. You can add this method to the end of the file.

This method looks at the number of items in the order. If the number of items is zero, it returns a static string indicating that there are no items in the order. Otherwise, the method uses the orderDescription method defined in IODOrder to display a string of all the items in the order and their quantities.

Add the prototype for this method to IODViewController.h:

- (void)updateOrderBoard;

Now that you can update the board with the current order, do so by replacing section #2 of the viewDidAppear method in IODViewController.m:

Do note that when we remove an item from the order, we only need to do something if the object is found in the order. If the item is found, we read the value, decrement it, remove the dictionary object, and reinsert the object with the new quantity if the quantity is greater than 0.

For both of these methods, all we do is get the current item from the inventory array, pass that object to the addItemToOrder: or removeItemFromOrder: method that we defined on IODOrder, and update the UI with our helper methods.

Give it another build and run. You should now see that you can add items to the order and that the chalkboard updates to display your order.

UIAnimation

Let's go back and add a little visual flair with another block method. Replace the ibaRemoveItem: and ibaAddItemMethod: code with the following:

The above might seem like a lot of code but it's really quite simple. The first new code segment we added just creates a UILabel and sets its properties. The second segment is an animation that moves the label that we just created. This is an example of the Block UIView animation system that we outlined at the beginning of this tutorial.

Compile and run and you'll see a nifty animation which shows the items being added and removed each time you tap the "+1" or "-1" buttons.

Getting the Total

The last helper method we are going to add to IODOrder.m is the method to total the order and return the result.

We define and initialize the variable that will accumulate the total. Note the __block keyword. We will be using this variable inside of a Block. If we do not use the __block keyword, the Block we create below would create a const copy of this variable and use that when referenced inside the Block, meaning we would not be able to change the value inside of the Block. By adding this keyword we are able to be read from AND write to the variable inside of the Block.

Then, we define a Block variable and assign it a Block that simply takes a price and a quantity and returns the item total based on price and quantity.

This code segment goes over every object in the orderItems dictionary using a Block method, enumerateKeysAndObjectsUsingBlock: and uses the previous Block variable to find the total for each item for the quantity ordered and then adds that to the grand total (which is why we needed the __block keyword on the total variable since it is being modified inside of a Block).

Once we are done calculating the total for all the items, we simply return the calculated total.

Go back to IODOrder.h and add the prototypes:

- (float)totalOrder;

The last thing to do is to add the total calculation functionality to the app. All the heavy work will be done by the totalOrder method and so, all we have to do is show the calculated total to the user when they hit the total button and trigger the ibaCalculateTotal: action. So fill in the ibaCalculateTotal: stub in IODViewController.m with the following:

enumerateObjectsAtIndexes:usingBlock: - Same as enumerateObjectsUsingBlock: except you can enumerate a specific range of items in the array instead of all the items. The range of items to enumerate is passed via the indexSet parameter.

indexesOfObjectsPassingTest: - The Block returns an indexset of the the objects that pass a test specified by the Block. Useful for looking for a particular group of objects.

Since a block is just an Objective-C object, you can store it in a property so you can call it later. This is useful if you want to call the method after some asynchronous task has completed, such as a network task. Here's an example:

Where to Go From Here?

The final completed project code can be downloaded here. If you are familiar with git, I also have the project hosted here at github, with commits at each step if you get stuck at a step.

Hopefully in creating this simple app, you've been able to see the power and simplicity that Blocks add to your dev toolkit, and gotten some ideas of how you can use them in your own projects.

If this is your first exposure to blocks, you've taken a pretty big step. With more and more apps needing asynchronous, networked, and multi-threaded code, blocks will definitely be considered required learning.

There's still more to learn about blocks - specifically in terms of variable capture and the __block keyword. More information about blocks and Grand Central Dispatch can be found here:

This is a blog post by iOS Tutorial Team member Adam Burkepile, a full-time Software Consultant and independent iOS developer. Check out his latest app Pocket No Agenda, or follow him on Twitter. Welcome back to our tutorial series on using blocks in iOS 5 – with some Storyboard/Interface Builder practice along the way! In […]