Sunday, March 18, 2012

Delete a row from a table in Objective-C

Here's a gotcha I came across while deleting a row from a UITableView in iOS. Basically, the order you delete the record from the view and the underlying data source can be crucial.

To delete an item from a UITableView, for this example, I'm going to assume your delegate and datasource are both set to be your TableViewController. This would be pretty common anyway. To give your tableview delete/insert support is actually very straight-forward.
  1. Simply implement the tableView:commitEditingStyle:forRowAtIndexPath: method (on the data source - in this case the controller). 
  2. You must then send deleteRowsAtIndexPaths:withRowAnimation: or insertRowsAtIndexPaths:withRowAnimation: to the table view to direct it to adjust its presentation. 
  3. And then update the corresponding data-model array by either deleting the referenced item from the array or adding an item to the array. 
This is described in the official documentation here

BUT WAIT! If you attempt to remove the item from the view first, then the data source you may run into an error. Something like this (for example if you tried to delete 1 row from a list of 10)...
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (10) must be equal to the number of rows contained in that section before the update (10), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted).
Internally deleteRowsAtIndexPaths calls the tableView's numberOfRowsInSection. If the data source has not changed then this will return the same count as before the delete (or insert for that matter). The runtime does not like this and tells you as much in the exception - it expected something to change.

So, simply remove the item from the underlying data source first and then call deleteRowsAtIndexPaths. Which is the other way around to the way it is described in the Apple docs (which are usually excellent). Here's some code...
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.
    return [recentImages count];
}

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {

    // Delete from underlying data source first!
    recentImages = [recentImages removeObjectAtIndex:indexPath.row];
    
    // Then perform the action on the tableView
    if (editingStyle == UITableViewCellEditingStyleDelete)
    {   
        [tableView beginUpdates];
        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                             withRowAnimation:UITableViewRowAnimationFade];        
        [tableView endUpdates];
    }
    
    // Finally, reload data in view
    [self.tableView reloadData];
}

Tuesday, March 6, 2012

A (Very) Gentle Introduction to Protocols in Objective-C

Protocols are a powerful abstraction in Objective-C OOP. This is a gentle introduction and should be treated as such. For comprehensive cover of Protocols, the Apple Developer Guide is the de facto resource.  I'm going to focus on formal protocols here and show an example of how they can be used to allow a View to call a method on its ViewController without breaking good design principles.

I come from a C# background so I think of them rather like Interfaces but with some differences. The similarities are that they both offer a contract that allow objects to make use of other objects behavior without the need for subclassing. Developers tend to favor Composition over inheritance as it can provide a more flexible way of building a system. It is a good design principle and worth understanding the benefits of to truly appreciate Protocols or Interfaces.

Protocols have some interesting features and although some of the ideas are the same as Interfaces, the terminology can take a bit of getting used to.
  • The object that declares the prototype will be the object that calls methods on it. 
  • Another object may "adopt" the prototype, meaning it implements the methods outlined in the prototype. 
Protocols are essentially lists of methods. Some methods are required to be implemented and some can be marked as optional. If the "adopter" of an interface (i.e. the object that implements it and provides the behavior) does not implement all required methods you will get a compiler warning.

@protocol MyProtocol
 
- (void)requiredMethod;
 
@optional
- (void)anOptionalMethod;
- (void)anotherOptionalMethod;
 
@required
- (void)anotherRequiredMethod;
 
@end
An basic example where I have started to use Protocols is when a View needs to call a method on its ViewController. A View should not "have" a ViewController directly. Sometimes a View does need to call a Controller to receive more data or updated data. You can do this through a Protocol.
  • I declare Protocol in my View.h 
  • I call the protocol method I need in my View.m 
  • I implement the Protocol in my ViewController. 
  • I set the ViewController to be the delegate for that protocol.
View.h
// Here a declaration of a View includes the declaration of a Protocol. 
// This means the view will want to call methods declared in this protocol
// and another object will have adopted the protocol and implemented the methods to provide behavior.
@class GraphView;

@protocol GraphViewDelegate
- (double)evaluateExpressionForX:(double)x;
@end

@interface GraphView : UIView { 
    id  delegate;
}
@end
View.m
// The declaring object actually goes ahead and calls the method it needs
// Note: the declaring object has no idea who implements the protocol or how they implement it. Nice.
double y = [self.delegate evaluateExpressionForX:someValue];
ViewController.h
// Declare that you adopt the protocol in your interface declaration.
@interface GraphViewController : UIViewController 
ViewController.m

- (void)viewDidLoad {
    // State that you will take care of messages from graphView (provided you have the protocol implementation!)
    self.graphView.delegate = self;
}

- (double)evaluateExpressionForX:(double)x {
    // do some calculations
    return result;
}
That's it. Now the View can message the delegate and get a response back. The View has no idea who implemented the protocol or how they went about implementing it. The Controller is in charge of declaring that it will look after the messages from the View through the Protocol. This is one short quick example of the power of Protocols.