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 […] By .

Leave a rating/review
Save for later
Share
You are currently viewing page 4 of 6 of this article. Click here to view the first page.

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:

- (NSMutableDictionary *)orderItems{
    if (!orderItems) {
        orderItems = [NSMutableDictionary new];
    }
    return orderItems;
}

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:

- (NSString*)orderDescription {
	// 1 - Create description string
    NSMutableString* orderDescription = [NSMutableString new];
	// 2 - Sort the order items by name
    NSArray* keys = [[self.orderItems allKeys] sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
        IODItem* item1 = (IODItem*)obj1;
        IODItem* item2 = (IODItem*)obj2;
        return [item1.name compare:item2.name];
    }];
	// 3 - Enumerate items and add item name and quantity to description
    [keys enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        IODItem* item = (IODItem*)obj;
        NSNumber* quantity = (NSNumber*)[self.orderItems objectForKey:item];
        [orderDescription appendFormat:@"%@ x%@\n", item.name, quantity];
    }];
	// 4 - Return order description
    return [orderDescription copy];
}

I'll break this down a little for you:

  1. This is the string for the order description. Each item in the order will be appended to this string.
  2. 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.
  3. 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.
  4. 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.

- (void)updateOrderBoard {
    if ([order.orderItems count] == 0) {
        ibChalkboardLabel.text = @"No Items. Please order something!";
    } else {
        ibChalkboardLabel.text = [order orderDescription];
    }
}

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:

	// 2 - Use queue to fetch inventory and then then update UI
	dispatch_async(queue, ^{
		self.inventory = [[IODItem retrieveInventoryItems] mutableCopy];
		dispatch_async(dispatch_get_main_queue(), ^{
			[self updateOrderBoard]; // <---- Add
			[self updateInventoryButtons];
			[self updateCurrentInventoryItem];
			ibChalkboardLabel.text = @"Inventory Loaded\n\nHow can I help you?";
		});
	});
}

I realize this is a bit pointless since you just overwriting it with the initial text a couple of lines down, but for sake of consistency, it's not a bad idea.

The next method you're going to implement will add an item to the order. Switch to IODOrder.m and add this method:

- (void)addItemToOrder:(IODItem*)inItem {
	// 1 - Find item in order list
    IODItem* key = [self findKeyForOrderItem:inItem];
	// 2 - If the item doesn't exist, add it
    if (!key) {
        [self.orderItems setObject:[NSNumber numberWithInt:1] forKey:inItem];
    } else {
		// 3 - If item exists, update the quantity
        NSNumber* quantity = [self.orderItems objectForKey:key];
        int intQuantity = [quantity intValue];
        intQuantity++;
		// 4 - Update order items list with new quantity
        [self.orderItems removeObjectForKey:key];
        [self.orderItems setObject:[NSNumber numberWithInt:intQuantity] forKey:key];
    }
}

Here's the step-by-step explanation:

  1. You use the method you previously created to find the key for the orderItem dictionary entry. Remember, if the object isn't found, it simply returns nil.
  2. If the object wasn't found in the order at this point, add the item to the order with a quantity of 1.
  3. If the object was found, we read the quantity value, store it, and increment it.
  4. Finally, we remove the the original entry and insert a new version with the updated quantity value.

The removeItemFromOrder: method is very much the same as the addItemToOrder: method. Add the following code to IODOrder.m:

- (void)removeItemFromOrder:(IODItem*)inItem {
	// 1 - Find the item in order list
    IODItem* key = [self findKeyForOrderItem:inItem];
	// 2 - We remove the item only if it exists
    if (key) {
		// 3 - Get the quanity and decrement by one
        NSNumber* quantity = [[self orderItems] objectForKey:key];
        int intQuantity = [quantity intValue];
        intQuantity--;
		// 4 - Remove object from array
        [[self orderItems] removeObjectForKey:key];
		// 5 - Add a new object with updated quantity only if quantity > 0
        if (intQuantity > 0)
            [[self orderItems] setObject:[NSNumber numberWithInt:intQuantity] forKey:key];
    }
}

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.

Switch to the IODOrder.h and add the prototypes:

- (void)addItemToOrder:(IODItem*)inItem;
- (void)removeItemFromOrder:(IODItem*)inItem;

Now we can switch to IODViewController.m and add code to the add and remove item stubs to call the newly created helper methods:

- (IBAction)ibaRemoveItem:(id)sender {
    IODItem* currentItem = [self.inventory objectAtIndex:currentItemIndex];
    [order removeItemFromOrder:currentItem];
    [self updateOrderBoard];
    [self updateCurrentInventoryItem];
    [self updateInventoryButtons];
}

- (IBAction)ibaAddItem:(id)sender {
    IODItem* currentItem = [self.inventory objectAtIndex:currentItemIndex];
    [order addItemToOrder:currentItem];
    [self updateOrderBoard];
    [self updateCurrentInventoryItem];
    [self updateInventoryButtons];
}

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.