本文由 简悦SimpRead 转码,原文地址 www.raywenderlich.com
了解如何使用Tuist来创建和管理复杂的Xcode项目和工作空间。
万能的项目文件和它的老大哥--工作区,是你在iOS开发中要处理的最重要的痛点之一。 Tuist 是一个工具,它的目的是使处理Xcode项目更容易,通过考虑你的项目结构的复杂性。它接受了 Configuration by Convention 的理念,让你专注于你的项目内容而不是它的组织。
在这个Tuist教程中,您将学习如何通过使用Tuist来取代传统的Xcode项目工作流程。您将从一个已经运行的项目开始,一个显示顶级电影的应用程序,并更新它以使用Tuist。然后,您将利用Tuist的功能来。
- 删除项目和工作区文件。取而代之的是,你将在任何需要的时候生成你的项目。
- 添加一个单元测试目标。
- 处理Swift Package Manager的依赖关系。
- 添加依赖于Tuist工作流的新功能。
Note。这个中级教程假定你能够使用Xcode和编写Swift来构建一个iOS应用程序。该示例还使用了SwiftUI。
入门
点击本教程顶部或底部的 Download Materials 按钮,下载启动项目。启动项目使用The Movie Database作为后端,以显示最高评分的电影列表。
请看一下这个项目。你会看到一个标准的设置,它有一个网络类,用你的证书执行API请求,然后获取并转换响应,再将其传递给SwiftUIContentView中的List组件。
要使用启动项目,你需要注册一个账户,以获得一个API密钥。按照以下步骤注册,并在示例应用程序中设置你的API密钥。
- 在[TMDB网站]上注册一个免费账户(www.themoviedb.org/signup)。
- 转到API设置部分。
- 在 Request an API Key 部分,点击它说的 click here 的地方。
- 在下一个屏幕上,选择注册一个_开发者_的API密钥。
- 假设你已经同意了使用条款,你将需要输入一些关于你和你的应用程序的信息。你在这里输入什么并不重要。对于开发者API密钥,你将立即获得一个API密钥。
- 你最终会回到API设置部分,现在会显示一个API密钥。复制 API Key (v3 auth) 中的值。
- 在启动项目中,从 Project/MovieInfo/Source/Network 组中打开 Network.swift 。
- 在文件的顶部,用你的API密钥替换 <YOUR_API_KEY_HERE> 的`apiKey'值。
- 在 MovieInfo 的目标设置中,将 YOUR-BUNDLE-ID-HERE- 替换为你选择的捆绑ID。
最后,构建并运行。你会看到20部评分最高的电影的列表。
安装Tuist
你的下一步是安装Tuist工具。打开终端,运行以下命令。
bash <(curl -Ls https://install.tuist.io)
你会看到
==> Downloading tuistenv...
==> Unzipping tuistenv...
==> Installing tuistenv...
Password:
==> tuistenv installed. Try running 'tuist'
==> Check out the documentation at https://tuist.io/docs
这就安装了tuist命令。
现在你已经设置好了一切,是时候进入Tuist了!
开始使用Tuist
你的第一个目标是把这个项目转换为使用Tuist。从这一点开始,你将不再依赖将项目和工作区文件放在项目目录的根部。你将转变思路;Tuist只会在你需要的时候生成这些文件。
了解Tuist清单文件
Tuist清单是一个名为 Project.swift 的文件,它告诉Tuist如何创建你的Xcode项目。你将从命令行与Tuist互动,你将在Xcode中建立你的Tuist清单。Tuist为您生成一个环境来处理您的清单文件,然后启动Xcode为您提供代码完成和您在Xcode环境中所习惯的一切。
在你开始之前,删除现有的项目和工作区文件。从这里开始,你将不需要保留它们,因为当你需要它们的时候,你就会生成它们!
在Xcode中关闭工作区,然后删除 MovieInfo.xcodeproj 和 MovieInfo.xcworkspace 。
设置项目文件
接下来,创建一个空的Tuist清单文件。在终端中,导航到启动项目的根目录,并输入以下内容。
touch Project.swift
这将创建一个空清单。接下来,输入以下内容。
tuist edit
第一次运行该命令时,你可能会在终端看到一些关于下载和更新Tuist的信息。之后,你会看到下面的输出。
Generating project Manifests
Opening Xcode to edit the project. Press CTRL + C once you are done editing
Xcode将打开一个生成环境,你可以开始编辑项目文件。在 项目导航器 中选择 Project.swift 开始。
接下来,在 Project.swift 的顶部添加以下内容。
import ProjectDescription
这将使你能够访问Tuist项目DSL。你将用它来建立你的项目,并完成一些有用的代码。
定义你的项目
你的第一步是建立你的主项目。在 Project.swift 的 "import "下添加这些代码。
// 1
let project = Project(
// 2
name: "MovieInfo",
// 3
organizationName: "<YOUR_ORG_NAME_HERE>",
// 4
settings: nil,
// 5
targets: [])
下面是上面代码中的内容。
- 你创建了
project,它代表你的基本项目和工作区容器。你可以给它起任何名字,但Tuist推荐project。 name代表项目和工作区文件的名称。organizationName出现在每个新文件的顶部添加的版权声明中。用你的组织名称替换 <YOUR_ORG_NAME_HERE> 。settings允许你指定其他一些项目设置。你将在后面的章节中进行设置。targets代表与项目相关的目标列表。接下来你将填写这个。
注意。你可以用与任何Xcode项目相同的方式来构建Tuist清单:使用 Command-B 。在这里你可以得到与Swift其他地方一样的安全,如果你做错了,编译器会告诉你的
定义你的第一个目标
在空的targets数组中添加以下内容。
// 1
Target(
// 2
name: "MovieInfo",
// 3
platform: .iOS,
// 4
product: .app,
// 5
bundleId: "<YOUR_BUNDLE_ID_HERE>",
// 6
infoPlist: "MovieInfo/Info.plist",
// 7
sources: ["MovieInfo/Source/**"],
// 8
resources: ["MovieInfo/Resources/**"],
// 9
dependencies: [],
// 10
settings: nil)
这将做以下工作。
- 为
targets数组定义一个Target。 - 指定应用程序的名称。
- 设置平台为iOS。
- 将输出产品定义为一个应用程序。
- 设置目标的捆绑ID。将 <YOUR_BUNDLE_ID_HERE> 替换为你喜欢的捆绑ID。
- 找到相对于 Project.swift_的_Info.plist 。
- 使用通配符模式来指定 Source 目录下的所有源文件。任何目录在项目生成后都会成为Xcode组。
- 使用通配符模式来指定 Resources 目录中的所有源文件。这些目录在项目生成后也成为Xcode组。
- 指定没有依赖性,这是需要连接的外部框架。在本教程的后面你会添加一些。
- 这是用来指定对目标的其他设置。在本教程的后面,你也会添加一些。
注意。还有几个可选的属性可用于
Target。Tuist为这些属性生成了合理的默认值,在这种情况下,你不需要对它们进行自定义。然而,对于更复杂的项目,有几个有用的选项值得检查。例如,你可以定义运行脚本和构建阶段,自定义构建方案,链接到Xcode模板文件以自定义文件生成等等。请查看Project.swift docs以获得更多信息。
你的 Project.swift 现在已经准备好用来生成你的项目了。
生成你的第一个项目
好了,是时候使用Tuist来生成你的第一个项目和工作区了! 再次打开终端。按 Control-C 或 Command-. 来停止当前的编辑会话。Xcode将显示一个信息,表明工作区不再存在。
点击 Close 。
在生成项目之前,你将使用Tuist的lint命令来检查你的项目清单的有效性。
在终端,运行以下程序。
tuist lint project
该命令将对当前目录下的清单文件进行提示。
你会看到类似这样的输出。
Loading the dependency graph
Loading project at /Users/ski081/Desktop/WiP
Running linters
Linting the environment
Linting the loaded dependency graph
No linting issues found
接下来,在终端中,输入以下内容。
tuist generate
你会得到类似于以下的输出。
Generating workspace MovieInfo.xcworkspace
Generating project MovieInfo
Project generated.
Total time taken: 0.579s
现在,在终端运行以下程序,看看目录的新内容。
ls -al
你会看到类似这样的内容。
drwxr-xr-x@ 10 ski081 staff 320 May 30 22:01 .
drwx------@ 26 ski081 staff 832 May 30 21:51 ..
-rw-r--r--@ 1 ski081 staff 6148 May 29 08:50 .DS_Store
drwxr-xr-x@ 4 ski081 staff 128 May 30 08:23 AdditionalTargetFiles
drwxr-xr-x 3 ski081 staff 96 May 30 22:01 Derived
drwxr-xr-x@ 8 ski081 staff 256 May 30 21:50 MovieInfo
drwxr-xr-x@ 6 ski081 staff 192 May 30 22:01 MovieInfo.xcodeproj
drwxr-xr-x@ 6 ski081 staff 192 May 30 22:01 MovieInfo.xcworkspace
-rw-r--r--@ 1 ski081 staff 409 May 30 22:00 Project.swift
drwxr-xr-x@ 5 ski081 staff 160 May 30 08:24 config
祝贺你! 你生成了你的第一个项目和工作区。 :]
最后,输入以下内容,打开工作区。
xed .
注意:
xed .在当前目录下打开一个工作区,如果存在的话。如果没有,它将打开一个Xcode项目。所以,无论哪种情况,它都会打开 "正确 "的文件。
建立并运行。该应用程序的行为将与之前完全一样,但有一个巨大的区别。你已经消除了对项目文件的依赖性,现在你可以随时生成它。
引入焦点模式
在你开始通过Tuist添加额外的功能和项目之前,还有一个有趣的功能需要回顾。focus模式。焦点模式让你打开一个单一的目标和它的依赖关系。这让你可以忽略任何你当时不想使用的项目或依赖关系。
在你新生成的项目设置上试试这个。在终端,输入以下内容。
tuist focus MovieInfo
这将告诉Tuist只生成和打开你需要使用MovieInfo的资产。它还会从Xcode缓存中删除不需要的资产,并用预编译的资产代替它们,以确保Xcode不会尝试索引它们。
你会看到这样的信息。
Generating workspace MovieInfo.xcworkspace
Generating project MovieInfo
Deleted persisted 1622124912.TuistAnalytics.45978508-3C7B-4A87-B3DD-1B5FD4476A55.json
Xcode将以你的目标和所有依赖的初始化来打开。这对于像MovieInfo这样的小项目来说并不有用,但你可以看到它对有多个子项目和依赖关系的大项目有什么好处。
添加对设置文件的支持
接下来,你将从外部 .xcconfig 文件中生成你的项目和目标设置。这是一个最佳做法,让你对Tuist如何构建项目有更多的控制。
这种做法还可以将你与将来需要对项目和目标设置进行的任何修改隔离开来。如果你需要改变一个没有外部配置的构建设置,Tuist会在每次重新生成文件时覆盖它。然后,你需要记住在项目或目标设置中再次做出改变。
通过将Tuist指向一个外部源,你可以在那里进行修改,任何新的项目生成命令都会使用这些修改。
Tuist使用Settings来表示配置,你可以在项目和目标对象中使用。你将用它来设置外部配置源。
从终端开始另一个编辑会话。
tuist edit
打开 Project.swift ,在 "import "后面添加以下内容。
// 1
let projectSettings = Settings(
// 2
debug: Configuration(xcconfig: Path("config/MovieInfoProject.xcconfig")),
release: Configuration(xcconfig: Path("config/MovieInfoProject.xcconfig")),
defaultSettings: .none)
这将做以下工作。
- 声明一个`Settings'对象。
- 指定 .xcconfig 文件,用于调试和发布配置。这些文件已经在启动项目中,在 config 文件夹中。
接下来,在project的初始化中,用你的新对象替换settings参数。
settings: projectSettings,
接下来,你将对你的目标应用设置。在projectSettings下,创建targetSettings。
let targetSettings = Settings(
debug: Configuration(xcconfig: Path("config/MovieInfoTarget.xcconfig")),
release: Configuration(xcconfig: Path("config/MovieInfoTarget.xcconfig")),
defaultSettings: .none)
现在更新Target对象初始化中的settings参数,内容如下。
settings: targetSettings)
保存 Project.swift 并在终端机上用 Control-C 退出。现在,再次生成你的项目。
tuist generate
打开工作区。构建并运行。应用程序看起来和以前一样,但现在你的构建设置是在项目之外的,所以你可以在每次用Tuist生成项目时应用同样的设置。
设置外部依赖关系
你的下一个目标是在Tuist清单文件中设置外部框架依赖。这可以让您自动设置并将您在磁盘上已有的任何框架代码链接到您的项目。不再需要拖放和对Xcode的构建设置大惊小怪了!
想象一下,您已经决定将网络功能从iOS代码库中移出,这样您就可以与多个目标共享,可能是为了未来的macOS或watchOS应用程序。最简单的方法是提取网络层并将其变成您项目中的一个动态框架。
将文件移到地方
首先,你需要将相关文件移到一个单独的位置。这可以让你从Tuist清单文件中轻松引用它们。
启动项目有一个名为 AdditionalTargetFiles 的目录。将 NetworkKit 从该目录内移至项目根目录。它应该与 MovieInfo 目录和 Project.swift 放在一起。
完成后,你的目录结构应该是这样的。
将 MovieInfo/Source/Network 目录及其所有内容移至垃圾箱。
从Tuist生成框架
在终端中,切换到 NetworkKit 目录并运行编辑命令。
cd NetworkKit
tuist edit
像以前一样,这将在Xcode中打开一个项目。选择 Project.swift 。你会看到一个空白文件,上面有一个import。
现在,输入以下内容。
let projectSettings = Settings(
debug: Configuration(xcconfig: Path("config/NetworkKitProject.xcconfig")),
release: Configuration(xcconfig: Path("config/NetworkKitProject.xcconfig")),
defaultSettings: .none)
let targetSettings = Settings(
debug: Configuration(xcconfig: Path("config/NetworkKitTarget.xcconfig")),
release: Configuration(xcconfig: Path("config/NetworkKitTarget.xcconfig")),
defaultSettings: .none)
let project = Project(
name: "NetworkKit",
organizationName: "Ray Wenderlich",
settings: projectSettings,
targets: [
Target(
name: "NetworkKit",
platform: .iOS,
product: .framework,
bundleId: "<YOUR_BUNDLE_ID_HERE>",
infoPlist: "NetworkKit/Info.plist",
sources: ["NetworkKit/Source/**"],
settings: targetSettings)
])
不要忘记用你自己的值替换 <YOUR_BUNDLE_ID_HERE> 。捆绑ID必须与你在MovieInfo应用程序中使用的捆绑ID不同。
上面的代码与你的主项目定义相似,但有一个关键区别。注意Target初始化器中的product声明了一个框架而不是一个应用产品。这样就生成了一个动态的iOS框架。
保存该文件并在终端输入 Control-C 来结束编辑会话。
链接MovieInfo中的框架
在这一点上,你需要让Tuist知道新的框架,以便它能够生成和链接它。改变你的目录,回到项目的根目录,然后再次从终端运行编辑命令。
cd ...
tuist edit
当Xcode打开时,选择 Project.swift 。
现在,在dependencies中,添加一个依赖关系到数组中。
.project(
target: "NetworkKit",
path: .relativeToManifest("NetworkKit"))
这告诉Tuist生成并链接 NetworkKit 框架。它还传递了该框架的Tuist项目文件的相对路径。有了这个,Tuist可以同时生成你的主项目和框架项目,并将它们连接起来。酷! :]
生成和更新代码库
好了,表演时间到了! 在终端,按 Control-C 退出Tuist,然后再次生成你的项目。
tuist generate
现在,打开 MovieInfo 工作区。你会看到两个项目, MovieInfo 和 NetworkKit 。
由于你从 MovieInfo 目标中删除了网络代码,你需要让你的视图模型知道 NetworkKit。在 MovieListViewModel.swift 的顶部导入这个框架。
import NetworkKit
由于NetworkKit有一个新的 Network.swift 的副本,它还没有你的API密钥。打开 Network.swift ,用你的API密钥替换 <YOUR_API_KEY_HERE> 。
选择 MovieInfo 方案。建立并运行。该应用程序看起来和以前一样,但你现在使用的是一个独立的网络框架,完全由Tuist配置。 :]
添加一个单元测试目标
接下来,你将添加一些单元测试和相应的目标。当然,你将使用Tuist来做这件事
首先,将_MovieInfoTests_目录从_AdditionalTargetFiles_移到项目根目录下。如果你偷看里面,你会看到 Source 中的一个测试套件和_Resources_中的一个 Info.plist 。这就足够定义你的测试目标了。
用Tuist再次进入编辑模式。
tuist edit
在targets中添加一个测试目标的定义,代码如下。
Target(
name: "MovieInfoTests",
platform: .iOS,
product: .unitTests,
bundleId: "<YOUR_BUNDLE_ID_HERE>",
infoPlist: "MovieInfoTests/Resources/Info.plist",
sources: ["MovieInfoTests/Source/**"],
dependencies: [
.target(name: "MovieInfo")
])
这将为 MovieInfo 添加一个单元测试目标。
你会注意到 product 类型是.unitTests,它对 MovieInfo 有依赖性。确保用你自己的bundleID替换bundleId,它与用于 MovieInfo 和 NetworkKit 目标的ID不同。
接下来,是时候重新生成你的项目了。按 Control-C 来结束编辑,并再次运行生成命令。
tuist generate
你会看到下面的输出。
Generating workspace MovieInfo.xcworkspace
Generating project MovieInfo
Generating project NetworkKit
Project generated.
Total time taken: 0.150s
打开工作区。你会看到 MovieInfoTests 目标在 MovieInfo 下。
按 Command-U 来运行这些测试。他们会通过的。
接下来,你将使用 Swift Package Manager 为应用程序添加一个需要第三方库的功能。你将使用Tuist来管理这个!
添加海报图片
你的最终目标是为应用程序添加一个新功能:为列表中的电影提供海报图片。你将使用第三方库FetchImage,根据你从MovieDB API获得的海报路径,轻松地偷懒加载图片。
添加一个Swift包
首先,你要添加一个对_FetchImage_的依赖。你需要向_Project.swift_添加两个声明:一个包声明和一个新的目标依赖。
添加包定义
在编辑模式下打开Tuist,运行。
tuist edit
接下来,打开 Project.swift 。在项目的初始化中,在组织名称后添加以下参数。
// 1
packages: [
// 2
Package.remote(
// 3
url: "https://github.com/kean/FetchImage.git",
// 4
requirement: .upToNextMajor(
from: Version(0, 4, 0)))
],
上面的代码做了以下工作。
- 声明一个
packages数组参数。这是可选的,在最初的设置中没有包括,因为你到现在还不需要它。 - 声明一个远程
包。 - 定义包定义的远程URL。
- 定义版本要求。对于这个包,你要使用 Swift 包管理器的默认版本。 up to next major version ,从 FetchImage 的当前版本开始。
添加软件包的依赖性
还是在 Project.swift 中,将该包作为依赖关系添加到目标中。在 MovieInfo 目标声明中,在dependencies数组的末尾添加以下内容。
, .package(product: "FetchImage")
这让Tuist知道 MovieInfo 对 FetchImage 有依赖性。保存 Project.swift 。
现在,再次生成你的项目。在终端,按 Control-C 来结束Tuist编辑会话。输入生成命令。
tuist generate
你会看到如下的输出。
Generating workspace MovieInfo.xcworkspace
Generating project MovieInfo
Generating project NetworkKit
Resolving package dependencies using xcodebuild
2021-05-27 21:40:21.126 xcodebuild[13105:459506] [MT] DVTPlugInManager: Required plug-in compatibility UUID F56A1938-53DE-493D-9D64-87EE6C415E4D for GraphQL.ideplugin (com.apollographql.xcode.graphql) not present
Project generated.
Total time taken: 7.779s
Note: DVTPlugInManager 错误似乎是Tuist的一个错误。如果它出现在你身上,那么在你试图使用Swift Package Manager支持时,它就会出现。然而,它并不影响工作区或项目的功能。而且你可能根本就看不到它。
打开工作区,你会看到 FetchImage 已经被取走,可以使用了!
注意列表中的 Nuke 包。 Nuke 是 FetchImage 的一个依赖项。
实现列表中的海报图像
好了,现在你已经初始化了这个包,你要用它来实现列表中的图像获取。
设置一个ImageView
你的下一步是添加一个可重用的图像视图,使用_FetchImage_来懒散地加载图像。ImageView已经在启动项目中了。
要做到这一点,请遵循以下步骤。
- 将 ImageView.swift 从 AdditionalTargetFiles 移到 MovieInfo/Source/Views 。它应该和 ContentView.swift 放在一起。
- 将 ImageView.swift 拖到Xcode项目中 MovieInfo/Source/Views 的 Views 组中。
整合ImageView
你的下一步是在ContentView中使用ImageView。
打开 ContentView.swift 。在List中,在Text视图的上方添加以下内容。
// 1
if let url = movie.posterURL {
// 2
ImageView(url: url)
.frame(width: 92, height: 138)
}
这将执行以下操作。
- 检查是否有一个电影的URL。这是一个来自API的可选属性,所以它可能是
nil。 - 如果有海报的URL,使用
ImageView并传递给它进行加载。
建立并运行。现在你会看到懒惰加载的海报图片!
祝贺你! 你已经在Tuist中建立了一个完整的项目结构,它为你提供了一个具有大量功能的便携设置。
何去何从?
点击本教程顶部或底部的 下载材料 按钮,下载完成的项目文件。
本教程只是触及了Tuist所能做到的表面。此外,你还可以用它来设置框架模板、自定义构建阶段、使用CocoaPods和Carthage、lint项目以及更多。
查看Tuist docs以扩展您对Tuist的了解,并继续沿着Xcode项目文件独立的道路前进!
我们希望您能喜欢这个教程。如果您有任何问题或意见,请加入下面的论坛讨论!
raywenderlich.com周报
raywenderlich.com周报是您作为一个移动开发者了解最新信息的最简单的方式。
每周获得我们的教程和课程的摘要,并获得免费的深入的电子邮件课程作为奖励!