跳转到内容

多 Target 工程配置

本教程将介绍如何在 Tuist 中创建和管理多 Target 工程,包括:

  • 创建 App、Framework 和 Tests target
  • 配置 Target 之间的依赖关系
  • 使用 tuist graph 生成依赖关系图

在 Xcode 中,Target 表示一个将被构建的产品。每个 target 定义了一个产品(App、Framework、Library 等)以及构建该产品所需的源代码和资源文件。

多 Target 项目的典型结构:

  • App Target: 主应用程序
  • Framework Target: 业务模块或共享代码
  • Tests Target: 单元测试或 UI 测试

Project.swift 文件中定义多个 target:

import ProjectDescription
let project = Project(
name: "ProjMeta",
targets: [
// Framework Target - 业务模块
.target(
name: "CoreModule",
destinations: .macOS,
product: .framework,
bundleId: "dev.tuist.CoreModule",
infoPlist: .default,
buildableFolders: [
"CoreModule/Sources",
],
dependencies: []
),
// App Target - 主应用程序
.target(
name: "ProjMeta",
destinations: .macOS,
product: .app,
bundleId: "dev.tuist.ProjMeta",
infoPlist: .default,
buildableFolders: [
"ProjMeta/Sources",
"ProjMeta/Resources",
],
dependencies: [.target(name: "CoreModule")]
),
// Tests Target - 单元测试
.target(
name: "ProjMetaTests",
destinations: .macOS,
product: .unitTests,
bundleId: "dev.tuist.ProjMetaTests",
infoPlist: .default,
buildableFolders: [
"ProjMeta/Tests"
],
dependencies: [
.target(name: "ProjMeta"),
.target(name: "CoreModule")
]
),
]
)
属性说明示例值
nameTarget 名称"CoreModule"
destinations目标平台.macOS, .iOS, .iOSAndmacOS
product产品类型.app, .framework, .staticFramework, .unitTests
bundleIdBundle 标识符"dev.tuist.CoreModule"
buildableFolders源代码文件夹["CoreModule/Sources"]
dependencies依赖列表[.target(name: "CoreModule")]

Tuist 支持多种依赖配置方式:

// 同一项目内的 target
.target(name: "MyFramework")
// 不同项目的 target
.project(target: "MyFramework", path: "../FrameworkProject")
// 二进制框架
.framework(path: "path/to/Framework.framework")
// 系统 SDK
.sdk(name: "Foundation")
// Swift Package Manager 外部包
.external(name: "PackageName")

在 Tuist 中配置 Framework 类型的 Target 时,可以使用 .framework.staticFramework 两种产品类型。它们的主要区别在于链接方式:

特性.framework (动态).staticFramework (静态)
链接方式动态链接,运行时加载静态链接,编译时合并
链接时机运行时链接编译时链接
App 体积较小(共享 Framework)较大(代码复制到 App)
编译速度较快(无需重新链接)较慢(每次编译都重新链接)
资源文件可以包含不包含(需单独处理)
嵌入 App需要嵌入 Frameworks 目录无需嵌入
冷启动稍慢(需加载 Framework)较快(代码已打包)
版本控制可多 App 共享同一版本每个 App 独立副本

选择 .framework (动态 Framework) 的场景:

  • 多个 App 共用同一个 Framework(如 SDK)
  • Framework 需要包含资源文件(图片、Storyboard 等)
  • 减少最终 App 包体积
  • 希望 Framework 可以独立更新

选择 .staticFramework 的场景:

  • 对启动性能要求极高的场景
  • 不需要包含资源文件
  • 希望简化分发(无需嵌入 Framework)
  • 避免动态链接带来的兼容性问题
// 动态 Framework
.target(
name: "CoreModule",
product: .framework, // 动态链接
// ...
)
// 静态 Framework
.target(
name: "CoreModule",
product: .staticFramework, // 静态链接
// ...
)
Terminal window
cd demo/ProjMeta
tuist generate
Terminal window
# 生成 PNG 格式的依赖图(默认)
tuist graph
# 跳过测试目标
tuist graph -t
# 指定平台过滤
tuist graph -l macos
# 指定输出格式
tuist graph -f png
tuist graph -f svg
tuist graph -f dot
tuist graph -f json

运行上述命令后,Tuist 会生成一个 PNG 文件,显示 Target 之间的依赖关系:

多 Target 依赖关系图

从图中可以清晰看到:

  • CoreModule (.framework) - 基础框架,无依赖
  • ProjMeta (App) - 依赖于 CoreModule
  • ProjMetaTests (Unit Tests) - 依赖于 ProjMeta 和 CoreModule

在 SwiftUI 视图中使用 Framework:

import SwiftUI
import CoreModule
public struct ContentView: View {
public init() {}
private let coreModule = CoreModule(name: "CoreModule")
public var body: some View {
Text(coreModule.greet())
.padding()
}
}
import Foundation
public struct CoreModule {
public let name: String
public init(name: String = "CoreModule") {
self.name = name
}
public func greet() -> String {
return "Hello from \(name)"
}
}

Project.swifttargets 数组中添加新的 .target() 配置,并确保在 dependencies 中正确配置依赖关系。

运行 tuist graph 命令,它会自动生成可视化的依赖关系图。

Q: 测试 Target 应该依赖哪些 Target?

Section titled “Q: 测试 Target 应该依赖哪些 Target?”

测试 Target 通常依赖:

  1. 被测试的产品 Target(如 App 或 Framework)
  2. 任何被测试代码直接依赖的模块

Q: 如何处理 Target 之间的循环依赖?

Section titled “Q: 如何处理 Target 之间的循环依赖?”

避免创建循环依赖。正确的依赖方向应该是:

  • App → Framework → 更底层的 Framework
  • Tests → App/Framework

本教程介绍了:

  1. 多 Target 配置: 在 Project.swift 中定义 App、Framework 和 Tests target
  2. 依赖关系配置: 使用 .target() 配置 Target 之间的依赖
  3. 关系图生成: 使用 tuist graph 可视化项目结构

这种多 Target 架构有助于:

  • 代码复用:将共享代码提取到 Framework 中
  • 模块化:保持代码结构清晰
  • 测试:便于编写和运行单元测试