MVC Design pattern for iOS apps


Introduction

Design patterns are simple patterns say like a template which is used to ease the problems related to development. It solves reoccurring problems in software construction. It breaks the code into chunks of code which will collectively full fill one purpose.

What is MVC?

MVC stands for Model-View-Controller. It classifies the objects into three category based on their role and hence separates the code into much cleaner and reusable environment.

The three categories according to their role are :

Model – It deals with the data of application. It holds the data and manipulates it based on request.

View – It is the collection of objects which defines the UI of application with which the user interacts. It basically presents the data hold by model objects.

Controller – It is the communication channel between model and view. It will display the data on view from model and tells the model if any changes in data is requested by view.

In MVC pattern model and view will never interact with each other. Controller will display the model data on view. Model will notify controller if any data changes and controller in turn updates the view. If user interacts with view it will notify the controller either requesting some model data or manipulate the model data.

Importance of MVC

The importance of this architecture is loose coupling between model objects and view objects. We can reuse the same view at different places with different data. No need of writing the same lines of code for same view again and again.

For example suppose we have created an app which will display list of songs and after tapping on any song it will display song’s details. Now the same UI can be used in an app which will display details about movie. Only data is changing, view is same for both the apps. So the view layer of music app can be moved to movie app easily.

How to implement MVC pattern in project ?

We have to create separate files for model, view and controller in our project. 

  1. For creating model create on cocoa touch class and make it subclass of NSObject.
  2. For creating view create cocoa touch class and make it subclass of UIView.

Model will interact with controller using NSNotificationCenter and view will interact with controller using delegation. There is no general rule of communication between controller and model or controller and view, but since we need to instantly tell the controller that some data got changed in model, so we will broadcast this to controller using NSNotificationCenter.

In the below example I will be creating a demo app which will show the list of songs along with its artist.

Let’s start development

  1. Create groups in project directory as follow
  2. Go to new file and select a cocoa touch class which will be subclass of NSObject. This will be model class.
  3. Again go to new files and select a cocoa touch class which will be subclass of UIView.
  4. Come to ViewController.h. There we need to initialise modal class and view class. Import class file of model and view.
  5. Put methods as shown below

Project hierarchy

 

fileHierarchy

 

MVCModel.m

#import "MVCModel.h"

@implementation MVCModel

-(void)getData{
    NSError *error;
    NSURL *URL= [[NSBundle mainBundle] URLForResource:@"File" withExtension:@"json"];
    NSData *data = [NSData dataWithContentsOfURL:URL];
    NSDictionary *objects = (NSDictionary*)[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
    [[NSNotificationCenter defaultCenter]postNotificationName:@"MODELDATA" object:nil userInfo:objects];
}

@end


MVCView.h

#import <UIKit/UIKit.h>
#import "MVCTableViewCell.h"

@interface MVCView : UIView <UITableViewDelegate,UITableViewDataSource>
{
    MVCTableViewCell *cell;
}
@property (strong,nonatomic) NSArray *dataArray;
@property (strong, nonatomic)UITableView *tableViewObj;
@property (nonatomic, assign) CGRect frameRect;
@property(strong,nonatomic)UILabel *headerLabel;

-(CGRect)setupTableViewWithArray:(NSArray*)arr referenceRect:(CGRect)rec andHeaderTitle:(NSString*)str;

@end

 

 

MVCView.m

#import "MVCView.h"

@implementation MVCView

-(id)initWithFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if (self) {
        self.frameRect = frame;
    }
    return self;
}

-(CGRect)setupTableViewWithArray:(NSArray*)arr referenceRect:(CGRect)rec andHeaderTitle:(NSString*)str{
    _dataArray = arr;
    
    
    _headerLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 0, self.frame.size.width, 42)];
    _headerLabel.text = str;
    _headerLabel.textColor = [UIColor grayColor];
    _headerLabel.backgroundColor=[UIColor clearColor];
    [self addSubview:_headerLabel];
    
    if (_dataArray.count==0) {
        self.tableViewObj = [[UITableView alloc]initWithFrame:CGRectMake(0, CGRectGetMaxY(_headerLabel.frame), self.frame.size.width, 66) style:UITableViewStylePlain];
        
        UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(10, 0, self.tableViewObj.frame.size.width, self.tableViewObj.frame.size.height)];
        label.numberOfLines = 0;
        label.textAlignment = NSTextAlignmentLeft;
        label.text = @"No data found";
        [self.tableViewObj addSubview:label];
        
    } else {
        self.tableViewObj = [[UITableView alloc]initWithFrame:CGRectMake(0, CGRectGetMaxY(_headerLabel.frame), self.frame.size.width, self.frame.size.height-2*CGRectGetMaxY(_headerLabel.frame)) style:UITableViewStylePlain];
    }
    self.tableViewObj.delegate = self;
    self.tableViewObj.dataSource = self;
    [self.tableViewObj setAlwaysBounceVertical:YES];
    self.tableViewObj.showsVerticalScrollIndicator = YES;
    [self addSubview:self.tableViewObj];
    self.tableViewObj.rowHeight = UITableViewAutomaticDimension;
    self.tableViewObj.estimatedRowHeight = 100;
    
    CGRect selfFrame = self.frame;
    selfFrame.size.height = CGRectGetMaxY(self.tableViewObj.frame);
    self.frame = selfFrame;
    
    return selfFrame;
}

-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
    return 1;
}

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return _dataArray.count;
}

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 58;
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *simpleTableId = @"MVCTableViewCell";
    cell = (MVCTableViewCell *) [tableView dequeueReusableCellWithIdentifier:simpleTableId];
    
    if (cell == nil)
    {
        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"MVCTableViewCell" owner:self options:nil];
        cell = [nib objectAtIndex:0];
    }
    NSDictionary *dic = [_dataArray objectAtIndex:indexPath.row];
    cell.nameLabel.text = [dic objectForKey:@"name"];
    cell.artistLabel.text = [dic objectForKey:@"artist"];
    
    return cell;
}

@end

 

ViewController.m

#import "ViewController.h"
#import "MVCModel.h"
#import "MVCView.h"

@interface ViewController (){
    MVCModel *modelObj;
    MVCView *viewObj;
    CGRect rect;
}

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    rect = [[UIScreen mainScreen] bounds];
    modelObj = [[MVCModel alloc] init];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(modalData:) name:@"MODELDATA" object:nil];
    
    viewObj = [[MVCView alloc] initWithFrame:CGRectMake(0, 20, rect.size.width, rect.size.height)];
    [self.view addSubview:viewObj];

    [modelObj getData];
}

#pragma mark - Model data
-(void)modalData:(NSNotification*)notification{
    NSDictionary *dic = notification.userInfo;
    //NSLog(@"Vallue of dic in Model is:%@",dic);
    NSString *codeStr = [NSString stringWithFormat:@"%@",[dic objectForKey:@"code"]];
    if ([codeStr isEqualToString:@"19001"]) {
        NSArray *dataArray = [dic objectForKey:@"data"];
        [viewObj setupTableViewWithArray:dataArray referenceRect:rect andHeaderTitle:@""];
        NSLog(@"Value of dataArray is:%@",dataArray);
    }
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}


@end

 

 

NOTE : For demo purpose I have created one local JSON file and fetching data from there. So in that fetching method in modal class we just need to replace with web services calling API. Just create an empty file with .json extension and copy the below code there.

Here is File.json

 

{
    "code": "19001",
    "data": [
             {
             "name": "Afire Love",
             "artist": "Ed Sheeran"
             },
             {
             "name": "Thriller",
             "artist": "Michael Jackson"
             },
             {
             "name": "Like a Prayer",
             "artist": "Madonna"
             },
             {
             "name": "When Doves Cry",
             "artist": "Prince"
             },
             {
             "name": "I Wanna Dance With Somebody",
             "artist": "Whitney Houston"
             },
             {
             "name": "Baby One More Time",
             "artist": "Britney Spears"
             },
             {
             "name": "It’s Gonna Be Me",
             "artist": "N Sync"
             },
             {
             "name": "Everybody (Backstreet’s Back)",
             "artist": "the Backstreet Boys"
             },
             {
             "name": "Rolling in the Deep",
             "artist": "Adele"
             },
             {
             "name": "Don’t Stop Believing",
             "artist": "Journey"
             },
             {
             "name": "Billie Jean",
             "artist": "Michael Jackson"
             },
             {
             "name": "Single Ladies (Put a Ring on It)",
             "artist": "Beyoncé"
             },
             {
             "name": "Genie in a Bottle",
             "artist": "Christina Aguilera"
             }
            ]
}

 

Summary

As we can se the view class is separate from model class. We can use this view class at multiple places or may be in multiple projects. But also there are some limitations of MVC pattern.

1. It increases the complexity as it divides the code into three layers.

2. There is insufficiency of data access in view layer.

3. But the main disadvantage is one way communication between model and view via controller.

These disadvantages are minimised in one more design pattern i.e. MVVM which we will be discussing in next post.