设计良好的应用架构是构建可维护、可扩展和高质量应用的基础。以下是应用开发架构设计的关键考虑因素和常见模式:
核心架构原则
- 业务点分离:将应用分为不同的责任层
- 单一职责:每个组件只做一件事
- 可测试性:设计应便于单元测试和集成测试
- 可扩展性:能够轻松添加新功能
- 可维护性:代码清晰、文档完善
常见架构模式
1. MVC (Model-View-Controller)
- 模型:数据与业务逻辑
- 视图:用户界面
- 控制器:处理用户输入,协调模型与视图
2. MVP (Model-View-Presenter)
- 比MVC更解耦的变体
- Presenter取代Controller,处理所有业务逻辑
3. MVVM (Model-View-ViewModel)
- 特别适合数据绑定框架(如Android Jetpack, SwiftUI-RxSwift)
- ViewModel暴露数据流供View观察
处理业务逻辑,提供数据绑定,数据驱动型 UI(通过 @Published、ObservableObject 等)。
易于单元测试(ViewModel 不依赖 UIKit)。
4. Clean Architecture
- 分层架构(实体、用例、接口适配器、框架/驱动)
- 依赖规则:内层不依赖外层
- 适合复杂业务逻辑的应用
VIPER
VIPER 是 iOS 开发中一种清晰分离职责的架构模式,适合中大型项目,尤其需要高可测试性和模块化的场景。它将代码分为 View(视图)、Interactor(业务逻辑)、Presenter(协调)、Entity(数据模型)、Router(路由) 五个部分。下面详细说明如何设计 VIPER 架构,并提供完整代码示例。
VIPER 核心组件与职责
组件: 对应的职责分层 | 职责 |
---|---|
View | 展示 UI,处理用户交互,将事件传递给 Presenter(被动视图,无业务逻辑)。 |
Interactor: ViewModel | 处理核心业务逻辑(如网络请求、数据库操作),与 Entity 交互。 |
Presenter:Manager | 接收 View 的事件,调用 Interactor;从 Interactor 获取结果后更新 View。 |
Entity:model | 纯粹的数据模型(如结构体或类)。 |
Router | 处理模块间导航(如页面跳转),依赖 UIViewController 的传递。 |
VIPER 设计步骤与代码示例
场景示例:实现一个用户列表页面(UserList
模块)
1. 定义模块协议(Contracts)
明确各组件间的通信接口,放在 UserListContracts.swift
中:
// MARK: - 模块协议
protocol UserListViewProtocol: AnyObject {
func showUsers(_ users: [User])
func showError(_ error: String)
}
protocol UserListPresenterProtocol {
func viewDidLoad()
func didSelectUser(_ user: User)
}
protocol UserListInteractorProtocol {
func fetchUsers()
}
protocol UserListRouterProtocol {
func navigateToUserDetail(_ user: User)
}
2. 实现 Entity
定义数据模型(简单结构体):
// User.swift
struct User {
let id: Int
let name: String
let email: String
}
3. 实现 Interactor
处理数据获取逻辑(如网络请求):
// UserListInteractor.swift
class UserListInteractor: UserListInteractorProtocol {
weak var presenter: UserListPresenterProtocol?
func fetchUsers() {
// 模拟网络请求
let users = [
User(id: 1, name: "Alice", email: "alice@example.com"),
User(id: 2, name: "Bob", email: "bob@example.com")
]
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.presenter?.didFetchUsers(users)
}
// 错误示例
// presenter?.didFailWithError("Network error")
}
}
4. 实现 Presenter
协调 View 和 Interactor:
// UserListPresenter.swift
class UserListPresenter: UserListPresenterProtocol {
weak var view: UserListViewProtocol?
var interactor: UserListInteractorProtocol
var router: UserListRouterProtocol
init(view: UserListViewProtocol, interactor: UserListInteractorProtocol, router: UserListRouterProtocol) {
self.view = view
self.interactor = interactor
self.router = router
}
func viewDidLoad() {
interactor.fetchUsers()
}
func didSelectUser(_ user: User) {
router.navigateToUserDetail(user)
}
// Interactor 回调方法
func didFetchUsers(_ users: [User]) {
view?.showUsers(users)
}
func didFailWithError(_ error: String) {
view?.showError(error)
}
}
5. 实现 View(ViewController)
被动展示数据,处理用户输入:
// UserListViewController.swift
class UserListViewController: UIViewController, UserListViewProtocol {
var presenter: UserListPresenterProtocol!
private var users: [User] = []
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
presenter.viewDidLoad()
}
// MARK: - UserListViewProtocol
func showUsers(_ users: [User]) {
self.users = users
tableView.reloadData()
}
func showError(_ error: String) {
let alert = UIAlertController(title: "Error", message: error, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default))
present(alert, animated: true)
}
}
extension UserListViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
presenter.didSelectUser(users[indexPath.row])
}
// ... 其他 UITableView 方法
}
6. 实现 Router
处理页面跳转:
// UserListRouter.swift
class UserListRouter: UserListRouterProtocol {
weak var viewController: UIViewController?
static func createModule() -> UIViewController {
let view = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "UserListViewController") as! UserListViewController
let interactor = UserListInteractor()
let router = UserListRouter()
let presenter = UserListPresenter(view: view, interactor: interactor, router: router)
view.presenter = presenter
interactor.presenter = presenter
router.viewController = view
return view
}
func navigateToUserDetail(_ user: User) {
let detailVC = UserDetailRouter.createModule(user: user)
viewController?.navigationController?.pushViewController(detailVC, animated: true)
}
}
7. 组装模块(依赖注入)
在入口处创建 VIPER 模块:
// AppDelegate 或 SceneDelegate
let userListVC = UserListRouter.createModule()
window?.rootViewController = UINavigationController(rootViewController: userListVC)
VIPER 关键设计原则
单向数据流
View → Presenter → Interactor → Presenter → View-
依赖规则
- View 知道 Presenter,但不知道 Interactor 或 Router。
- Presenter 知道 View 和 Interactor,但不知道 Router 的具体实现。
- Router 知道如何创建模块和导航。
-
测试优势
- View:测试 UI 展示是否正确(通过 Mock Presenter)。
- Presenter:测试事件传递和逻辑协调(通过 Mock View 和 Interactor)。
- Interactor:测试纯业务逻辑(不依赖 UIKit)。
VIPER 的优缺点
优点 | 缺点 |
---|---|
职责清晰,模块化高 | 代码量显著增加(适合大型项目) |
便于单元测试 | 学习曲线较陡 |
避免 Massive View Controller | 需要手动管理模块间依赖 |
何时使用 VIPER?
- 项目复杂度高,需要长期维护。
- 团队协作开发,需严格分工。
- 对测试覆盖率要求高(如金融、医疗类应用)。
扩展建议
-
使用代码生成工具:
如 Generamba 自动生成 VIPER 模板文件。 -
结合 Combine/RxSwift:
在 Presenter 中处理响应式数据流,简化回调逻辑。
通过 VIPER,你可以构建高度解耦、可测试的 iOS 应用,但需权衡其带来的额外代码成本。
现代应用架构组件:分层架构
-
UI层:
- Activities/Fragments (Android)
- ViewControllers/Views (iOS)
- 组合式UI (Jetpack Compose/SwiftUI)
-
业务层:
- 业务逻辑和用例
- 领域模型
-
数据层:
- 仓库模式(Repository)
- 本地数据源(Room, CoreData)
- 远程数据源(Retrofit, Alamofire)
-
依赖注入:测试、第三方功能:
- Dagger Hilt (Android)
- Swinject (iOS) 是一个轻量级的依赖注入框架,专为 Swift 语言设计。
- 减少耦合,提高可测试性
架构决策考虑因素
- 应用复杂度:简单应用可能不需要复杂架构
- 团队规模:大型团队需要更严格的架构
- 测试需求:高测试覆盖率需要更解耦的设计
- 平台特性:考虑原生平台的最佳实践
- 未来扩展:预留适当扩展空间
实施建议
- 从简单开始,随着应用增长逐步演进架构
- 保持一致性 - 整个应用使用相同架构模式
- 文档化架构决策和模式
- 定期重构以保持架构健康
- 考虑使用模板或脚手架工具保持一致性
App完善项:
卡顿分析:Firebase、友盟
内存泄漏:MLeaksFinder
异常处理:AvoidCrash、JJException,但AvoidCrash已经停止维护,使用AvoidCrash后期需要自己维护,如果要使用第三方库可以考虑集成JJException
修复文件:MangoFix 使用OC相似语法编写热修复文件,支持Swift AES128加/解密热修复确保安全,内置语法、词法分析使用OC的Runtime动态替换原方法 原方法前缀添加ORG保留