ReSwift
支持的 Swift 版本: Swift 4.2, 5.x
对于 Swift 3.2 或 4.0 支持,请使用 版本 5.0.0 或更早版本。对于 Swift 2.2 支持,请使用 版本 2.0.0 或更早版本。
简介
ReSwift 是一个类似 Redux 的单向数据流架构在 Swift 中的实现。ReSwift 帮助您分离应用程序组件的三个重要方面
- 状态:在 ReSwift 应用程序中,整个应用程序状态被显式地存储在一个数据结构中。这有助于避免复杂的状态管理代码,使调试更好,并且还有更多好处...
- 视图:在 ReSwift 应用程序中,您的视图会在状态改变时更新。您的视图成为当前应用程序状态的简单可视化。
- 状态改变:在 ReSwift 应用程序中,您只能通过操作执行状态更改。操作是描述状态更改的小数据块。通过极大地限制状态可以被突变的方式,您会发现应用程序更容易理解,并且更容易与许多合作者一起工作。
ReSwift 库很小 - 允许用户深入代码,理解每一行,并 希望做出贡献。
ReSwift 迅速超越了核心库,提供对路由和时间旅行(通过过去的应用程序状态)的实验性扩展!
激动吗?我们也一样!
查看我们的 公共 Gitter 聊天室!
目录
关于ReSwift
ReSwift依赖于一些原则
- Store 以单一数据结构的形式存储你的整个应用状态。这个状态只能通过向Store分派Actions来修改。每当Store中的状态发生变化时,Store将通知所有观察者。
- Actions 是描述状态变化的一种声明式方式。Actions不包含任何代码,它们被Store消费并转发给reducers。Reducers将通过为每个action实现不同的状态改变来处理操作。
- Reducers 提供了纯函数,这些函数基于当前操作和当前应用状态,创建新的应用状态
对于一个非常简单的应用程序,它维护一个可以增加和减少的计数器,你可以这样定义应用程序状态:
struct AppState {
var counter: Int = 0
}
你还需要定义两个操作,一个用于增加计数器,一个用于减少计数器。在入门指南中,你可以了解如何构造复杂操作。对于本例中的简单操作,我们可以定义符合action的空结构体
struct CounterActionIncrease: Action {}
struct CounterActionDecrease: Action {}
你的reducer需要响应对这些不同操作类型,这可以通过切换action的类型来实现
func counterReducer(action: Action, state: AppState?) -> AppState {
var state = state ?? AppState()
switch action {
case _ as CounterActionIncrease:
state.counter += 1
case _ as CounterActionDecrease:
state.counter -= 1
default:
break
}
return state
}
为了有一个可预测的应用状态,reducer总是没有副作用很重要,它接收当前的app状态和一个操作,并返回新的app状态。
为了维护我们的状态并将操作委托给reducers,我们需要一个store。我们可以将其称为mainStore
并将其定义为一个全局常量,例如在app代理文件中
let mainStore = Store<AppState>(
reducer: counterReducer,
state: nil
)
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
[...]
}
最后,你的视图层,在这种情况下是一个视图控制器,需要通过订阅store更新并在app状态需要更改时发出操作来将自身捆绑到这个系统中
class CounterViewController: UIViewController, StoreSubscriber {
@IBOutlet var counterLabel: UILabel!
override func viewWillAppear(_ animated: Bool) {
mainStore.subscribe(self)
}
override func viewWillDisappear(_ animated: Bool) {
mainStore.unsubscribe(self)
}
func newState(state: AppState) {
counterLabel.text = "\(state.counter)"
}
@IBAction func increaseButtonTapped(_ sender: UIButton) {
mainStore.dispatch(
CounterActionIncrease()
)
}
@IBAction func decreaseButtonTapped(_ sender: UIButton) {
mainStore.dispatch(
CounterActionDecrease()
)
}
}
newState
方法将在Store调用并有一个可用的新应用状态时被调用,这是我们调整我们的视图以反映最新应用程序状态的地方。
按钮点击将产生分派的操作,这些操作将被Store和它的reducers处理,导致新的应用状态。
这是一个非常基本的示例,它只显示了ReSwift功能的一部分。阅读入门指南以了解您如何使用这种体系结构构建完整的应用程序。有关本例的完整实现,请参阅CounterExample项目。
组合创建多个子状态的订阅
只需创建一个表示订阅者类中所需的数据模型的struct,并在构造函数中接受整个应用状态作为参数。将此构造函数视为从应用状态到订阅者状态的映射器/选择器。《MySubState》是一个struct并且符合《Equatable》,ReSwift默认情况下将不会在计算输出没有发生变化时通知订阅者。此外,Swift能够推断出订阅的类型。
struct MySubState: Equatable {
// Combined substate derived from the app state.
init(state: AppState) {
// Compute here the substate needed.
}
}
store.subscribe(self) { $0.select(MySubState.init) }
func newState(state: MySubState) {
// Profit!
}
为什么选择ReSwift?
模型-视图-控制器(MVC)不是一个全面的应用架构。典型的Cocoa应用会将大量复杂性推迟到控制器,因为MVC没有提供其他状态管理的解决方案,这是应用开发中最复杂的问题之一。
建立于MVC之上的应用通常会陷入大量围绕状态管理和传播的复杂性。我们需要使用回调、委托、键值观察和通知在我们应用程序中传递信息,并确保所有相关视图都有最新的状态。
这种方法涉及很多手动步骤,因此容易出错,并且不能很好地适应复杂的代码库。
这也导致代码难以一目了然,因为依赖项可能隐藏在视图控制器内部深处。最后,你通常得到的是不一致的代码,每个开发者使用他们个人偏好的状态传播程序。你可以通过风格指南和代码审查来解决这个问题,但你不能自动验证这些指南的遵守情况。
ReSwift试图通过限制应用编写方式来解决这个问题。这减少了程序员的错误空间,并导致易于理解的应用程序 - 通过检查应用程序状态数据结构、动作和reducer。
这种架构除了改进你的代码库外还提供其他好处
- 存储、reducer、动作以及像ReSwift Router这样的扩展完全依赖于平台 - 你可以轻松地使用相同的服务器逻辑,并在不同的平台(iOS、tvOS等)之间共享。
- 想要与同事一起修复应用崩溃吗?请使用ReSwift Recorder记录导致崩溃的动作并向他们发送JSON文件,这样他们就可以立即重新播放动作并再现问题。
- 也许记录的动作可以用来构建UI和集成测试?
ReSwift的工具有一个非常基础的起点,但上述前景让我和其他社区成员都感到兴奋!
入门指南
在这里有一个描述用ReSwift构建的应用核心组件的入门指南.
要理解核心原则,我们推荐阅读出色的redux文档。
安装
CocoaPods
您可以通过将以下内容添加到您的Podfile
中来使用CocoaPods安装ReSwift:CocoaPods
use_frameworks!
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
pod 'ReSwift'
然后运行pod install
。
Carthage
您可以通过在您的Cartfile
中添加以下行来通过Carthage安装ReSwift:
github "ReSwift/ReSwift"
Accio
您可以通过将以下行添加到您的Package.swift
中来使用Accio安装ReSwift:Accio
.package(url: "https://github.com/ReSwift/ReSwift.git", .upToNextMajor(from: "5.0.0")),
接下来,像这样将ReSwift
添加到您的应用目标的依赖项中:
.target(
name: "App",
dependencies: [
"ReSwift",
]
),
然后运行accio update
。
Swift Package Manager
您可以通过在您的Package.swift
中添加以下行来使用Swift Package Manager安装ReSwift:Swift Package Manager
import PackageDescription
let package = Package(
[...]
dependencies: [
.package(url: "https://github.com/ReSwift/ReSwift.git", from: "5.0.0"),
]
)
检出源代码
检出项目后运行 pod install
以获取最新支持的版本 SwiftLint,我们用它来确保代码库风格的一致性。
示例
使用此库可以实现具有明确、可复现状态的应用程序,让您可以回放和重播应用程序状态,如下所示
扩展
此存储库包含ReSwift的核心组件,以下扩展可用
- ReSwift-Thunk:提供了一个ReSwift中间件,允许您派发thunks(动作创建者),以封装API回调等过程。
- ReSwift-Router:提供了一个与ReSwift兼容的Router,允许在iOS应用程序中声明性地进行路由。
- ReSwift-Recorder:提供一个
Store
实现,它记录了所有Action
,并允许进行热重载和时间旅行。
示例项目
- CounterExample:使用ReSwift实现的非常简单的计数器应用程序。
- CounterExample-Navigation-TimeTravel:此示例基于简单的CounterExample应用程序,增加了时间旅行功能(使用ReSwiftRecorder)和路由功能(使用ReSwiftRouter)。
- GitHubBrowserExample:一个真实世界的示例,包括身份验证、网络请求和导航。仍在进行中,但应该是开始将
ReSwift
适配到自己的应用程序的最佳资源。 - ReduxMovieDB:一个简单的应用程序,它查询tmdb.org API来显示最新的电影。允许进行搜索和查看详情。
- Meet:使用ReSwift开发的实际应用,目前处于非常早期的阶段。它没有更新到ReSwift的最新版本,但是最能够展示时间旅行特性的项目。
- Redux-Twitter:使用ReSwift和RxSwift构建的Twitter基本搜索实现,涉及Twitter认证、网络请求和导航。
开源代码的正式应用
- Persephone,MacOS上的一款MPD音乐播放器守护进程控制器
- Product Hunt for OS X,macOS官方的Product Hunt客户端
贡献
这里还有很多工作要做!我们非常希望您能参与进来!您可以在贡献指南中找到如何开始的全部细节。
鸣谢
- 非常感谢Dan Abramov开发了Redux - 这里所有的想法以及许多实现细节都由他的库提供。
联系我们
如果您有任何问题,您可以在推特上找到核心团队
我们还有一个公共gitter聊天室!