构建一个Foursquare克隆iOS应用—第6部分:状态管理

  • 第1部分:简介和设置
  • 第2部分:位置数据和管理依赖项
  • 第三部分:持续集成
  • 第4部分:流媒体位置
  • 第5部分:网络层
  • 第六部分:国家管理

到目前为止,我们已经为我们的应用程序创建了可靠的数据源,现在我们将致力于在数据和视图之间建立通信。

Apple官方文档提出的MVC架构对于新开发人员来说是最直观的。 这种架构足以满足简单的应用程序和小型团队的需要,但是遵循MVC的趋势是将所有代码集中到UIViewController类中,这会使您的应用程序难以理解并且容易出现错误。

首先,我想说明一下这是一个非常主观的概念,受以下因素的影响:功能的复杂性,团队规模,质量的定义和限制,组件的可重用性水平,开发人员的技术技能以及开发人员具有该架构的经验。

我个人在定义应用程序体系结构时优先考虑了3个基本原则:

  • 组件责任:项目包含预定义的角色,避免了繁琐的类,并且添加新功能相对容易。
  • 简便性:新开发人员第一次阅读代码时,应该能够轻松理解基本原理。 它还应该能够理解定义的模式,并在短时间内开始开发新功能或修复错误。
  • 可测试性:顾名思义,代码应该更易于进行单元测试。

我在这里提出的解决方案不应被视为灵丹妙药,但它绝对比普通的MVC更好,而且相对容易学习。

通过选择使用ReSwift,我获得了体系结构以及数据流模式。

ReSwift的组件包括:

  • Store:一个全局变量,代表您应用程序的当前状态。 在我们的情况下,它应该包含一个位置数组和数据获取状态(加载,完成或错误)。
  • 视图:负责观察状态更改并基于新状态显示正确的UI。 在我们的例子中,它将是主要的UIViewController
  • 行动: structs ,包含状态更改所需的有效载荷数据。 示例:一个名为SetPlacesAction的结构, SetPlacesAction包含一个公共位置数组。
  • Reducer:接收两个参数( ActionState )并返回新State 。 所有状态更改应集中在减速器上。

下面的代码定义了“状态”:

国家代表

这里的主要结构是FetchedPlacesState ,它必须符合StateType协议。 此结构包含一个枚举,表示应用程序的所有可能的三种状态:

  • 加载:默认状态,表示应用程序正在等待提取所有数据。
  • 失败:如果发生任何错误,我们使用此状态为用户显示一条人类可读的消息。
  • 已完成:包含成功获取位置的数组

Equatable扩展仅对于单元测试是必需的。

Actions结构如下所示:

现在唯一缺少的是Reducer

现在我们已经准备好所有代码,现在可以清楚地看到返回的StateAction和前一个State的函数。

我想在Reducer代码中指出一些细节:

  • 具体来说,在我们的应用程序中,新状态不依赖于先前状态。 因此reduce方法仅使用action参数。
  • 当您调用Observable方法时,它在ObservableDisposablesubscribe方法的返回类型)之间创建了一个循环内存引用。 为了打破保留周期并避免内存泄漏,您只需要像我已经显示的那样调用disposed(by:)方法即可。 有关在此处处理袋子的更多详细信息。
  • 您可能会注意到在此代码中闻起来不太香的是,在这种情况下, reduce方法不是纯函数。 由于异步网络请求,我们必须通过在请求返回成功或错误时调度新动作来产生副作用。

为了测试状态管理,我决定编写一些Reducer测试以确保正确完成状态转换:

  • 当分派等同于开始获取数据的操作时,应用程序的新状态应为“正在加载”状态。 我们稍后可以使用此信息在状态栏中显示网络指示器。
  • 当调度代表成功获取数据的动作时,新状态应包含相应的位置列表,以便我们可以将其显示给用户(例如:使用UITableView )并隐藏网络指示器。
  • 如果派遣了代表数据请求中错误的操作,则新状态应为“失败”状态。 在这种状态下,我们可能会显示错误消息和“重试”按钮。

转换为代码,如下所示:

状态管理测试

现在,在执行状态管理后,我们拥有了显示在用户界面中所需的所有数据及其转换。 自动化测试与持续集成工具的结合使我们的应用程序更加强大,并且开发人员更加有信心将此代码交付生产。

希望您能学到一些东西,并提高自己成为开发人员的能力!