本文由 简悦SimpRead 转码,原文地址 www.raywenderlich.com
了解如何使用Point-Fre......,以可理解和可预测的状态变化来构建你的iOS应用。
随着SwiftUI和Combine出现在越来越多的应用程序中,管理状态变得越来越重要。可组合架构(TCA)是一个提供许多有用工具的框架。它有助于用可理解和可预测的状态变化来构造你的应用程序。
TCA专注于状态管理、组合和测试。它是由来自Point-Free的Brandon Williams和Stephen Celis开发的。他们有许多视频提供关于函数式编程和Swift开发的信息。
在本教程中,你将创建一个应用程序,显示raywenderlich.com的最新公共GitHub存储库。此外,它还会展示有关GitHub账户的用户信息。你将了解到
- 状态管理以及它如何帮助你创建更好的应用程序。
- 孤立地开发和测试功能。
- 以一种可理解的方式管理依赖性和副作用。
- 可组合架构提供哪些工具来帮助你构造你的应用。
本教程假定你已经熟悉了SwiftUI和Combine,这样你就可以直接进入可组合架构。
注意。TCA框架背后的想法与有限状态机(FSM)或有限状态自动机(FSA)和状态设计模式非常接近。在阅读本教程的同时,你可能会发现熟悉这些概念是很有用的--参见 Where to Go From Here? 部分,以获得一些参考资料。
入门
首先,使用本教程顶部或底部的 Download Materials 按钮下载项目材料。启动项目 RepoReporter 已经包含了Composable Architecture Swift包。
建立并运行 starter 项目。它将看起来像这样。
用户功能已经实现,但其余部分看起来有点空。在本教程中,你将使用Composable Architecture完成资源库功能。
仓库功能提供了一些信息,如描述、星级和分叉的数量。它还允许你标记你最喜欢的软件库,以便以后重新访问它们。
探索可组合式架构
TCA围绕开发不同规模和复杂度的应用程序,关注不同的方面。它提供了解决各种问题的概念,包括。
- 状态管理。每个应用程序都由某种状态组成。TCA提供了一个概念来管理和分享状态。
- composition。这使你能够孤立地开发较小的功能,并将它们组合在一起,形成整个应用程序。
- 侧面效果。这些往往是难以理解和测试的。TCA试图通过定义一种处理它们的方法来改变这种情况。
- 测试。这一点始终很重要,而TCA使之易于实现。
- 经济学。有一个框架提供了一个方便的API来实现所有的组件。
了解TCA的组件
用TCA构建的应用程序由五个主要组件组成,它们有助于为你的应用程序建模。
- 状态。通常情况下,一个属性集合代表了一个应用程序的状态或一个分布在许多类中的功能。TCA将所有相关的属性放在一个单一的类型中。
- Actions: 一个枚举,包括在你的应用程序中可能发生的所有事件的情况,例如,当用户点击一个按钮,当一个定时器启动或一个API请求返回。
- Environment: 一个包装你的应用程序或功能的所有依赖关系的类型。例如,这些可以是具有异步方法的API客户端。
- Reducer: 一个使用给定动作将当前状态转换为下一个状态的函数。
- Store: 一个你的用户界面观察变化的地方,也是你发送动作的地方。基于这些动作,它运行还原器。
你可能想知道为什么要使用可组合架构。它有很多优点。
- 通过不同组件的数据流是明确定义的,而且是单向的。这使得它易于遵循和理解。
- 环境包含所有的依赖关系。你可以从一个地方了解和管理与外部世界的连接。有可能将实时环境与开发或测试环境进行切换。这允许你不费吹灰之力配置或模拟你的依赖关系。
- 通过将独立的功能组合在一起,每个功能都可以独立地进行规划、构建和测试。因此,使用TCA可以改变你在应用程序上的工作方式,让你一次专注于应用程序的一个部分,甚至可以孤立地运行它。
- 只有还原器通过处理动作来转换状态。因此,测试一个功能可以归结为运行带有动作的还原器,并将生成的状态与期望值进行比较。
使用可组合式架构
这听起来好像有很多事情要做,但别担心,可组合架构是一个框架,可以让你轻松上手。 :]
该框架通过 Swift 包管理器分发。你可以通过添加一个新的Swift包将其包含在你的项目中。不过,启动项目已经包含了这个框架,所以你可以马上开始。
为状态和动作建模
你将首先在启动项目的第一个标签中添加一个存储库的列表。你将使用TCA的状态和动作来完成这个任务,这是使用TCA的良好起点。
打开 RepositoryFeature.swift ,你会发现RepositoryState、RepositoryAction、RepositoryEnvironment和repositoryReducer。
这些是你需要为一个特性创建的组件。存储器已经由框架提供,所以不需要再创建一个新的。
RepositoryState在一个地方定义了存储库特性的状态。为了显示存储库并将其标记为你的最爱,你需要两个属性。在RepositoryState里面添加这两个。
var repositories: [RepositoryModel] = []
var favoriteRepositories: [RepositoryModel] = []
正如属性名称所示,你将在这两个数组中存储所有的存储库。
接下来,你需要定义哪些动作可以在存储库标签中发生。把这些添加到RepositoryAction。
// 1
case onAppear
// 2
case dataLoaded(Result<[RepositoryModel], APIError>)
// 3
case favoriteButtonTapped(RepositoryModel)
下面是每个动作的描述。
- 当用户选择 Repositories 标签并出现视图时, RepoReporter 会加载新数据。因此,需要有一个动作来发送API请求。
- 第二个动作代表API请求完成的事件,可以是存储库的列表,也可以是错误。
- 最后,你需要另一个动作,当用户点击存储库的_Favorite_按钮时。
从视图访问存储库
你的视图并不与还原器或状态直接交互。相反,它从存储空间获取数据并将动作传递给它。存储器会根据给定的动作来执行还原器。这些还原器可以访问状态,并根据给定的动作更新状态,从而触发视图的更新。你可以在这里看到这个过程。
第一步是让视图访问存储空间。打开 RepositoryView.swift 。RepositoryView是代表一个单一存储库的视图。在这个视图中,按住 Command 并点击 VStack 。在菜单中,选择 Embed... ,将其包裹在一个容器中。接下来,替换
Container {
替换为
WithViewStore(self.store) { viewStore in
这是一个包裹其他视图的SwiftUI视图,类似于GeometryReader。现在你可以通过viewStore访问存储,从其中包含的状态中读取数据并向其发送动作。每当商店发生变化时,视图会自动更新。
接下来,按照上面同样的方法,将ScrollView嵌入到RepositoryListView的容器中,然后将
Container {
替换为
WithViewStore(self.store) { viewStore in
就像你之前做的那样。
最后,对FavoritesListView中的ScrollView重复同样的步骤。
注意。不要错过上面的步骤。
现在,你会得到错误,但你会通过下面的步骤解决它们。
-
在
RepositoryView中,将结构顶部的两个属性替换为以下内容。let store: Store<RepositoryState, RepositoryAction> let repository: RepositoryModel这确保了视图在初始化时有一个对保存所有资源库的商店的引用。
在
RepositoryListView和FavoritesListView中,将repositories和favoriteRepositories替换为。let store: Store<RepositoryState, RepositoryAction>以前,你把
repositories和favoriteRepositories传递给视图。但由于它们已经是状态的一部分,因此可以通过存储空间访问,视图不再需要它们了。 -
接下来,你要修复所有使用你刚刚替换的属性的地方。在
RepositoryListView中,找到。ForEach(repositories) { repository in并把它换成。
ForEach(viewStore.repositories) { repository in同样地,在
FavoriteListView中,替换为ForEach(favoriteRepositories) { repository in替换为
ForEach(viewStore.favoriteRepositories) { repository in在
RepositoryView中,找到。if favoriteRepositories.contains(repository) {并将其替换为:
if viewStore.favoriteRepositories.contains(repository) { -
由于你改变了
RepositoryView的属性,这也改变了它的初始化器。在RepositoryListView和FavoritesListView中,找到。RepositoryView(repository: repository, favoriteRepositories: [] )并将其替换为:
RepositoryView(store: store, repository: repository)接下来,更新
RepositoryListView_Previews。用以下代码替换其previews属性的内容。RepositoryListView( store: Store( initialState: RepositoryState(), reducer: repositoryReducer, environment: RepositoryEnvironment()))然后,删除
dummyRepo声明。这将创建一个新的
Store,它有一个初始的RepositoryState,repositoryReducer和一个空的RepositoryEnvironment。别担心,你会在后面处理还原器和环境的问题。
向仓库发送动作
存储器不仅提供对状态的访问,还接受处理视图上发生的事件的操作。这包括在RepositoryView中点选_Favorite_按钮。替换。
action: { return },
用。
action: { viewStore.send(.favoriteButtonTapped(repository)) },
经过这样的改变,当用户点击这个按钮时,就会向仓库发送favoriteButtonTapped动作,仓库会运行还原器并更新仓库。
你还需要从你的视图中发送一个动作。onAppear。
在RepositoryListView中,为ScrollView添加onAppear修改器。
.onAppear {
viewStore.send(.onAppear)
}
每当列表出现时,它就会向商店发送一个动作,触发存储库数据的刷新。
处理副作用
还原器根据动作来转换当前状态。但很少有一个应用程序只由用户可以采取的内部行动组成。因此,需要有一些方法来访问外部世界,例如,执行API请求。
TCA用于异步调用的机制是Effect。但这些效果涵盖的范围不只是异步调用。它们还将所有非确定性的方法调用包裹在其中。例如,这也包括获取当前日期或初始化一个新的UUID。
你可以把Effect看作是一个围绕Combine publisher的包装器,并有一些额外的帮助方法。
打开 RepositoryEffects.swift 。在这里你可以找到两个准备好供你使用的效果。repositoryEffect(decoder:)调用GitHub的API。然后它映射错误和数据,用eraseToEffect将结果转化为一个效果。
另一个效果是dummyRepositoryEffect(解码器:)。它提供了三个假仓库,供开发时或SwiftUI预览时使用。
用环境来管理依赖关系
但还原器如何使用这些效果呢?除了状态和动作,还原器还可以访问一个环境。这个环境持有应用程序以效果形式存在的所有依赖关系。
回到 RepositoryFeature.swift 。将这些依赖添加到RepositoryEnvironment。
// 1
var repositoryRequest: (JSONDecoder) -> Effect<[RepositoryModel], APIError>
// 2
var mainQueue: () -> AnySchedulerOf<DispatchQueue>
// 3
var decoder: () -> JSONDecoder
下面是正在发生的事情。
- 访问GitHub的API是该仓库功能对外部世界的唯一依赖。这个属性是一个闭包,它被传递给一个
JSONDecoder并产生一个Effect。这个效果最终提供了一个RepositoryModel'的列表或一个APIError',以防请求失败。 mainQueue提供对主线程的访问。decoder提供对JSONDecoder实例的访问。
你可以为开发、测试和生产创建不同的环境。你将在下一步添加一个用于预览版本库的功能。为此,在decoder下面的RepositoryEnvironment内添加以下代码。
static let dev = RepositoryEnvironment(
repositoryRequest: dummyRepositoryEffect,
mainQueue: { .main },
decoder: { JSONDecoder() })
这将创建一个新的环境dev。它使用了dummyRepositoryEffect,提供了虚拟数据以及.main调度器和一个默认的JSONDecoder。
要在位于 RepositoryView.swift 的RepositoryListView_Previews中使用这个环境,请找到。
environment: RepositoryEnvironment()))
并将其替换为
environment: .dev))
构建并运行该项目,以确保所有内容仍然可以编译。
到目前为止,你已经做了很多工作,但迎接你的仍然是一个空白的屏幕! 你创建了一个环境,提供了下载软件库的途径。你还声明了所有可以在资源库屏幕上发生的动作,并创建了一个被你所有视图使用的存储空间。
但缺少一个关键的东西:一个还原器。没有任何东西将你的环境与存储库连接起来,所以你还没有下载任何东西。接下来,你会处理这个问题。
用减速器转换状态
Reducer是一个结构,包含一个函数的签名。
(inout State, Action, Environment) -> Effect<Action, Never>
这意味着,一个还原器有三个参数,代表它的操作。
- 状态
- 行动
- 环境
状态是一个inout参数,因为它是由还原器根据给定的动作来修改的。还原器使用环境来访问它所包含的依赖关系。
返回类型意味着还原器可以产生一个下一步处理的效果。当没有进一步的效果需要被执行时,还原器会返回Effect.none。
打开 RepositoryFeature.swift ,其中已经包含了一个返回空效果的空还原器。用下面的代码替换它。
// 1
let repositoryReducer = Reducer<
RepositoryState,
RepositoryAction,
RepositoryEnvironment>
{ state, action, environment in
switch action {
// 2
case .onAppear:
return environment.repositoryRequest(environment.decoder())
.receive(on: environment.mainQueue())
.catchToEffect()
.map(RepositoryAction.dataLoaded)
// 3
case .dataLoaded(let result):
switch result {
case .success(let repositories):
state.repositories = repositories
case .failure(let error):
break
}
return .none
// 4
case .favoriteButtonTapped(let repository):
if state.favoriteRepositories.contains(repository) {
state.favoriteRepositories.removeAll { $0 == repository }
} else {
state.favoriteRepositories.append(repository)
}
return .none
}
}
下面是正在发生的事情。
repositoryReducer对RepositoryState、RepositoryAction和RepositoryEnvironment发挥作用。你可以在闭包内访问这些。- 还原器的功能取决于给定的动作。在
onAppear的情况下, RepoReporter 执行API请求以加载存储库。为此,还原器使用来自环境的repositoryRequest,产生一个新的效果。但是一个reducer需要返回一个与它可以操作的动作类型相同的效果。因此,你需要将效果的输出映射到RepositoryAction。 - 在
dataLoaded的情况下,reducer提取收到的存储库并更新状态。然后还原器返回.none,因为不需要进一步处理效果。 - 最后一个要处理的动作是
favoriteButtonTapped,它可以切换一个仓库的喜爱状态。如果给定的版本库没有被收藏,它就会被添加到状态的收藏版本库列表中,反之亦然。
现在一切都设置好了,是时候看看版本库功能的运作了。打开 RepositoryView.swift 。
如果不可见,通过点击Xcode右上角的 Adjust Editor Options 并选择 Canvas 来启用SwiftUI的预览。点击 Live Preview 。预览将看起来像这样。
现在版本库的功能已经完成,并且可以独立使用。下一个任务是把它和用户功能结合起来。在一个应用程序中组合独立的功能是 Composable 架构的一个主要优势。]
组成功能
现在是时候将现有的用户功能与你刚刚完成的资源库功能结合起来了。
打开位于 Root 组中的 RootFeature.swift 。它包含与版本库功能相同的结构,但这次是针对整个应用程序。目前,它只使用了用户功能。现在你的任务是把版本库功能也加进去。
与SystemEnvironment共享依赖关系
repositoryReducer使用DispatchQueue和JSONDecoder由RepositoryEnvironment提供。在 UserFeature.swift 中声明的userReducer也使用了DispatchQueue和JSONDecoder。然而,你不想复制它们并多次管理相同的依赖关系。你将探索一种机制,在分离的功能之间共享相同的依赖关系。SystemEnvironment。
从 Shared 组中打开 SystemEnvironment.swift 。它包含一个名为 "SystemEnvironment "的 "结构",持有所有共享的依赖关系。在这种情况下,它有DispatchQueue和JSONDecoder。它也可以包裹一个子环境,如RepositoryEnvironment,包含特定功能的依赖关系。除此之外,两个静态方法,live(environment:)和dev(environment:),可以创建预配置的SystemEnvironment实例,用于实时应用或开发时使用。
现在你已经了解了 "SystemEnvironment",现在是时候使用它了。进入 RepositoryFeature.swift ,从RepositoryEnvironment中删除mainQueue、decoder和dev。这样就只剩下repositoryRequest了,这是仓库特性特有的。
接下来,替换。
let repositoryReducer = Reducer<
RepositoryState,
RepositoryAction,
RepositoryEnvironment>
有了。
let repositoryReducer = Reducer<
RepositoryState,
RepositoryAction,
SystemEnvironment<RepositoryEnvironment>>
这让repositoryReducer使用来自SystemEnvironment的共享依赖。
你只需要做最后一个改动。切换到 RepositoryView.swift 。用以下代码替换RepositoryListView_Previews中的RepositoryListView。
RepositoryListView(
store: Store(
initialState: RepositoryState(),
reducer: repositoryReducer,
environment: .dev(
environment: RepositoryEnvironment(
repositoryRequest: dummyRepositoryEffect))))
以前,你在创建商店时使用RepositoryEnvironment。repositoryReducer现在与SystemEnvironment一起工作。因此,你需要在初始化Store时使用它来代替。
使用dev(environment:)创建一个新的SystemEnvironment,并使用dummyRepositoryEffect传入RepositoryEnvironment,而不是实时效果。
构建该项目,现在它将再次编译,不会有错误。你不会看到任何可见的变化,但你现在在你的版本库功能中使用了系统环境的依赖关系。
结合状态和动作
接下来,是时候将版本库特性的所有状态和动作添加到根状态中。根特性是你的应用程序的主标签栏。你将通过结合应用程序内的两个特性来定义这个特性的状态和动作,以创建一个单一的根特性。
回到 RootFeature.swift 。RootState通过拥有每个功能状态的属性来代表整个应用程序的状态。在userState下面添加资源库状态。
var repositoryState = RepositoryState()
接下来,你要把RepositoryAction添加到RootAction。与RootState类似,RootAction将所有独立功能的动作合并为整个应用程序的一组动作。要包括版本库功能的动作,请将它们添加到userAction(UserAction)的正下方。
case repositoryAction(RepositoryAction)
在你的应用程序中,用户可以在根视图上做两件事。查看版本库和查看用户配置文件,所以你为这些功能分别定义一个动作。
将视图添加到应用程序中
打开 RootView.swift 。RootView是RepoReporter的主视图。
两个带有Color.clear的标签作为版本库特征视图的占位符。将第一个Color.clear替换为。
RepositoryListView(
store: store.scope(
state: \.repositoryState,
action: RootAction.repositoryAction))
这段代码初始化了一个新的RepositoryListView并传入一个存储。scope'将全局存储转为本地存储,所以RepositoryListView'可以关注其本地状态和动作。它不能访问全局的状态或动作。
接下来,将第二个Color.clear替换为。
FavoritesListView(
store: store.scope(
state: \.repositoryState,
action: RootAction.repositoryAction))
这一次,添加FavoritesListView作为一个标签。再次,scope将全局状态和动作转换为局部状态和动作。
组成还原器
最后一步是将repositoryReducer加入rootReducer。切换回 RootFeature.swift 。
但是,一个在本地状态、行动和环境上工作的还原器如何能在更大的、全局的状态、行动和环境上工作?TCA提供了两种方法来做到这一点。
- combine。通过组合许多减速器来创建一个新的减速器。它按照列出的顺序执行每个给定的还原器。
- pullback: 对一个给定的还原器进行转换,使其能够在全局状态、行动和环境中工作。它使用三个方法,你需要把它们传递给
pullback。
combine已经被用来创建rootReducer。因此,你可以在userReducer的结尾括号之后添加repositoryReducer,用逗号分开。
// 1
repositoryReducer.pullback(
// 2
state: \.repositoryState,
// 3
action: /RootAction.repositoryAction,
// 4
environment: { _ in
.live(
environment: RepositoryEnvironment(repositoryRequest: repositoryEffect))
})
这里你可以看到如何使用pullback。虽然只有几行,但有很多事情要做。下面我们来详细看看发生了什么。
pullback将repositoryReducer转换为在RootState、RootAction和RootEnvironment上工作。repositoryReducer在本地的RepositoryState上工作。你使用一个关键路径从全局的`RootState'中插入本地状态。- 3.案例路径使本地的
RepositoryAction可以从全局的RootAction访问。案例路径是TCA自带的,就像钥匙路径一样,但在枚举案例中工作。你可以在Point-Free: Case Paths了解更多关于它们的信息。 - 最后,你要创建一个
repositoryReducer可以使用的环境。你使用SystemEnvironment.live(environment:)来开始使用_SystemEnvironment.swift_中定义的实时环境。它已经提供了mainQueue和decoder。此外,你使用repositoryEffect创建一个新的RepositoryEnvironment实例。然后,你把它嵌入到实时环境中。
构建并运行该应用程序。仓库功能和用户功能一起工作,形成一个应用程序。
测试还原器
TCA的一个目标是可测试性。要测试的主要组件是还原器。因为它们将当前状态转换为一个新的动作,这就是你要写的测试。
你将写两种类型的测试。首先,你要用一个不产生进一步影响的动作来测试减速器。这些测试用给定的动作运行减速器,并将产生的状态与预期结果进行比较。
第二种类型是验证一个产生效果的还原器。这些测试使用一个测试调度器来检查效果的预期结果。
创建一个TestStore
打开 RepoReporterTests.swift ,其中已经包含了一个提供假仓库的效果。
第一步是创建TestStore。在testFavoriteButtonTapped中,添加以下代码。
let store = TestStore(
// 1
initialState: RepositoryState(),
reducer: repositoryReducer,
// 2
environment: SystemEnvironment(
environment: RepositoryEnvironment(repositoryRequest: testRepositoryEffect),
mainQueue: { self.testScheduler.eraseToAnyScheduler() },
decoder: { JSONDecoder() }))
这就是正在发生的事情。
- 你传入你想测试的状态和还原器。
- 创建一个包含测试效果和测试调度器的新环境。
测试没有效果的减速器
一旦商店设置好了,你就可以验证还原器是否处理了favoriteButtonTapped这个动作。要做到这一点,在store的声明下添加以下代码。
guard let testRepo = testRepositories.first else {
fatalError("Error in test setup")
}
store.send(.favoriteButtonTapped(testRepo)) { state in
state.favoriteRepositories.append(testRepo)
}
这就把动作favoriteButtonTapped发送到包含一个假版本库的测试商店。当在测试仓库上调用send时,你需要定义一个闭包。在这个闭包中,你定义了一个新的状态,这个状态需要与测试商店运行还原器后的状态相匹配。
你希望repositoryReducer将testRepo添加到favoriteRepositories中。因此,你需要在最喜欢的存储库列表中提供一个包含这个存储库的存储库。
测试商店将执行repositoryReducer。然后,它将比较产生的状态和执行关闭后的状态。如果它们相同,测试就通过,否则就失败。
通过按 Command-U 来运行测试套件。
你会看到一个绿色的检查,表示测试通过。
用效果测试还原器
接下来,测试动作onAppear。在实时环境下,这个动作会产生一个新的效果来下载存储库。要改变这一点,使用与上面相同的测试存储,并将其添加到testOnAppear。
let store = TestStore(
initialState: RepositoryState(),
reducer: repositoryReducer,
environment: SystemEnvironment(
environment: RepositoryEnvironment(repositoryRequest: testRepositoryEffect),
mainQueue: { self.testScheduler.eraseToAnyScheduler() },
decoder: { JSONDecoder() }))
这使用了测试效果而不是API调用,提供了一个假的存储库。
在store的声明下面,添加以下代码。
store.send(.onAppear)
testScheduler.advance()
store.receive(.dataLoaded(.success(testRepositories))) { state in
state.repositories = self.testRepositories
}
在这里你把onAppear发送到测试商店。这产生了一个新的效果,testScheduler需要处理它。这就是为什么你在调度器上调用advance,以便它可以执行这个效果。
最后,receive验证了发送到商店的下一个动作。在本例中,下一个动作是dataLoaded,由onAppear创建的效果触发。
运行测试。同样,你会看到一个绿色检查,证明对onAppear的处理是正确的。
理解失败的测试
Composable Architecture提供了一个有用的工具来理解失败的测试。
在testFavoriteButtonTapped中,在send的结束处,删除。
state.favoriteRepositories.append(testRepo)
重新运行测试,它失败了,正如预期。
点击红色的失败指示器,检查错误信息。
因为你从send中删除了预期的状态,结果的状态与预期的空状态不一致。相反,实际结果包含一个存储库。
这样一来,TCA 可以帮助你在第一时间了解预期状态和实际状态的区别。
何去何从?
你可以使用本教程顶部或底部的 Download Materials 按钮下载项目的完成版本。
TCA框架背后的思想与有限状态机(FSM)或有限状态自动机(FSA)和状态设计模式非常接近。尽管可组合架构是作为一个现成的框架出现的,你可以在不需要学习许多底层机制的情况下对其进行调整,但以下材料可以帮助你理解并将所有的概念放在一起。
- 这里是对有限状态机的一个很好的总体概述。
- 基于状态的设计模式是四人帮所记录的23种设计模式之一。 你可以在状态模式的网页上找到简要介绍。
- 阅读状态设计模式与状态机可以看到一些实际的方面和区别。
尽管FSM/FSA是一个数学计算模型,但每当你的代码中有switch语句或大量的if语句时,都是一个使用它来简化代码的好机会。你可以使用现成的框架,如TCA,也可以使用你自己的代码,如果你了解关键的基本原则,这其实并不难。
你可以在GitHub页面上了解更多关于该框架的信息。TCA还配有详细的文档,包含对每一种类型和使用的方法的解释和例子。
了解更多关于TCA和函数式编程的另一个重要资源是Point-Free网站。它的视频详细介绍了该框架是如何创建的以及它是如何工作的。其他视频介绍了案例研究和用TCA构建的应用实例。
可组合架构与Redux共享许多想法,Redux是一个主要用于React的JavaScript库。要了解更多关于Redux的信息以及如何在没有额外框架的情况下实现它,请查看Getting a Redux Vibe Into SwiftUI。
我们希望你喜欢这个教程。如果你有任何问题或意见,请加入下面的论坛讨论!