Mobile Development 13 min read

Componentization Architecture and Engine Design for iOS Applications at iQIYI

iQIYI’s iOS team replaced a mediator‑based component system with a registration‑driven engine, allowing independent development, hot‑plug module addition, server‑controlled launches, and full Open‑Closed compliance, while integrating CocoaPods, GitLab, and Jenkins for automated binary delivery and scalable modular architecture.

iQIYI Technical Product Team
iQIYI Technical Product Team
iQIYI Technical Product Team
Componentization Architecture and Engine Design for iOS Applications at iQIYI

Author Introduction

Razor, senior engineer in iQIYI iOS infrastructure team, has been working on the iQIYI video app since 2013, focusing on componentization, performance optimization and crash reduction.

Problem Statement

iQIYI runs many business lines (bubble, reading, show, movie tickets, e‑commerce, comics, games, etc.). As the number of modules and their interactions increase, coupling between business modules becomes a critical issue. Directly referencing a module makes independent development and testing difficult, and any change in a module’s interface forces changes everywhere it is used.

Goals

1. Enable independent development and testing of multiple business lines without interference. 2. Provide a unified calling interface to simplify inter‑module communication.

First Componentization (Mediator Pattern)

We introduced a component‑based approach where each business is packaged as a component. Components are divided into:

Basic components (network, data processing, etc.)

Business components (e.g., movie‑ticket module)

The engine (named QYEngine ) mediates all module interactions, eliminating direct module references. The engine consists of the following parts:

QYEControlCenter – core scheduler and dispatcher.

QYEChecker – validates module registration.

QYEParser – converts a call into a task.

QYETask – represents a single module invocation.

QYETaskStack – stores tasks that require callbacks.

QYEActionHandler – turns a task into an event call.

QYEInterfaceManager – provides the concrete implementation for each module.

Example of a URL‑router style call (not used in the final design):

[QYRouter  openURL:@"iqiyi://ModuleA?xx=xx" params:xx completion:xx];

Typical engine usage to open a login registration page:

EngineObj *obj = [EngineObj engineObjWithModule:ModuleLogin type:LoginRegisterType andParams:@{@"info":@"请先注册"}];
EngineCallback *cb = [EngineCallback engineCallbackWithTarget:self action:@selector(registerDone:)];
EngineSend(obj, cb);

The call flow is:

EngineSend forwards the EngineObj to the ControlCenter.

ControlCenter invokes Checker for validation.

Parser creates a QYETask .

Task is handed to ActionHandler.

If a callback is needed, the task is stored in TaskStack.

ActionHandler finally calls the injected Manager to execute business logic.

Diagrams (omitted) illustrate the internal flow and the relationship between Engine, Manager, and modules.

Issues with the First Approach

1. Adding a new module requires modifying Engine’s header and adding a new protocol method, violating the Open‑Closed Principle. 2. The main project’s Manager must be updated for every new module or interface change, increasing maintenance effort. 3. When module selection is driven by server data, developers still need to write explicit launch code, preventing true hot‑plug capability.

Second Componentization (Registration‑Based)

To address the above pain points, we switched to a registration‑based model. Each module registers itself with the Engine during its +load method and implements a protocol that the Engine can invoke.

+ (void)load {
    [Engine registerID:@"2" withClass:[self class]];
}

+ (void)launchWithObj:(Obj *)obj {
    // Module‑specific creation logic
}

The Obj class carries three parameters:

// Server‑provided data (module ID, parameters)
@property (nonatomic, strong) NSDictionary *serverParams;
// Client‑provided data (e.g., view controller to present)
@property (nonatomic, strong) NSDictionary *clientParams;
// Callback when the module exits
@property (nonatomic, copy) EngineClose close;

Calling a module now looks like:

Obj *obj = [Obj objWithSP:sp andCP:cp closeBlock:close];
EngineOpen(obj);

The Engine maintains a map of registered IDs to classes, so no Engine code changes are needed when adding or removing modules. The Manager is injected at startup:

EngineManager *mgr = [[EngineManager alloc] init];
[QYEApi setInterfaceMgr:mgr];

And the Manager implements the required protocol methods, e.g.:

- (void)openLoginModuleByType:(Type)type andParams:(NSDictionary *)params {
    // Implementation for opening login module
}

Advantages of the Registration Model

Fully respects the Open‑Closed Principle – Engine never needs modification for new modules.

No additional code in the main project when a module is added.

Server‑driven dynamic invocation is possible; a single call path supports all current and future modules.

Continuous Integration

To streamline module delivery we use CocoaPods and GitLab for binary management and Jenkins for building and publishing each module’s library. After a developer triggers a build, Jenkins updates the main project’s Podfile, compiles the binary, and uploads it, making the new module instantly available to the main app.

Summary

Componentization at iQIYI required cooperation between server, business teams, and the infrastructure team. A unified entry point simplifies statistics (module launch counts, active module during crashes) and supports automated CI pipelines. The transition from a Mediator‑based first componentization to a registration‑based second componentization eliminated manual Engine maintenance, enabled hot‑plug module addition, and created a complete, scalable workflow for iOS modular development.

iOSCI/CDcomponentizationMediator PatternEngine Architecturemodular designRegistration
iQIYI Technical Product Team
Written by

iQIYI Technical Product Team

The technical product team of iQIYI

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.