Search
Follow
Recent Comments

Entries in cocoa (9)

Saturday
Oct022010

Dynamically loading new rows into a table

A pattern that is common to many mobile apps is to allow the user to scroll down a long list of items that must be retrieved over the network from a remote server. Retrieving all of the items up front would be slow and give a very poor user experience. What typically happens is that a block of items is retrieved and presented to the user in a table view. The last row in the table shows a “Load More…” message which when tapped triggers the retrieval of the next block of items which are then added to the end of the table.

This approach can be seen in Apple’s own App Store and You Tube applications. It is also employed by HTML-based web applications such as Google Reader. It has the advantage of minimising the network traffic and also of not making the user wait too long before seeing the table updated.

Example App: Feeder

To explore how to implement a table view that dynamically loads new rows I will build an example RSS feed reader application. Given a particular RSS feed my app should retrieve a defined number of records from the feed and allow the user to view each individual post. Tapping the “Load More…” cell in the table view should update the table with any additional items in the feed.

To get started I am going to cheat by avoiding the complexity of actually retrieving and parsing an RSS feed and focus on the implementation of the table view. I may come back to the network and parsing parts of the app in future posts but for now I will just simulate this with some sample data. The objective in this first post is to implement a basic table view with a “Load More…” option that can be used to navigate a large collection of items.

Getting Started

I used the Navigation-based Application template to create a new X-code project which contains a root table view controller to show what will be the RSS Feed and then a detailed view controller to show individual posts that are selected from the root table view. I will skip some of the basic project setup as you can see this for yourself in the sample code. The basic view hierarchy as seen in Interface Builder (IB) is as follows:

The root view controller is called FeedViewController and implements the top level table view to show the index of posts in the RSS Feed. We will come back to the details of this controller in a minute but first I want to look at the basic objects we need for the model part of our model-view-controller (MVC) design.

Post Model

Since the whole point of this app is to show the contents of posts in an RSS feed I know that I will need a post class. To keep things simple in this initial version I am only going to include the most basic of items - a post title, a post description and a publication date. The contents of Post.h are shown below:

@interface Post : NSObject {
    NSString *postTitle;
    NSString *postDescr;
    NSDate *pubDate;
}

@property (copy, nonatomic) NSString *postTitle;
@property (copy, nonatomic) NSString *postDescr;
@property (copy, nonatomic) NSDate *pubDate;

@end

The only thing worth noting is that the instance variable properties are defined as using a copy setter. This means that when we assign something to a Post object ivar a copy of the object is made and kept inside the post object rather than keeping a reference to an external object. This ensures our model cannot be changed after the fact by some external class modifying the original object.

The implementation of a Post object is currently trivial, it synthesises the ivar properties and releases everything when the object is deallocated. Check the code download if you want to see it.

Feed Model

In addition to modelling the individual posts I also need an object to hold the details about the whole feed. In a full implementation this would contain details on the feed as well as an index of posts as they are retrieved from the remote server. Since I am currently only going to simulate the retrieval of data my feed object is pretty much empty. Actually in this initial version it has no ivars at all and only a single instance method to fetch new posts:

@interface Feed : NSObject {

 

}

 

- (NSArray *)newPosts;

 

@end

 

The implementation of the newPosts instance method is as follows:

- (NSArray *)newPosts {

    NSMutableArray *posts = [[NSMutableArray alloc] init];
	
    for (NSInteger item = 1; item <= 5; item++) {
		
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
		
        Post *newPost = [[Post alloc] init];
        NSDate *pubDate = [NSDate dateWithTimeIntervalSinceNow:item];
        newPost.postTitle = [NSString stringWithFormat:@"Article %@", pubDate];
        newPost.postDescr = @"Some text describing this post...";
        newPost.pubDate = pubDate;
		
        [posts addObject:newPost];
        [newPost release];
		
        [pool drain];
    }
	
    return posts;
}

 

For now this method just allocates and returns to the caller an array filled with five dummy Post objects. Following standard Cocoa naming conventions the method name starts with “new” to indicate to the caller should expect to get back allocated objects that it must take responsibility for and ultimately release when no longer required.

The only other point of interest is the use of an autorelease pool inside the loop that allocates Post objects. This is not really necessary in this case since we are only building five post objects. However if we change this code to start creating 500 posts the memory requirements for the temporary NSDate and NSString objects which are autoreleased could start to become a problem. You can find a more detailed discussion of using autorelease pools in this earlier post on Cocoa Factory Classes.

Implementing the Feed Table View

With our basic model classes setup we can implement the table view used to show the list of posts in our dummy RSS feed. As we saw earlier our root table view controller class is named FeedViewController. The interface for this class is pretty simple:

@interface FeedViewController : UITableViewController {
    Feed *feed;
    NSMutableArray *posts;
}

@property (nonatomic, retain) Feed *feed;
@property (nonatomic, retain) NSMutableArray *posts;

 

Our interface consists of a feed object to allow us to interact with the RSS feed and an NSMutableArray which we will use to store the posts we retrieve from the feed. This is sufficient for now to allow us to experiment with the table view. If we start wanting to store the retrieved posts when the application terminates or retain large numbers of posts in memory we may want to replace this simple storage model with a core data backed storage. However for now we will keep things simple. Actually this is really not a bad approach when building an App. We don’t want to add too much complexity too soon until we get a feel for which direction the App is taking.

Generating Some Data

In order to play with our table view we need to populate the objects in our feed controller. A good place to do this initialisation is in the viewDidLoad method of the FeedViewController:

- (void)viewDidLoad {

    [super viewDidLoad];
	
    self.feed = [[Feed alloc] init];
    self.posts = [[NSMutableArray alloc] init];
	
    NSArray *newPosts = [feed newPosts];
	
    if (newPosts) {
		
        [self.posts addObjectsFromArray:newPosts];
        [newPosts release];
    }
}

 

The first thing we do is allocate and initialise a feed object. In a real implementation this would most likely trigger some network communication with a remote server but for now it really does nothing much. We also allocate a mutable array to hold the posts we have retrieved and then use the newPosts method we saw previously to get the feed object to fetch us some posts.

Implementing the Table View Data Source Methods

To get our data into the table view we need to implement as a minimum the two required UITableViewDataSource protocol methods (our FeedViewController class will act as both the table data source and table delegate):

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [posts count] + 1;
}

 

The first method is simple and determines how many rows we have in each section of our table. Our table has a single section and needs to have one row for every post that we have retrieved from the feed plus one. The plus one is to allow for an extra row at the end of the table to show the “Load More…” message.

As an aside before we see the second required method I have also implemented the optional method to return the number of sections in the table:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {

    return 1;
}

This method is not really required in this case since a table will default to 1 section if this method is not implemented. The second required method that we must implement is to return a cell for each row in the table when requested:

- (UITableViewCell *)tableView:(UITableView *)tableView 
                     cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *postCellId = @"postCell";
    static NSString *moreCellId = @"moreCell";
    UITableViewCell *cell = nil;
	
    NSUInteger row = [indexPath row];
    NSUInteger count = [posts count];
	
    if (row == count) {
		
       cell = [tableView dequeueReusableCellWithIdentifier:moreCellId];
       if (cell == nil) {
           cell = [[[UITableViewCell alloc
                     initWithStyle:UITableViewCellStyleDefault 
                     reuseIdentifier:moreCellId] autorelease];
       }
		
       cell.textLabel.text = @"Load more items...";
       cell.textLabel.textColor = [UIColor blueColor];
       cell.textLabel.font = [UIFont boldSystemFontOfSize:14];
		
	
    } else {
	
        cell = [tableView dequeueReusableCellWithIdentifier:postCellId];
        if (cell == nil) {
            cell = [[[UITableViewCell alloc
                     initWithStyle:UITableViewCellStyleSubtitle 
                     reuseIdentifier:postCellId] autorelease];
        }

        Post *currentPost = [posts objectAtIndex:row];
        cell.textLabel.text = [currentPost postTitle];
        cell.textLabel.font = [UIFont systemFontOfSize:14];
		
        cell.detailTextLabel.text = [currentPost postDescr];
        cell.detailTextLabel.font = [UIFont systemFontOfSize:10];
    }
	
    return cell;
}

To retrieve new posts we just need to send the newPosts message to our feed object and see if we get back an array containing some posts. If we have some new posts to add to the table we could at this point simply call [self.tableView reloadData] and when the table refreshes it will show the new posts.

In this case I have made things a bit more complicated by animating the insertion of the new rows into the table. To do this we need to build an array of NSIndexPath objects for each new row. Then we use the insertRowsAtIndexPaths method to fade the rows into view. This method should be preceded by the beginUpdates method and followed by the endUpdates method.

Finally we scroll the table so that the first newly inserted row is in the middle of the screen and we deselect any previously selected row.

In Summary

Implementing a table that can trigger the loading of new rows is pretty straightforward and is a very useful technique anytime you are relying on retrieving large amounts of data from a remote server. This is already a fairly long post so I will follow up in a future post with some further refinements to the table view. The Xcode project containing all of the code for this example app can be downloaded here.

Sunday
Sep052010

iTunes 10 Toolbar Buttons

The new iTunes 10 User Interface is creating a lot of discussion both from Users and Cocoa developers. Whenever a program as familiar as iTunes is changed it always generates a big reaction (both positive and negative). My instinct is always to give it a week or two before reacting to see if the change grows on me with use. The change that certainly made me do a double take is the change to the toolbar with the close/minimise/maximise buttons rotated from a horizontal to a vertical layout. The title bar which previously just said “iTunes” has also disappeared.

As well as mimicking the layout of the buttons in the iTunes mini player this layout seems to have been done to save space in the iTunes toolbar. There is some debate as to whether this is a violation of the Apple Human Interface Guidelines (HIG). Certainly the HIG does say that all windows should have a title bar. It does not say if the close/minimise/maximise buttons should be arranged horizontally or vertically. All the examples in the HIG show the buttons arranged horizontally and that seems to be the way that most Cocoa apps do it with the possible exception of the iTunes mini player.

Anyway I will leave it to the HIG gurus to argue the pros and cons. I do suggest you give it a week but if after that it still annoys you there is a simple way to revert to the old style. Quit iTunes and then from a terminal window:

$ defaults write com.apple.iTunes full-window -boolean YES

Restart iTunes and you will be back to the familiar toolbar and title bar layout:

If you want to get back to the iTunes 10 default layout as Steve intended:

$ defaults write com.apple.iTunes full-window -boolean NO

(Tip of the hat to the cult-of-mac blog for the original tip)

Tuesday
Jul272010

Filtering arrays with NSPredicate

I think my first experience of using the NSPredicate class on the iPhone was with core data queries but it is has many other useful applications. I had the need the other day to process a list of filenames and match against a simple regular expression. The code to create the array of filenames that I needed to process looked something like this:

    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSString *defaultPath = [[NSBundle mainBundle] resourcePath];
    NSError *error;
	
    NSArray *directoryContents = [fileManager contentsOfDirectoryAtPath:defaultPath error:&error];


The first few lines just setup a search of the resource directory in the main application bundle so that the array directoryContents ends up containing the filenames of all the files in the directory. I then need to process that array and pull out the filenames that match my pre-defined regular expression.

NSPredicate Format Strings

The NSPredicate class provides a general way to specify a query that can then be applied to filter an array. The actual query to use is specified when creating the predicate as an argument to the predicateWithFormat: method. Some examples will hopefully make it clear:

Simple match

The simplest example is when you just need an exact match against a single value. This is a fairly meaningless example in this case but illustrates the basic technique:

    NSString *match = @"imagexyz-999.png";
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF == %@", match];
    NSArray *results = [directoryContents filteredArrayUsingPredicate:predicate];

 

The query string is simply “SELF == %@” where SELF refers to the each element in the array. To use the predicate we apply it to the array of file names using filterArrayUsingPredicate:. In this trivial (and pointless) example the results array would contain a single string “imagexyz-999.png” assuming that file existed in the directory.

Wildcard match

To match using a wildcard string the query can use like instead of the comparison operator and include the “*”  (match zero or more characters) or “?” (match exactly 1 character) as wildcards as follows:

    NSString *match = @"imagexyz*.png";
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF like %@", match];
    NSArray *results = [directoryContents filteredArrayUsingPredicate:predicate];

 

In this case the results array would contain the filenames that match imagexyz*.png (e.g. imagexyz-001.png, imagexyz-002.png, etc.).

Case Insensitive match

To make the previous example use a case insensitive match the like operator is modified to like[c]. There is also an additional option to match in a diacritic-insenstive way to ignore accented characters by using like[d]. You can combine these two options as follows:

    NSString *match = @"imagexyz*.png";
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF like[cd] %@", match];
    NSArray *results = [directoryContents filteredArrayUsingPredicate:predicate];

 

Other string comparisons

In addition to like you can also use beginswith, endswith, contains and matches. These are mostly self-explanatory with the exception perhaps of matches. With matches you can specify a full regular expression which allows more complex matches than is possible with just the * and ? wildcard characters. For example to match filenames of the form imagexyz-ddd.png where ddd are all digits:

    NSString *match = @"imagexyz-\\d{3}\\.png";
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF matches %@", match];
    NSArray *results = [directoryContents filteredArrayUsingPredicate:predicate];

 

I will not try to explain regular expression syntax here but note that the backslash (\) characters are escaped by using a double-backslash. Also note that strict ICU regular expression formats are used.

Sunday
Jun062010

Delegation or Notification

Is it generally considered best practise to reduce the coupling or dependencies between classes in your application. A key benefit is that your code becomes easier to maintain since a change in one class is less likely to impact another dependent class. It also becomes easier to reuse classes across applications.

Cocoa implements a number of features such as delegates and notifications that make this easier to achieve though it can be confusing at first to know when you should use one or the other (or both) or these techniques.

Delegation

The concept of delegation as its name suggests allows an object to send a message to another object (the delegate) so that it can customise the handling of an event. The UITableViewController provides a common example of this in an iPhone application. The delegate of the UITableViewController is informed when a user will do something such as select a row allowing the delegate to prevent or change the action. A further call is made to the delegate when the user did do something allowing the delegate to take further action such as saving data or triggering the update of a view.

The important point about delegation is the way it is implemented allows for minimal dependency between the delegating object and its delegate. The delegating object needs to have a reference to its delegate so that it can call methods in the delegate. However this reference is declared using the anonymous id type:

    id delegate;

This way the delegating object does not need to know the type of its delegate which removes a big potential dependency between the classes. This weak reference between the two classes means that the compiler cannot verify that a delegate class actually implements the methods expected by the delegating object.

To avoid unexpected runtime exceptions a delegating object can test if a delegate responds to a certain message before invoking it:

if ([delegate respondsToSelector:@selector(myMethod)]) {

        [delegate myMethod];
 }

This allows a delegate to optionally implement some methods. The ability to specify some delegate methods as mandatory or optional can be formally defined (and checked at compile time) through the use of protocols. There is an example on using delegates with protocols in the post on iPad Modal View Controllers so I will skip ahead to the sending and receiving of notifications.

Notification

The concept of notification differs from delegation in that it allows a message to be sent to more than one object. It is more like a broadcast rather than a straight communication between two objects. It removes dependencies between the sending and receiving object(s) by using a notification center to manage the sending and receiving of notifications. The sender does not need to know if there are any receivers registered with the notification center. There can be one, many or even no receivers of the notification registered with the notification center.

The other difference between notifications and delegates is that there is no possibility for the receiver of a notification to return a value to the sender. Remember the earlier example of a delegate method indicating that a user will select a row in a table. The delegate has the opportunity to return a value which prevents the user from selecting a row. If we use a notification to inform another object that the user is selecting a row there is no way to return a value to influence this action.

Typical uses of notifications might be to allow different objects with an application to be informed of an event such as a file download completing or a user changing an application preference. The receiver of the notification might then perform additional actions such as processing the downloaded file or updating the display.

The Default Notification Center

Cocoa allows you to create multiple notification centers but in practise on the iPhone you will probably only ever need to use the default notification center. You access this default notification center using a class method as follows:

   NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];

Naming a Notification

A notification consists of a name, an object and an optional dictionary to contain user information. The name is used to identify the notification so you should try to choose something unique to your application. Using a two or three letter prefix specific to your application is a good way to do this. Apple also recommends that you define the name as a global constant string variable rather than using the hard coded string value in your code. If you look at the way Apple defines notification names you will also see the name often follows the pattern <object> did/will <action>

So for example, a name to indicate that a download has finished might be defined as follows:

   NSString *ABCMyApplicationDidFinishDownload = @”ABCMyApplicationDidFinishDownload”;

The header file for the class that generates this notification would include the declaration:

   extern NSString *ABCMyApplicationDidFinishDownload;

Posting a Notification

There are two ways to post a notification depending on whether we want to send the optional user information or not. In the simplest case without the user information you would post a notification as follows:

   [[NSNotificationCenter defaultCenter]
       postNotificationName:ABCMyApplicationDidFinishDownload
       object:self];

The object can be any object we want to send with the notification but is usually either the object posting the object or nil. To send additional data with the notification you need to construct a dictionary to hold the data. So in our example if we want to send a filename with our message we would first create a dictionary to hold the filename.

   NSString *ABCMyApplicationFilename = @”ABCMyApplicationFilename”;
    ...
    ...
    NSDictionary *userInfo = [NSDictionary dictionaryWithObject:filename
                              forKey:ABCMyApplicationFilename];

Our dictionary key would as with the notification name be declared in the header file of the class posting the notification:

   extern NSString *ABCMyApplicationFilename;

Posting the notification with the user information is now simple:

   [[NSNotificationCenter defaultCenter]
       postNotificationName:ABCMyApplicationDidFinishDownload 
       object:nil 
       userInfo:userInfo];

Observing a Notification

To receive a notification you need to register an observer with the notification center for the notification name you are interested in. If you are adding an observer for a view controller in an iPhone application a good place to do that would be the viewDidLoad method. This ensures that you create one and only one observer for the notification when the controller finishes loading. You register the observer as follows:

    [[NSNotificationCenter defaultCenter] addObserver:self
         selector:@selector(downloadFinished:)
         name:ABCMyApplicationFilename
         object:nil];

The observer object in this case (self) is the view controller and the selector is used to specify the method in our controller that will be called when the notification is received (technically this is the message the notification center will send to the observer object). The method must return a void and take a single parameter as follows:

- (void)downloadFinished:(NSNotification *)notification

If you need to get at the user information sent with the notification you can extract it from the NSNotification object:

- (void)downloadFinished:(NSNotification *)notification {
 
    NSDictionary *userInfo = [notification userInfo];
    NSString *filename = [userInfo objectForKey:ABCMyApplicationFilename];
    …
}

Unregistering an Observer

An important step to remember if you register an observer for a notification is to ensure that you remove that observer when it is no longer required. For example if we register a view controller as an observer and that view controller is then deallocated the application will most likely crash if the notification center attempts to send it a notification. To ensure this does not happen we should unregister the observer in both the viewDidUnload and dealloc methods of the view controller.

You can unregister for a specific notification using the removeObserver:name:object method but it is often easier to just unregister for all notifications, especially if your controller is observing several notifications:

- (void)viewDidUnload
{
    [super viewDidUnload];
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    ...

}

- (void)dealloc {

    [[NSNotificationCenter defaultCenter] removeObserver:self];
    ...

    [super dealloc];
}

Performance of Notifications

At first notifications seems like a cost free way of avoiding dependencies between classes. You do not even need to add a delegate instance variable to your class. However before getting carried away with notifications it is worth being aware of the downsides. When you post a notification the notification center will deliver the message to each registered observer synchronously. Control is not returned to your code until all of the observers have had their registered method called.

This means that you need to be careful if you need to deliver the notification to many observers of if the code posting the notification is waiting to complete some action. Control is not returned to the posting code until all of the observer methods have been called and completed (the observer methods are called one after the other in some unspecified order).

One way around this is to have additional notification centers on different threads and use asynchronous notifications (NSNotificationQueue) that allow the calling code to return immediately. That is a lot of extra complexity in most cases. A simpler workaround is to delay processing of the notification using the performSelector:withObject:afterDelay: method:

- (void)downloadFinished:(NSNotification *)notification {

    [self performSelector:@selector(doTheRealWork:) withObject:notification afterDelay:0.0f];
}

This allows the method that posted the notification to get control back faster. Note however that the observer methods are still executed on the same thread.

Delegation or Notification?

As often seems to be the case this has turned into a longer post than I expected but I think we can finally consider when to use delegation and when to use notification. One obvious difference is that delegation is for sending a one-to-one message (to which the receiver can return a value) whereas notification is a one-to-many message (where the receivers cannot return anything to the sender). This leads to some clear recommendations:

  • use delegates when you want the receiving object to influence an action that will happen to the sending object.
  • use notifications when you need to inform multiple objects of an event.

(There is one other situation that I should briefly mention where notifications might not be the best choice. If you want to be informed of a change to a property of an object you might be better off using the Key Value Observing protocol provided by Cocoa.)

In situations where either of these guidelines could apply there may not be a clear choice between delegation or notification. For example in situations where you just want to inform a delegate that something has happened you could also choose to use notification. Likewise if you are only sending a notification to a single object you could also choose to use delegation. One extra consideration is whether it is likely that you could have more than one observer in the future in which notification would be the best choice.

Thursday
May272010

Cocoa Design Patterns

A copy of Cocoa Design Patterns by Erik M. Buck and Donald A. Yacktman arrived from Amazon this morning and I am already liking it a lot. I would not say it is the first book you should read about Cocoa programming but it may well be the second.

Note that this is not a book about iPhone programming though a lot of the material applies directly to Cocoa Touch on the iPhone. So don’t expect to find a detailed discussion of UIKit or other iPhone specific APIs. What it does provide are a large number of best practises from some experienced Cocoa developers. Aaron Hillegass sums it up nicely in the foreword:

Through floundering about with bad solutions, grumpy old Cocoa programmers have figured out some really good solutions to common design problems. The existence of this book means that you are not required to suffer through the same misery that we went through.

The book does assume some basic knowledge of object-oriented programming and if not Objective C than at least some C/C++ or Java. But in my view this book is perfect for somebody who has come to Cocoa via the iPhone and now wants to really understand how to get the most out the framework.

The first part of the book is a detailed discussion of the Model View Controller (MVC) architecture. If you were not already convinced that you should be using MVC in your applications they walk thought a simple application written with and without it.

The second part of the book steps through some fundamental Cocoa patterns such as two stage creation (alloc, init), categories, accessors, etc. Each pattern explains the motivation for creating the pattern (what the problem is), a detailed description of the pattern (how it solves the problem), examples of using the pattern and finally the consequences or what you should be aware of if you apply the pattern.

The third and fourth parts of the book cover patterns that either help to decouple your code (that is reduce dependencies between classes) or help to reduce complexity. Examples include the singleton pattern, delegates, the responder chain, bundles, class clusters, etc. There is some real depth here with good explanations and examples that make it easy to follow.

The final part of the book covers some of the tools that help apply patterns and includes some discussion on the use of core data models, views, binding and controllers.

In summary I highly recommend this book if you really want to understand Cocoa. Applying the patterns it describes can make a big difference to your application and I think it does a good job of revealing some of the more arcane parts of Cocoa.