FluxCapacitor
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)
}
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 install
和 carthage update
。另外,您必须设置 GitHub 个人访问令牌。
// ApiSessionType.swift
extension ApiSession: ApiSessionType {
static let shared: ApiSession = {
let token = "" // Your Personal Access Token
return ApiSession(injectToken: { InjectableToken(token: token) })
}()
}
应用程序结构如下。
- SearchViewController(使用 RxSwift)您可以在 GitHub 上搜索用户。
- FavoriteViewController 你可以在内存中存储喜欢的项目。
- UserRepositoryViewController 你可以显示用户的仓库。
- RepositoryViewController 你可以显示仓库的网页,并在内存中添加喜欢的项目。
GithubKitForSample 在此示例项目中使用。
额外内容
Flux + MVVM 示例在此这里。
迁移指南
作者
marty-suzuki, [email protected]
许可证
FluxCapacitor 在 MIT 许可证下可用。更多信息请参阅 LICENSE 文件。