Mobile Development 27 min read

How MVP+Context Transforms iOS Architecture for Scalable Apps

This article analyzes common iOS architectural patterns, identifies the drawbacks of traditional MVP implementations, and introduces a deep‑optimization MVP+Context solution with role definitions, automatic binding, memory‑leak prevention, and Swift support, illustrated with real‑world code and diagrams.

Huolala Tech
Huolala Tech
Huolala Tech
How MVP+Context Transforms iOS Architecture for Scalable Apps

Background Introduction

In many iOS projects we encounter MVC, MVP, MVVM, VIPER and other patterns. As the codebase grows, view controllers become massive, coupling increases, and testing becomes difficult. Optimizing the architecture aims to improve maintainability, testability, extensibility, reduce development cost, and deliver a better user experience.

Common Architecture Patterns

MVC

Model‑View‑Controller separates the app into three independent layers: Model (business logic and data handling), View (UI presentation), and Controller (mediates between Model and View). It works well for small projects but the controller can become bloated as complexity rises.

MVP

Model‑View‑Presenter also has three layers. The Presenter handles user interactions, fetches data from the Model, formats it, and updates the View. This reduces coupling between Model and View, but the Presenter may become too large and tightly coupled to a specific View.

MVVM

Model‑View‑ViewModel introduces a ViewModel that bridges Model and View via data binding. It simplifies UI logic but can become hard to debug and maintain when the data flow is overly complex.

VIPER

View‑Interactor‑Presenter‑Entity‑Router enforces strict separation of concerns. It is suitable for large projects but adds many layers, increasing complexity for small apps.

Traditional MVP Practice

In MVP, all communication between View and Model goes through the Presenter, which often leads to a massive Presenter class and a proliferation of interface definitions, creating “glue code” and long access chains that hinder maintainability.

Typical MVP Issues

Excessive glue code across multiple interface files.

Long hierarchical access paths (e.g., view → presenter → parent presenter → …).

Difficulty accessing shared data models from deep sub‑views.

MVP+Context Architecture Solution

Role Division

Context middleware: passes information and shares data among all Presenters and Views.

Main Controller (VC): binds Context, Presenter, View, and Interactor; manages lifecycle.

Main View: handles UI, delegates logic to Context.

Main Presenter: contains business logic, may host multiple sub‑Presenters.

Interactor: implements detailed business operations.

Architecture Flow

Every View (including sub‑views) can directly obtain the Context object, which provides access to the main View, Presenter, and Controller, enabling low‑coupling, high‑cohesion interactions.

Automatic Binding

Binding is performed in viewDidLoad of the base controller. The following Objective‑C code demonstrates the binding process:

- (id<HomePresenterInterface>)presenter {
    if (!_presenter) {
        _presenter = [[HomePresenter alloc] initWithView:self];
        PricePresenter *pricePresenter = [[PricePresenter alloc] initWithView:self.priceView parentView:self];
        _presenter.pricePresenter = pricePresenter;
        _presenter.categoryPresenter = [[BusinessPresenter alloc] initWithView:self.categoryView parentView:self];
        _presenter.navPresenter = [[NavBarPresenter alloc] initWithView:self.navBarView parentView:self];
    }
    return _presenter;
}

Memory‑Leak Prevention

Context holds a weak reference to the controller to break retain cycles, ensuring that when the controller is deallocated, the Context is also released.

Middleware Everywhere

Using Objective‑C runtime association, any object can retrieve the Context by recursively searching its super‑view hierarchy. The association is weak to avoid leaks.

@implementation NSObject (HLLCT)

- (void)setContext:(HLLContext *)object {
    objc_setAssociatedObject(self, @selector(context), object, OBJC_ASSOCIATION_ASSIGN);
}

- (HLLContext *)context {
    id curContext = objc_getAssociatedObject(self, @selector(context));
    if (curContext == nil && [self isKindOfClass:[UIView class]]) {
        UIView *view = (UIView *)self;
        UIView *superV = view.superview;
        while (superV) {
            if (superV.context) { curContext = superV.context; break; }
            superV = superV.superview;
        }
        if (curContext) [self setContext:curContext];
    }
    return curContext;
}
@end

Supporting Swift

Swift cannot use categories, so a protocol with a default implementation provides similar Context access. The protocol is adopted by NSObject to make the property available to all classes.

protocol HLLUContextProtocol {
    var context: HLLUContext? { get set }
}

extension HLLUContextProtocol {
    var context: HLLUContext? {
        get {
            var cur: HLLUContext? = objc_getAssociatedObject(self, &kAssociatedObjectKey) as? HLLUContext
            if cur != nil { return cur }
            guard let view = self as? UIView else { return nil }
            var superV = view.superview
            while let sv = superV {
                if let ctx = sv.context { cur = ctx; break }
                superV = sv.superview
            }
            superV?.context = cur
            return cur
        }
        set { objc_setAssociatedObject(self, &kAssociatedObjectKey, newValue, .OBJC_ASSOCIATION_ASSIGN) }
    }
}

extension NSObject: HLLUContextProtocol {}

Swift Binding Example

public func startBinding(bundleName: String, prefixName: String) {
    self.isMVPStructor = true
    self.context = self.theContext
    // Bind Presenter
    if let presenterClass = NSClassFromString("(bundleName).HLLU\(prefixName)Presenter") as? HLLUBasePresenter.Type {
        let presenter = presenterClass.init()
        self.context?.presenter = presenter
        self.context?.presenter?.context = self.context
    }
    // Bind Interactor
    if let interactorClass = NSClassFromString("(bundleName).HLLU\(prefixName)Interactor") as? HLLUBaseInteractor.Type {
        let interactor = interactorClass.init()
        self.context?.interactor = interactor
        self.context?.interactor?.context = self.context
    }
    // Bind View
    if let viewClass = NSClassFromString("(bundleName).HLLU\(prefixName)View") as? HLLUBaseView.Type {
        let view = viewClass.init()
        self.context?.view = view
        self.context?.view?.context = self.context
    }
    // Mutual binding
    self.context?.presenter?.view = self.context?.view
    self.context?.presenter?.baseController = self
    self.context?.interactor?.baseController = self
    self.context?.view?.presenter = self.context?.presenter
    self.context?.view?.interactor = self.context?.interactor
    self.view = self.context?.view
}

Context‑Based Practice

Using the order‑confirmation page of the HuoLaLa app as an example, the workflow shows how Context enables direct communication between price, service, and vehicle modules without extensive glue code.

Quick MVP Template Generation

A Bash script can generate Presenter, Interactor, and View classes automatically based on a module name, reducing manual errors.

#!/bin/bash
read -p "Enter the module name: " moduleName
# Generate Presenter
presenterHeader="${moduleName}Presenter.h"
presenterImpl="${moduleName}Presenter.m"
echo "@interface ${moduleName}Presenter : NSObject" > $presenterHeader
echo "@property (nonatomic, weak) HLLContext *context;" >> $presenterHeader
echo "@end" >> $presenterHeader
echo "@implementation ${moduleName}Presenter" > $presenterImpl
echo "@end" >> $presenterImpl
# Similar code for Interactor and View omitted for brevity

Conclusion

The article examined the shortcomings of traditional MVP in large iOS projects and proposed a Context‑based MVP+Context architecture that simplifies binding, eliminates glue code, prevents memory leaks, and works seamlessly in both Objective‑C and Swift. Selecting the right architecture depends on project size and complexity, and the presented solution offers a practical path for scalable mobile development.

iOSSwiftMVPObjective‑Ccontext
Huolala Tech
Written by

Huolala Tech

Technology reshapes logistics

0 followers
Reader feedback

How this landed with the community

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.