Mobile Development 37 min read

Understanding MVC and MVVM Patterns in iOS Development

This article provides a comprehensive guide to the MVC and MVVM architectural patterns for iOS, explaining their components, interactions, KVO usage, and includes complete Objective‑C code examples that demonstrate how to implement both patterns in a real‑world news‑list app.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
Understanding MVC and MVVM Patterns in iOS Development

The article begins with a preface urging readers to invest time in the extensive textual explanation of MVC and MVVM design patterns for iOS development.

MVC Overview

It introduces the classic MVC diagram from Stanford, describing the responsibilities of Model (data management), View (UI rendering), and Controller (coordination). The text emphasizes that most apps simply display data and handle user interactions, which already follows MVC implicitly.

Key interactions are explained:

Model provides data and does not know about the View.

View displays UI elements and forwards events.

Controller mediates by assigning Model data to View and handling user actions.

The article highlights the target‑action mechanism, delegate and dataSource protocols, and the importance of keeping View and Model decoupled.

KVO and Notification

Key‑Value Observing (KVO) is presented as a way for the Controller to react to Model changes without the Model needing to know about the Controller. Notification is mentioned for broader event broadcasting.

MVC Code Demo

@interface DHNewsModel : NSObject

@property (nonatomic, strong, readonly) id dataList;
- (void)getData;
@end

@implementation DHNewsModel
- (void)getData {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        self.dataList = @[@{ @"title":@"新闻一", @"date":@"2016-01-25", @"image":@"http://...jpg", @"content":@"blablabla" },
                         @{ @"title":@"新闻二", @"date":@"2016-01-27", @"image":@"http://...jpg", @"content":@"ahahaha" }];
    });
}
@end
#import "ViewController.h"
#import "DHNewsModel.h"

@interface ViewController ()
@property (nonatomic, strong) UITableView *tableView;
@property (nonatomic, strong) DHNewsModel *model;
- (void)_registerObeserver;
- (void)_unregisterObserver;
@end

@implementation ViewController
- (void)dealloc { [self _unregisterObserver]; }
- (void)viewDidLoad {
    [super viewDidLoad];
    [self.view addSubview:self.tableView];
    [self.model getData];
    [self _registerObeserver];
}
- (void)_registerObeserver {
    [self.model addObserver:self forKeyPath:@"dataList" options:NSKeyValueObservingOptionNew context:nil];
}
- (void)_unregisterObserver {
    [self.model removeObserver:self forKeyPath:@"dataList"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    [self.tableView reloadData];
}
- (UITableView *)tableView {
    if (!_tableView) {
        _tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
        _tableView.dataSource = self;
        _tableView.delegate = self;
        _tableView.tableFooterView = [[UIView alloc] init];
    }
    return _tableView;
}
- (DHNewsModel *)model {
    if (!_model) { _model = [[DHNewsModel alloc] init]; }
    return _model;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [self.model.dataList count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cellIdf"];
    if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"cellIdf"]; }
    NSDictionary *infoDic = self.model.dataList[indexPath.row];
    cell.textLabel.text = infoDic[@"title"];
    cell.detailTextLabel.text = infoDic[@"date"];
    NSURL *imageUrl = [NSURL URLWithString:infoDic[@"image"]];
    dispatch_async(dispatch_get_global_queue(0,0), ^{
        NSData *imageData = [NSData dataWithContentsOfURL:imageUrl];
        UIImage *image = [UIImage imageWithData:imageData];
        dispatch_async(dispatch_get_main_queue(), ^{ cell.imageView.image = image; });
    });
    return cell;
}
@end

Transition to MVVM

The article explains why MVVM was introduced: to separate data parsing logic from the Controller, reducing its responsibilities. It defines Model, View, and ViewModel, and stresses that the ViewModel handles data transformation while the Controller merely binds to the ViewModel.

MVVM Code Demo

#import "DHNewsViewModel.h"
#import "DHNewsModel.h"
#import
@interface DHNewsViewModel ()
@property (nonatomic, strong) DHNewsModel *model;
@end

@implementation DHNewsViewModel
- (NSString *)observingKeyPath { return @"model.dataList"; }
- (void)getData { [self.model getData]; }
- (NSInteger)numberOfRowsInSection:(NSInteger)section { return [self.model.dataList count]; }
- (NSString *)cellTitleAtIndexPath:(NSIndexPath *)indexPath { return self.model.dataList[indexPath.row][@"title"]; }
- (NSString *)cellDateAtIndexPath:(NSIndexPath *)indexPath { return self.model.dataList[indexPath.row][@"date"]; }
- (NSURL *)cellImageUrlAtIndexPath:(NSIndexPath *)indexPath { return [NSURL URLWithString:self.model.dataList[indexPath.row][@"image"]]; }
- (DHNewsModel *)model {
    if (!_model) { _model = [[DHNewsModel alloc] init]; }
    return _model;
}
@end
#import "ViewController.h"
#import "DHNewsViewModel.h"

@interface ViewController ()
@property (nonatomic, strong) UITableView *tableView;
@property (nonatomic, strong) DHNewsViewModel *viewModel;
- (void)_registerObeserver;
- (void)_unregisterObserver;
@end

@implementation ViewController
- (void)dealloc { [self _unregisterObserver]; }
- (void)viewDidLoad {
    [super viewDidLoad];
    [self.view addSubview:self.tableView];
    [self.viewModel getData];
    [self _registerObeserver];
}
- (void)_registerObeserver {
    [self.viewModel addObserver:self forKeyPath:[self.viewModel observingKeyPath] options:NSKeyValueObservingOptionNew context:nil];
}
- (void)_unregisterObserver {
    [self.viewModel removeObserver:self forKeyPath:[self.viewModel observingKeyPath]];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    [self.tableView reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [self.viewModel numberOfRowsInSection:section];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cellIdf"];
    if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"cellIdf"]; }
    cell.textLabel.text = [self.viewModel cellTitleAtIndexPath:indexPath];
    cell.detailTextLabel.text = [self.viewModel cellDateAtIndexPath:indexPath];
    NSURL *imageUrl = [self.viewModel cellImageUrlAtIndexPath:indexPath];
    dispatch_async(dispatch_get_global_queue(0,0), ^{
        NSData *imageData = [NSData dataWithContentsOfURL:imageUrl];
        UIImage *image = [UIImage imageWithData:imageData];
        dispatch_async(dispatch_get_main_queue(), ^{ cell.imageView.image = image; });
    });
    return cell;
}
- (UITableView *)tableView {
    if (!_tableView) {
        _tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
        _tableView.dataSource = self;
        _tableView.delegate = self;
        _tableView.tableFooterView = [[UIView alloc] init];
    }
    return _tableView;
}
- (DHNewsViewModel *)viewModel {
    if (!_viewModel) { _viewModel = [[DHNewsViewModel alloc] init]; }
    return _viewModel;
}
@end

Finally, the article concludes that repeated practice with MVVM will solidify understanding of the pattern and improve object‑oriented thinking, and suggests a simple exercise of displaying the current system time using Model‑View‑ViewModel.

Design Patternsmobile developmentiOSMVCMVVMObjective-CKVO
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.