ReSwift 6.1.1

ReSwift 6.1.1

测试已测试
语言语言 SwiftSwift
许可证 MIT
发布最后发布2023年1月
SPM支持 SPM

Karl BowdenBenjamin EnczMalcolm JarvisChristian Tietze 维护。



ReSwift 6.1.1

  • Benjamin Encz、Karl Bowden、Malcolm Jarvis 和 Christian Tietze 撰写

ReSwift

Build Status Code coverage status CocoaPods Compatible Platform support License MIT Reviewed by Hound

支持的 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背后的动机的演讲。.

入门指南

在这里有一个描述用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认证、网络请求和导航。

开源代码的正式应用

贡献

这里还有很多工作要做!我们非常希望您能参与进来!您可以在贡献指南中找到如何开始的全部细节。

鸣谢

  • 非常感谢Dan Abramov开发了Redux - 这里所有的想法以及许多实现细节都由他的库提供。

联系我们

如果您有任何问题,您可以在推特上找到核心团队

我们还有一个公共gitter聊天室!