26. 组件化
组件化的目标是“模块边界清晰、依赖可替换、功能可复用”。下面给出一套可落地的拆分方式。
一、模块分层
- Core:网络、存储、工具
- Features:业务模块
- UI:通用组件
结构示例:
App
├─ Core
├─ Network
├─ Storage
├─ Features
│ ├─ Home
│ ├─ Profile
└─ UIComponents
二、SPM 本地包
Package.swift 示例:
// swift-tools-version:5.7
import PackageDescription
let package = Package(
name: "Features",
platforms: [.iOS(.v13)],
products: [
.library(name: "ProfileFeature", targets: ["ProfileFeature"])
],
targets: [
.target(name: "ProfileFeature", dependencies: ["Core"]),
.target(name: "Core")
]
)
三、对外暴露入口
只暴露工厂方法,不暴露内部实现。
public struct ProfileFeature {
public static func makeRoot(userID: Int, service: UserService) -> UIViewController {
let vm = ProfileViewModel(userID: userID, service: service)
return ProfileViewController(viewModel: vm)
}
}
四、协议解耦依赖
public protocol UserService {
func fetch(userID: Int, completion: @escaping (Result<User, Error>) -> Void)
}
final class RemoteUserService: UserService {
func fetch(userID: Int, completion: @escaping (Result<User, Error>) -> Void) {
// 网络请求实现
}
}
业务模块只依赖协议,不依赖具体实现。
五、依赖注入
final class AppContainer {
let userService: UserService
init() {
userService = RemoteUserService()
}
func makeProfile(userID: Int) -> UIViewController {
ProfileFeature.makeRoot(userID: userID, service: userService)
}
}
六、资源与 Bundle
SPM 资源建议放在模块内,使用 Bundle.module 访问:
let image = UIImage(named: "avatar", in: .module, compatibleWith: nil)
七、避免循环依赖
- 业务模块只能依赖 Core/UI
- Core 不依赖任何业务模块
- 依赖方向单向
组件化不是堆模块数量,而是让每个模块职责稳定、边界清晰。