19th February 2009

MVC on the iPhone: The Model

posted in Objective C, iPhone |

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:
  1. #import
  2.  
  3. @interface MainViewController : UIViewController {
  4. UILabel *label;
  5. }
  6.  
  7. @property (nonatomic, retain) IBOutlet UILabel *label;
  8. @end

and MainViewController.m like so:

C:
  1. #import "MainViewController.h"
  2. #import "MainView.h"
  3.  
  4. @implementation MainViewController
  5. @synthesize label;
  6.  
  7. - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
  8. if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
  9. // Custom initialization
  10. }
  11. return self;
  12. }
  13.  
  14. /*
  15. // Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
  16. - (void)viewDidLoad {
  17. [super viewDidLoad];
  18. }
  19. */
  20.  
  21. /*
  22. // Override to allow orientations other than the default portrait orientation.
  23. - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
  24. // Return YES for supported orientations
  25. return (interfaceOrientation == UIInterfaceOrientationPortrait);
  26. }
  27. */
  28.  
  29. - (void)didReceiveMemoryWarning {
  30. [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
  31. // Release anything that's not essential, such as cached data
  32. }
  33.  
  34. - (void)dealloc {
  35. [label release];
  36. [super dealloc];
  37. }
  38.  
  39. @end

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:
  1. #import
  2.  
  3. @interface FlipsideViewController : UIViewController {
  4. UITextField *textField;
  5. }
  6.  
  7. @property (nonatomic, retain) IBOutlet UITextField *textField;
  8.  
  9. - (IBAction)textChanged:(id)sender;
  10. @end

and FlipsideViewController.m:

C:
  1. #import "FlipsideViewController.h"
  2.  
  3. @implementation FlipsideViewController
  4. @synthesize textField;
  5.  
  6. - (void)viewDidLoad {
  7. [super viewDidLoad];
  8. self.view.backgroundColor = [UIColor viewFlipsideBackgroundColor];
  9. }
  10.  
  11. /*
  12. // Override to allow orientations other than the default portrait orientation.
  13. - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
  14. // Return YES for supported orientations
  15. return (interfaceOrientation == UIInterfaceOrientationPortrait);
  16. }
  17. */
  18.  
  19. - (void)didReceiveMemoryWarning {
  20. [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
  21. // Release anything that's not essential, such as cached data
  22. }
  23.  
  24. - (IBAction)textChanged:(id)sender
  25. {
  26.  
  27. }
  28.  
  29. - (void)dealloc {
  30. [textField release];
  31. [super dealloc];
  32. }
  33.  
  34. @end

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:
  1. #import <Foundation/Foundation.h>
  2.  
  3.  
  4. @interface Model : NSObject {
  5.     NSString *text;
  6. }
  7.  
  8. @property (nonatomic, retain) NSString *text;
  9.  
  10. + (Model *)sharedModel;
  11. @end

And here is Model.m:

C:
  1. #import "Model.h"
  2. #import "SynthesizeSingleton.h"
  3.  
  4. @implementation Model
  5.  
  6. SYNTHESIZE_SINGLETON_FOR_CLASS(Model);
  7.  
  8. @synthesize text;
  9.  
  10. - (id) init
  11. {
  12.     self = [super init];
  13.     if (self != nil) {
  14.         text = @"";
  15.     }
  16.     return self;
  17. }
  18.  
  19.  
  20. - (void) dealloc
  21. {
  22.     [text release];
  23.     [super dealloc];
  24. }
  25.  
  26. @end

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:
  1. - (IBAction)textChanged:(id)sender
  2. {
  3.     Model *model = [Model sharedModel];
  4.     model.text = textField.text;
  5. }

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:
  1. - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
  2.     if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
  3.         // Custom initialization
  4.         Model *model = [Model sharedModel];
  5.         [model addObserver:self forKeyPath:@"text" options:NSKeyValueObservingOptionNew context:nil];
  6.     }
  7.     return self;
  8. }

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:
  1. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
  2. {
  3.     label.text = [change valueForKey:@"new"];
  4. }

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

Post to Twitter

This entry was posted on Thursday, February 19th, 2009 at 7:30 pm and is filed under Objective C, iPhone. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

There are currently 19 responses to “MVC on the iPhone: The Model”

  1. 1 On February 19th, 2009, christian said:

    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. 2 On February 20th, 2009, Kent said:

    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. 3 On February 20th, 2009, Erik said:

    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. 4 On February 20th, 2009, Darren Minifie said:

    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. 5 On February 21st, 2009, Raj said:

    Good work. I have linked this post at http://www.iphonekicks.com/objectivec/MVC_on_the_iPhone_The_Model

  6. 6 On February 21st, 2009, MVC on the iPhone: The Model — Some Random Dude said:

    [...] MVC on the iPhone: The Model [...]

  7. 7 On February 22nd, 2009, イナヅマtvログ » iPhone SDK + Objective-C, 参考サイト メモ said:

    [...] on the iPhone: The Model iPhone Dev Center : The Model-View-Controller Design Pattern MVCで Singletonパターン [...]

  8. 8 On February 23rd, 2009, Samuel Wan said:

    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…

  9. 9 On February 24th, 2009, iphoneish.com said:

    Explaining the M in MVC for iPhone development…

    The MVC design pattern is key to Cocoa development, but the basic tutorials cover mainly the View and the Controller, but rarely the Model. This article gives a nice example of how to use a Model in your application….

  10. 10 On February 26th, 2009, Jeff said:

    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.

  11. 11 On February 28th, 2009, kp said:

    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.

  12. 12 On February 28th, 2009, kp said:

    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.

  13. 13 On March 1st, 2009, One Minute There « From Out Of Nowhere said:

    [...] I did find this very helpful blog post to help out with following the MVC design [...]

  14. 14 On March 7th, 2009, Mac said:

    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!

  15. 15 On March 7th, 2009, More on MVC on the iPhone | BIT-101 Blog said:

    [...] my previous post, I showed how you could set up a model and using key/value observing, know when various properties [...]

  16. 16 On April 6th, 2009, Sam said:

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

  17. 17 On April 6th, 2009, kp said:

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

  18. 18 On April 6th, 2009, Sam said:

    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

  19. 19 On August 28th, 2009, A. said:

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

Leave a Reply

Who is reading BIT-101?

Copyright ©2009 by Keith Peters. All rights reserved. This means that you may not reprint or repost the contents of this site without express written permission of the author.


  • Calendar

  • February 2010
    M T W T F S S
    « Jan    
    1234567
    891011121314
    15161718192021
    22232425262728
hoodia order buy Levitra Plus betablockers weight loss information buy pills without a prescription arthritis menopause ambien doses cat's eye health information on cholesterol cialis online order valtrex cheapest phentermine onlin e increase breast size lower blood sugar immediately terramycin which is better cialis or viagra buy cheap cialis reduce cholesterol naturally new blood pressure treatment products for back pain cheapest cialis index will levitra help piroxicam 20 mg order viagra online in germany buy tadalafil online buy levitra onlines how to naturally lower cholesterol buy generic viagra where to buy soma anti allergic drug levothyroxine dogs new hair loss treatmen buy levitra pain meds buy cheap malaria therapy weight loss after baby asthma medications chronic snoring viagra gel prostate cancer cures order viagra cialis alprazolam men health natural cure arthritis immune system support diet medicine cialis approval lipitor effects where can i buy arthritis drugs overactive bladder in men self help weight loss natural cholesterol control ativan medication cialis approval best cure for snoring breast enhancing pills order prozac celebrex pharmacy buy levitra onlines premature ejaculation cure confidence hypnotherapy free stop smoking bust enhance diet weight loss supplements skin fungal infection valium with no prescription viagra with out prescription breast enhancement products alpha blocker medications azithromycin 250mg skin disease chronic heart failure medicines dog care products buying cialis online gerd in children antibiotics to buy my drug store muscle building diet drugs affecting levitra anti anxiety medications really large breast enhancement help for constipation ulcers stomach drugs for high blood pressure selling pet products buy pain medicine viagra online overnight fucidin ointment generic zyrtec prices soft tab cialis smoking treatment dog products online weight loss solution cialis on line blue pills weight loss diet pill nitroglycerin sublingual floxin prevention of heart attack imuran order gasex vermox treatment of depression Viagra On Line buy generic cialis professional tooth whitening kits to buy valium 2mg treatment for hypertension ultram cheapest online stores hair loss products cheap weight loss diovan prescription malaria preventative taurine treating prostate cancer immune system support natural constipation cure phentermine no prescription fast delivery purchase meds without prescription buy plendil diet drug taking viagra after cialis protonix cheapest generic cialis online viagra levitra cialis yohimbe benefits muscle mass gain diet and health products medical treatment for insomnia buy blood pressure meds buy celexa levaquin interactions blood pressure drug skin disorder where can i buy arthritis drugs natural breast enhancer acute pain control online diazepam natural acne remedy antifungal strategies triphala pravachol online how can i stop smoking breast enhancement natural nautral breast enhancement beta blocker medications wellbutrin dosages order viagra cialis lower high blood pressure mass muscle phentermine from canada how to loss weight osteoporosis bone health lipitor use dog medication drug allergies buy diazepam buyviagra cialis phentermine 37.5 mg zestril medication parkinsons treatment generic revatio free nexium cosmetic dentistry tooth whitening avalide generic buy cheap tadalafil uk simvastatin tablets buy cialis online in usa breast pain cat care ovulating clomid medical skin care lines viagra to canada viagra or cialis cheap cialis tramadole buy azulfidine drugs used for cancer ear pain oral ketoconazole raloxifene evista taurine sex with levitra stop smoking today heart failure natural cholesterol control protonix dose oxybutynin 5mg irritable bowel syndrome treatments new treatment for hepatitis c cheap prescription drugs viagra online prescription depression therapy buy sumycin menopause treatment hair loss treatments medication pletal what is a natural antibiotic viagra purchase synthroid tablets generic prilosec lipitor cat health info discount vitamin cholesterol and health bacterial diarrhea weight loss medicine new treatment for depression removing retention fluids diuretic medicines soma 250mg cat anxiety loss weight online pharmacy viagra buy phentermine without a prescription herbs for breast growth cymbalta dosage fast weight loss supplement arthritis menopause levitra online order cheapest place to buy phentermine cold flu medications for nausea buy ultram where pills for acne free weight loss programs help with anxiety improve skin valium 2mg urinary tract health cat urinary tract disease crestor dosage drug zofran calan zyrtec buy nirdosh dosage digoxin buy pain patch acomplia alendronate cialis best price cheap wellbutrin small dog products depression medicine sildenafil dosage dog health depression and anxiety lamictal withdrawal viagra, levitra and cialis online drug buy bone maker strontium cures for hair loss nitroglycerin tablets natural arthritis treatment arimidex buy buy energy patch how to treat a yeast infection viagra herb alternative viagra cialis levitra order sublingual cialis cialis comparison breast lift augmentation seroquel for depression carisoprodol mg new treatment for depression cialis soft tabs safe sleep aid severe leg muscle pain natural weight loss gabapentin medication what is ambien clozapine medication viagra online ordering cures for hair loss free weight loss help buy viagra levitra pet treats order plan b diabetes type 2 phentermine risk ultram er side effects treatment for hepatitis b constipation cures drugs used in treating depression leg pain buy cheap generic cialis anti anxiety meds hypnotherapy for weight loss motilium body building fitness dog skin relieve upper back pain cures for high blood pressure cardura celecoxib Viagra Online Cheap cheap bactrim ambien online lamisil cost infertility meds progesterone clomid osteoporosis hormon urinary tract infection symptoms hypnotherapy for health how to buy viagra online joint pain cure online allegra buy generic cialis uk generic abilify cures for lung cancer new treatments for lung diseases pain meds buy cheap treatment for dry skin disease of the skin nexium drug free stop smoking buy tooth whitening products viagra tablet naprosyn dosage women's fertility male sexual power carisoprodol purchase asthma attack treatment estradiol pills phentermine from canada pet health care hair loss products online astelin generic cheap estrace free weight loss program buy rimonabant relieve lower back pain lexapro prescription new breast cancer drug buying ambien best online viagra scams home scabies treatment hair loss in woman buy generic cialis uk eye drop gabapentin medication amitriptyline uses ultram no prescription natural pain cures buy cla products back pain lowest price generic viagra pain meds buy cheap mg buy phentermine acne skin care cialis rx weight loss and fitness nitrofurantoin buy phentermine without a prescription high blood pressure medicines stop hair loss viagra china use levitra female health coreg dosage carisoprodol price pain relief product breast enlargement depression pills buy how to treat flu home neck pain relief order imitrex online vitamin b-6 cialis soft tabs pharmacy software description of soma buy isoniazid cheap prevacid help ear infections on dog fat burning stop smoking remedies rhinitis treatment chronic pain relief birth control online meds without prescriptions buy lovastatin drug stores penis enlargement without pill cancer medicine buy deltasone cure for throat infection thyroid dogs dosage cipro viagra from uk cheap alcoholism treatment natural cure for constipation paxil cialis 5mg tablets amitriptyline uses topamax drugs lower heart rate drug discount codes dog medicines body fat loss joint pain recurring urinary tract infections ativan information buy drugs online cheap fast valium body building ambien maximum dosage information on valium how to sperm more chlamydia medication dosage buy cialis online viagra chest pain heart fluconazole interaction calcium channel blocker side effects zolpidem dosage online drug stores zelnorm muscle strength fluconazole buy stress gum free weight loss products information on gout low immune system online viagra cialis 20 buy cefixime phentermine from canada gain muscle mass fast lasix side effects buy singulair penis enlargement free natural muscle and joint health viagra online overnight cialis online aceon allergies and asthma diamox side effects weight loss software generic compazine price for tramadol high blood pressure symtoms osteoporosis help treatment severe constipation drug new smoking stop pain relief product xanax online dog health info clonazepam .5mg buy tribulus pregnancy prevention methods allergy hives