MVC on the iPhone: The Model

CocoaTouch is built from the ground up with an MVC paradigm in mind. in most templates, views and view controllers are built for you. UIView and UIViewController are core classes. In many cases you use UIView as is, adding UI elements to a generic UIView in Interface Builder. You subclass UIViewController to add your own functionality. IBOutlets let you hook up UIElements in your view to access them, and IBActions allow you to trigger actions based on user gestures on the UIView elements. All this will be explained to you in any basic tutorial or book on the subject.

But what about the Model? Very little is said about that, at least from what I could find. Most of the basic tutorials I’ve seen avoid the model all together and code any data right into the controllers.

This document starts to explain it, but doesn’t go into much detail.

For a couple of the apps I’ve been writing, I finally settled on what I think is a decent implementation, which I’ll share here, not as an authoritative description, but as an offering and even somewhat a request for validation. If this works for you, great. If you can see some improvement, please offer it up.

Basically, I create a Singleton class that extends NSObject for my model. Then use key/value observing to get notified of updates. This is very much like the ModelLocator in Cairngorm, if you’ve worked with that in Flex.

To start with, let’s create a project that has a couple of views. One view will allow the user to change a value. This value will be set on the model, which will trigger a change in the other view. The Utility Application template works well for this. So create a project using that template, naming it “MVC”. (OK, name it whatever you want, but you’ll be able to follow along with my code better if you name it the same thing.)

This will give you something like this:

mvc_01

As you see, you have a main view and a flipside view, with view controllers and nibs for both. And a RootViewController that controls both views. Run the project and take a look through the code to familiarize yourself with what is going on. I’m going to concentrate mainly on the model part, so I assume you understand the rest of this so far.

Now let’s add a label and a text field to these views. The label will go in the main view, and the text field in the flipside view. First let’s make the outlets.

Make your MainViewController.h look like this:

[c]#import

@interface MainViewController : UIViewController {
UILabel *label;
}

@property (nonatomic, retain) IBOutlet UILabel *label;
@end[/c]

and MainViewController.m like so:

[c]#import “MainViewController.h”
#import “MainView.h”

@implementation MainViewController
@synthesize label;

– (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
// Custom initialization
}
return self;
}

/*
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
– (void)viewDidLoad {
[super viewDidLoad];
}
*/

/*
// Override to allow orientations other than the default portrait orientation.
– (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/

– (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning]; // Releases the view if it doesn’t have a superview
// Release anything that’s not essential, such as cached data
}

– (void)dealloc {
[label release];
[super dealloc];
}

@end[/c]

Similarly, in FlipsideViewController.h, we’ll add a textField outlet. We’ll also add an IBAction which will let us know when the text in the text field has changed.

[c]#import

@interface FlipsideViewController : UIViewController {
UITextField *textField;
}

@property (nonatomic, retain) IBOutlet UITextField *textField;

– (IBAction)textChanged:(id)sender;
@end[/c]

and FlipsideViewController.m:

[c]#import “FlipsideViewController.h”

@implementation FlipsideViewController
@synthesize textField;

– (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor viewFlipsideBackgroundColor];
}

/*
// Override to allow orientations other than the default portrait orientation.
– (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/

– (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning]; // Releases the view if it doesn’t have a superview
// Release anything that’s not essential, such as cached data
}

– (IBAction)textChanged:(id)sender
{

}

– (void)dealloc {
[textField release];
[super dealloc];
}

@end
[/c]

Now you can open up MainView.xib and add a UILabel to the view. Drag a connection from the File’s Owner (MainViewController class) to the label and connect it to the label outlet.

mvc_02 mvc_03

Do the same thing in FlipsideView.xib, adding a UITextField and hooking it up to the textField outlet in the File’s Owner (FlipsideViewController) and hooking up the textChanged IBAction to the editingChanged event of the text field.

mvc_04 mvc_05

By now, you should understand that what we want to do is have the user type some text into the text field, and have that appear in the label. But these things are in two separate views, and controlled by two sepearate view controllers. So how do we hook them together? The model of course. Make sure both these nibs are saved, close Interface Builder and go back to XCode.

The Model

Now we create the model. Add a new file to your project, sublcass NSObject. Name the class Model. Now, I’m not a big fan of Singletons. I know the downsides, and believe they are way overused, but I’m also pragmatic, and I think in a case like this, a Singleton is fine. If you want to start a debate on Singletons, do it elsewhere. If it’s really abhorrent to you, you can create your Model in the RootViewController and pass an instance of it to each of your other view controllers. That will work fine. But again, I’m going to go with Singleton. To do that, I’m using this nifty SynthesizeSingleton file from Matt Gallagher at CocoaWithLove.com. You can get it here. That link is also a good place to debate Singletons, if you feel so obliged. To use it, add the SynthesizeSingleton.h file to your project and call the SYNTHESIZE_SINGLETON_FOR_CLASS macro in the implementation of the class you want to be a Singleton. One thing I noticed is that using this macro, you will get a warning unless you also declare the static sharedModel method in the interface file.

Our model will also need a single property, text, with some public accessors. Here’s Model.h:

[c]#import

@interface Model : NSObject {
NSString *text;
}

@property (nonatomic, retain) NSString *text;

+ (Model *)sharedModel;
@end[/c]

And here is Model.m:

[c]#import “Model.h”
#import “SynthesizeSingleton.h”

@implementation Model

SYNTHESIZE_SINGLETON_FOR_CLASS(Model);

@synthesize text;

– (id) init
{
self = [super init];
if (self != nil) {
text = @””;
}
return self;
}

– (void) dealloc
{
[text release];
[super dealloc];
}

@end
[/c]

Setting the data on the model.

When the text in the text field changes, the textChanged method in FlipsideViewController will be called. Here we can set the Model’s text property to the new value.

[c]- (IBAction)textChanged:(id)sender
{
Model *model = [Model sharedModel];
model.text = textField.text;
}[/c]

Make sure you import Model.h in FlipsideViewController.m.

Listening for changes to the model.

Now, the main view just needs to know when the model is changed. We can do this through key/value observing. This means you set up a watcher on a particular property of an object, which will call a method any time that property is changed. Here we want to know when the text property of the model singleton class is changed. We can do this in the initWithNibName method of the MainViewController class. Here’s what it looks like:

[c]- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
// Custom initialization
Model *model = [Model sharedModel];
[model addObserver:self forKeyPath:@”text” options:NSKeyValueObservingOptionNew context:nil];
}
return self;
}[/c]

First we get a reference to the Model singleton. Then we call the addObserver:forKeyPath:options:context method.

The observer is self, the MainViewController class. The keyPath is the property we want to observe, text, as a string. The options allow us to specify what data we want about the change. Here we want the new value of the property that changed. You can also get initial, old, or prior values, or any combination thereof. The context we can leave nil.

Whatever object is observing the change needs to implement a special method that will be called when the change occurs. Here is the signature of that method:

– (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context

The keyPath is the property name as a string, the object is the object that owns that property, in this case, the Model, the change is a dictionary containing the old, new, prior, initial values, as specified, and context is whatever context you might have used, in this case, nil.

If we were observing more than one property on the model, we would probably need to use a switch statement with the keyPath to see which property changed. For now, we are only observing text, so we know that’s what changed. We can get the new value of this property by querying the change dictionary parameter. Of course we could also just grab the model’s text property directly, but let’s use the data that’s passed in here.

[c]- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
label.text = [change valueForKey:@”new”];
}[/c]

Here we are asking the change object for its “new” value (which is all that it will contain, since that’s all we specified). We assign that to label.text and we’re done.

And that’s that. We have a model that stores data and broadcasts events when it changes. Note that in the Cocoa MVC paradigm, the views don’t generally listen directly to the model. The view controllers do, and they tell the view what to do. That’s not written in stone, but is generally how you go about it.

Another concept I want to investigate is using NSNotification instead of key/value observing. Not sure if that’s really a viable alternative and what the benefits/drawbacks might be.

Now of course, this is a rather useless example, for illustration only. There are lots of things you’d do differently in a real app, like probably not listening to every edit change event, just getting it when the flipside view is closed. But that’s not the real purpose of the exercise. The purpose is to see how to implement a model. Or at least ONE way to implement one.

Oh, and in case you are too damn lazy to type in a few lines of code, or can’t get it to work right with the above, here’s a link to the project itself.

MVC PROJECT FILES

This entry was posted in iPhone, Objective C. Bookmark the permalink.

20 Responses to MVC on the iPhone: The Model

  1. christian says:

    I’m not big fan of singletons, that’s why I like PureMVC. But it might be just a personal taste. Said that KVO is pretty cool and that’s the way to go imho.

  2. Kent says:

    I like the idea of having a single model class incapsulating other model objects. In the old days of long ago I created a few software solutions using Microsoft MFC with the same type of code structure (MFC was/is a modified MVC pattern implementation). I often wonder what the module/class break down of other IPhone applications look like, not for validation but just to see how others are doing Iphone dev. I have been kinda sticking to the same type of structure that I used in some of my earlier regular cocoa applications. Do you think you are going to use this type of structure in all of your non trivial iphone apps?

  3. Erik says:

    There’s nothing wrong with “Singletons” per se – enforcing a single instance of an object in a program. The problem is with the way they’re commonly implemented (with a static method accessor). The inflexibility of static access to singletons in languages like Java or AS3 is what really causes the problems, because unless you resort to making some elaborate factory, it’s very hard to switch out the implementation of the class that’s returned by the Singleton’s “accessor method”. So essentially you end up with a “hard-coded” singleton type. This makes unit testing a bitch.

    In Objective-C, however, the class accessor method (“sharedObject”) can be overridden with a Category in your unit tests (or even in your general application code) so you are not “locked-in” to a single implementation of the Singleton object. So this pattern actually works quite well.

  4. Thanks for the article. I usually just give each ViewController an instance of the model, like you mentioned. I like the idea of having one shared instance in this case though. I’ll give that a try.

  5. Samuel Wan says:

    Thanks for the great explanation. Responding to an observed change in the Key-Value observer pattern looks a lot like responding to an observed change in the SharedObject’s sync event handler. Both cases seem to require a big if-else or switch-case statement to handle specific property changes individually. The iPhone SDK doesn’t include bindings, so I’m looking for something with the convenient registration steps of key-value observation and the convenience of individual property change notification handling like in WPF/Silverlight or Flex…

  6. Jeff says:

    This is the only Website I have found that gives an easy-to-understand, step-by-step tutorial for passing variables between views. Other blogs and forums advocate using Singletons, but they don’t explain how to do it. Thanks for making everything clear.

  7. kp says:

    Yes, Sam, this method does wind up in a long switch statement if you are observing several values. Or, you could possibly set different objects as observers I suppose, one for each property you are observing. But that adds its own complexity. I guess you could also do something tricky like using something like “on” plus the keyPath name to code a selector as a string, letting you call a specific function.

    Then again, as mentioned, NSNotifications might be the way to go for something like this.

  8. kp says:

    Come to think of it, you could probably pass a selector in in the context param when adding the observer. Whatever you pass in as a context gets passed through when the observeValueForKeyPath method is called. So if you passed a different selector each time you added an observer, you’d just have to call whatever selector got passed in.

  9. Mac says:

    Hey, thanks for the post. I’m trying this out now, and trying to pass a method in the context parameter but having trouble figuring out how to do that. How do you call a method whose name has been passed in as a parameter? thanks!

  10. Sam says:

    Help… The source code for SynthesizeSingleton.h +m is no longer available. Can you send it to me please?

  11. kp says:

    Sam, it’s right on the site I linked to. Just checked.

  12. Sam says:

    Sorry just seen the link lower down, I was using the one at top of page which goes to apple site. Page not found. Sorry. Thanks for the article VERY GOOD

  13. A. says:

    you should write a book, your writings are always very clear!

  14. bluelobe says:

    Great job, but i wasn’t able to test your code because you did not include the “SynthesizeSingleton.h” file which, when you download the zip from the link you gave, returns a text file that has foreign characters on it like “/” and “#”. Probably because he used a different character set.

    Could you possibly upload the MVC example with the correct header file? Thanks. Here’s the file when I extract from the zip:

    //
    // SynthesizeSingleton.h
    // CocoaWithLove
    //
    // Created by Matt Gallagher on 20/10/08.
    // Copyright 2009 Matt Gallagher. All rights reserved.
    //
    // Permission is given to use this source code file without charge in any
    // project, commercial or otherwise, entirely at your risk, with the condition
    // that any redistribution (in part or whole) of source code must retain
    // this copyright and permission notice. Attribution in compiled projects is
    // appreciated but not required.
    //

    #define SYNTHESIZE_SINGLETON_FOR_CLASS(classname) \
    \
    static classname *shared##classname = nil; \
    \
    + (classname *)shared##classname \
    { \
    @synchronized(self) \
    { \
    if (shared##classname == nil) \
    { \
    shared##classname = [[self alloc] init]; \
    } \
    } \
    \
    return shared##classname; \
    } \
    \
    + (id)allocWithZone:(NSZone *)zone \
    { \
    @synchronized(self) \
    { \
    if (shared##classname == nil) \
    { \
    shared##classname = [super allocWithZone:zone]; \
    return shared##classname; \
    } \
    } \
    \
    return nil; \
    } \
    \
    – (id)copyWithZone:(NSZone *)zone \
    { \
    return self; \
    } \
    \
    – (id)retain \
    { \
    return self; \
    } \
    \
    – (NSUInteger)retainCount \
    { \
    return NSUIntegerMax; \
    } \
    \
    – (void)release \
    { \
    } \
    \
    – (id)autorelease \
    { \
    return self; \
    }

  15. cain says:

    Thanks for this clear explanation.
    The current XCode Utility template is a little different from the code above, but it still works if you follow the tutorial with a bit of common sense. One thing I did differently was, instead of creating an initWithNibName method (in the MainViewController), I put the two lines in viewDidLoad.. like so:

    – (void)viewDidLoad {
    Model *model = [Model sharedModel];
    [model addObserver:self forKeyPath:@”text” options:NSKeyValueObservingOptionNew context:nil];
    [super viewDidLoad];
    }

  16. gonzobrains says:

    @cain: I was just thinking the same thing! Update for Xcode 4!

    I didn’t think initWithNib would work (it didn’t), but putting this stuff in viewDidLoad certainly did!

    Thanks, dude!

    gb

  17. Steve says:

    This is probably exactly what I am looking for, but I can’t seem to get it to work in XCode 4”s Utility template. I made the change that cain mentioned above but I get a crash when I try to type any text in the text field.

    The crash happens here:

    -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    label.text = [change valueForKey:@”new”];
    }
    on the change valueForKey line. The error is

    2011-08-26 16:12:15.977 MVC[51074:207] -[NSNull isEqualToString:]: unrecognized selector sent to instance 0xe2f5e8
    2011-08-26 16:12:15.982 MVC[51074:207] *** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘-[NSNull isEqualToString:]: unrecognized selector sent to instance 0xe2f5e8’

    Anyone know what I’m going wrong?

  18. Steve says:

    Ignore my pervious post, I set up the flipside xib incorrectly, all is working!

Leave a Reply