SwiftUI 中的模块化特性架构

发布: (2025年12月11日 GMT+8 06:44)
6 min read
原文: Dev.to

Source: Dev.to

🧩 1. 什么是功能模块?

功能模块是一个 自包含的单元,代表应用中的一个功能块:

Home/
Profile/
Settings/
Feed/
Auth/
OfflineSync/
Notifications/

每个模块包含:

  • 视图(Views)
  • 视图模型(ViewModels)
  • 数据模型(Models)
  • 服务(Services)
  • 路由定义(Routing definitions)
  • 预览(Previews)
  • Mock 数据(Mocks)

一个功能应该可以被移除而不破坏整个应用。如果你可以删除一个文件夹且其他地方都不受影响 → 那么它是模块化的。

📁 2. 推荐的文件夹结构

下面是一种干净、可扩展的模式:

AppName/

├── App/
   ├── AppState.swift
   ├── AppEntry.swift
   └── RootView.swift

├── Modules/
   ├── Home/
   ├── HomeView.swift
   ├── HomeViewModel.swift
   ├── HomeService.swift
   ├── HomeRoute.swift
   └── HomeMocks.swift

   ├── Profile/
   ├── ProfileView.swift
   ├── ProfileViewModel.swift
   ├── ProfileService.swift
   ├── ProfileRoute.swift
   └── ProfileMocks.swift

   ├── Settings/
   ├── SettingsView.swift
   ├── SettingsViewModel.swift
   └── SettingsMocks.swift

   └── Shared/
       ├── Components/
       ├── Models/
       ├── Utilities/
       └── Styles/

├── Services/

└── Resources/

不再有“上帝文件夹”——每个模块都是独立的世界。

🔌 3. 模块边界的依赖注入

每个模块通过协议声明 它需要什么

protocol ProfileServiceProtocol {
    func fetchProfile(id: String) async throws -> Profile
}

模块 知道具体实现(真实 API 客户端、离线缓存、Mock 数据源、测试环境)。应用在外部注入具体的服务:

ProfileViewModel(
    service: appServices.profileService
)

好处

  • 可测试性
  • 灵活性
  • 易于替换
  • 隔离性

🧭 4. 模块间的路由

每个模块定义自己的路由类型:

enum ProfileRoute: Hashable {
    case details(id: String)
    case followers(id: String)
}

根视图聚合各模块的路由:

enum AppRoute: Hashable {
    case profile(ProfileRoute)
    case home
    case settings
}

统一的导航处理:

.navigationDestination(for: AppRoute.self) { route in
    switch route {
    case .profile(let pr): ProfileRouter.view(for: pr)
    case .home: HomeView()
    case .settings: SettingsView()
    }
}

模块专属的路由器:

struct ProfileRouter {
    @ViewBuilder static func view(for route: ProfileRoute) -> some View {
        switch route {
        case .details(let id):
            ProfileView(userID: id)
        case .followers(let id):
            FollowersView(userID: id)
        }
    }
}

模块保持独立。

🧪 5. 功能模块应完全可预览

示例预览:

#Preview("Profile Details") {
    ProfileView(
        viewModel: ProfileViewModel(
            service: MockProfileService()
        )
    )
}

自包含的预览

  • 加速开发
  • 防止回归
  • 降低认知负担

预览文件夹成为每个模块的迷你设计系统。

🏎 6. 模块化带来的性能提升

模块化代码库带来:

  • 更快的构建时间 – 只重新编译变动的模块。
  • 更安全的重构 – 模块之间不泄露内部细节。
  • 更好的测试隔离 – 按模块运行测试。
  • 更小的认知负荷 – 新成员能快速理解功能。
  • 更佳的 CI 并行化 – 每个模块可以是独立的测试目标。

这些收益在拥有 10+ 屏幕的应用中尤为明显。

🔄 7. 功能通信模式

模块 不应 直接相互 import。可以使用以下模式之一:

  • AppRoute(最常用) – 根层协调导航。
  • 服务层 – 模块通过共享的服务协议进行通信。
  • 事件总线 – 处理全局副作用,如埋点统计。
  • 共享模型 – 明确放在 Modules/Shared 下。

关键规则: 📌 模块向“上层”说话,而不是横向交流。

🧵 8. 在每个模块内部隔离业务逻辑

所有业务逻辑都应位于对应模块内部:

Profile/
   ProfileViewModel.swift
   ProfileService.swift
   ProfileValidation.swift
   ProfileFormatter.swift

避免:

  • 全局工具类
  • 共享状态
  • 引入无关模块

这样可以保持代码所有权的清晰。

🧱 9. 跨应用公共模块

你的 Modules/Shared 文件夹应包含:

  • 基础组件
  • 排版(Typography)
  • 主题系统
  • 全局模型
  • 网络工具
  • 动画帮助类

绝不要在这里放置特定功能的逻辑。

🧭 10. 何时该进行模块化?

使用以下检查清单:

  • 应用拥有 6+ 个屏幕
  • 两位或以上开发者 在同一代码库工作
  • 功能可以 独立发布
  • 需要 快速预览
  • 需要 安全的重构
  • 使用 深度链接
  • 支持 离线模式
  • 使用 DI + 全局 AppState

如果满足 3 条或以上 → 进行模块化。

🚀 最后思考

对 SwiftUI 应用进行模块化会彻底改变你的代码库:

  • 更易于开发
  • 更易于测试
  • 更易于扩展
  • 更易于重构
  • 更易于协作

这是一项能显著提升架构质量的重大升级。

Back to Blog

相关文章

阅读更多 »

构建可复用的 SwiftUI 组件库

SwiftUI 让构建 UI 变得轻而易举——但构建在整个应用中外观一致的可复用组件却是另一项挑战。随着应用的增长,UI 复制……

Swift #11:守卫语句

Guard 语句 guard 包含一个条件,随后是 else 和一个 guard 块。如果条件为 false,则执行 guard 块并…

Swift #6:可选

可选类型 有时需要指示变量的缺失值。对于这些情况,Swift 有修饰符 ?,它将任何类型…

从算法到冒险

《From Algorithms to Adventures》的封面图片 https://media2.dev.to/dynamic/image/width=1000,height=420,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-...