FluxCapacitor 0.11.0

FluxCapacitor 0.11.0

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

Taiki Suzuki 维护。



Logo

FluxCapacitor

Build Status Build Status Version License Platform Carthage compatible

FluxCapacitor 使用协议和 typealias 使实现 Flux 设计模式更加方便。

  • 可存储协议
  • 可操作协议
  • 分发状态协议

要求

  • Xcode 10.1 或更高版本
  • Swift 4.2 或更高版本
  • iOS 10.0 或更高版本

安装

CocoaPods

FluxCapacitor 通过 CocoaPods 获得。要安装它,只需在您的 Podfile 中添加以下行:

pod "FluxCapacitor"

Carthage

如果您使用的是 Carthage,只需将 FluxCapacitor 添加到您的 Cartfile

github "marty-suzuki/FluxCapacitor"

使用

这是一个使用 Flux 设计模式的 ViewController 示例。如果 ViewController 调用 RepositoryAction 的 fetchRepositories 方法,则在从 Github 获取仓库后,将自动通过观察 RepositoryStore 中的 Constant 的变化进行重新加载。通过 FluxCapacitor 介绍如何实现 Flux 设计模式。

final class UserRepositoryViewController: UIViewController {
    @IBOutlet weak var tableView: UITableView!

    private let repositoryAction = RepositoryAction()
    private let repositoryStore = RepositoryStore.instantiate()
    private let userStore = UserStore.instantiate()
    private let dustBuster = DustBuster()
    private let dataSource = UserRepositoryViewDataSource()

    override func viewDidLoad() {
        super.viewDidLoad()

        dataSource.configure(with: tableView)
        observeStore()

        if let user = userStore.selectedUser.value {
            repositoryAction.fetchRepositories(withUserId: user.id, after: nil)
        }
    }

    private func observeStore() {
        repositoryStore.repositories
            .observe(on: .main, changes: { [weak self] _ in
                self?.tableView.reloadData()
            })
            .cleaned(by: dustBuster)
    }
}

分发器

首先,实现 DispatchState。它连接了 Action 和 Store,但它扮演着一个不直接依赖于彼此的角色。

extension Dispatcher {
    enum Repository: DispatchState {
        typealias RelatedStoreType = RepositoryStore
        typealias RelatedActionType = RepositoryAction

        case isRepositoryFetching(Bool)
        case addRepositories([GithubApiSession.Repository])
        case removeAllRepositories
    }
}

存储器

使用 Storable 协议实现 Store。当分发器分发表达状态时,会调用 func reduce(with:_)。请使用关联值更新存储器的值。

final class RepositoryStore: Storable {
    typealias DispatchStateType = Dispatcher.Repository

    let isRepositoryFetching: Constant<Bool>
    private let _isRepositoryFetching = Variable<Bool>(false)

    let repositories: Constant<[Repository]>
    private let _repositories = Variable<[Repository]>([])

    required init() {
        self.isRepositoryFetching = Constant(_isRepositoryFetching)
        self.repositories = Constant(_repositories)
    }

    func reduce(with state: Dispatcher.Repository) {
        switch state {
        case .isRepositoryFetching(let value):
            _isRepositoryFetching.value = value
        case .addRepositories(let value):
            _repositories.value.append(contentsOf: value)
        case .removeAllRepositories:
            _repositories.value.removeAll()
        }
    }
}

如果您想使用存储器,请使用 XXXStore.instantiate()。这个静态方法会返回其引用或新实例。如果您想从分发器中注销任何存储器,请调用 xxxStore.clear()

行为

使用 Actionable 协议实现 Action。如果调用 invoke 方法,它可以分发表达状态相关值。

final class RepositoryAction: Actionable {
    typealias DispatchStateType = Dispatcher.Repository

    private let session: ApiSession

    init(session: ApiSession = .shared) {
        self.session = session
    }

    func fetchRepositories(withUserId id: String, after: String?) {
        invoke(.isRepositoryFetching(true))
        let request = UserNodeRequest(id: id, after: after)
        _ = session.send(request) { [weak self] in
            switch $0 {
            case .success(let value):
                self?.invoke(.addRepositories(value.nodes))
            case .failure:
                break
            }
            self?.invoke(.isRepositoryFetching(false))
        }
    }
}

使用 Constant<Element> / Variable<Element> 来观察变化

您可以使用 instantiate() 初始化存储库。如果存储库的引用保留,该方法将返回保留的引用。如果引用未保留,则返回新的实例。您可以常量或变量来观察变化。当调用 observe 时,它会返回 Dust。因此,使用 DustBuster 进行清理。

let dustBuster = DustBuster()

func observeStore() {
    // Get store instance
    let store = RepositoryStore.instantiate()

    // Observer changes of repositories that is `Constant<[Github.Repository]>`.
    store.repositories
        .observe(on: .main) { value in
            // do something
        }
        .cleaned(by: dustBuster)
}

dustbuster罗伯特·泽米吉斯(1989)回到未来 Part II,环球影业

Constant<Element>Variable<Element>

Variable<Element> 有 Element 的获取器和设置器。

let intVal = Variable<Int>(0)
intVal.value = 1
print(intVal.value) // 1

Constant<Element> 只有 Element 的获取器。因此,您可以将变量初始化为常量。变量与常量共享其值。

let variable = Variable<Int>(0)
let constant = Constant(variable)
variable.value = 1
print(variable.value) // 1
print(constant.value) // 1

此外,使用某些变量初始化的常量可以使用相同的观察。

let variable = Variable<Int>(0)
let constant = Constant(variable)

_ = variable.observe { value in
    print(value) // 0 -> 10
}

_ = constant.observe { value in
    print(value) // 0 -> 10
}

variable.value = 10

与 RxSwift 一起使用

您可以使用如 此链接 所示的 FluxCapacitor 与 RxSwift。

或者像这样实现 func asObservable()

// Constant
extension PrimitiveValue where Trait == ImmutableTrait {
    func asObservable() -> Observable<Element> {
        return Observable.create { [weak self] observer in
            let dust = self?.observe { observer.onNext($0) }
            return Disposables.create { dust?.clean() }
        }
    }
}

示例

要运行示例项目,请克隆存储库,然后首先从示例目录中运行 pod installcarthage update。另外,您必须设置 GitHub 个人访问令牌。

// ApiSessionType.swift
extension ApiSession: ApiSessionType {
    static let shared: ApiSession = {
        let token = "" // Your Personal Access Token
        return ApiSession(injectToken: { InjectableToken(token: token) })
    }()
}

应用程序结构如下。

flux_image

GithubKitForSample 在此示例项目中使用。

额外内容

Flux + MVVM 示例在此这里

迁移指南

FluxCapacitor 0.10.0 迁移指南

作者

marty-suzuki, [email protected]

许可证

FluxCapacitor 在 MIT 许可证下可用。更多信息请参阅 LICENSE 文件。