🦆 RxDucks
RxDucks 是什么?
RxDucks 是一个基于 RxSwift 类似 Redux 的框架。有多种 Redux 框架,这是一个专门为 RxSwift 定制的框架。
Redux 是现代应用架构之一。有关详细信息,请参考以下链接。
要求
- Swift 5.0
- RxSwift 4.4 或更高版本
如何安装
CocoaPods
将以下内容添加到您的 Podfile
中:
pod "RxDucks"
Carthage
将以下内容添加到您的 Cartfile
中:
github "cats-oss/RxDucks"
如何使用RxDucks
所需的最小元素包括 State
、Action
、Reducer
和 Store
。
状态
对于 State
,准备应用程序状态所需的属性。例如,当需要用户可以增加或减少的计数器时,应创建计数属性。
它不必一定是不可变的。
struct AppState: State {
var counter: Int = 0
var user = UserState()
}
struct UserState: State {
var loggedIn = false
}
动作
准备增加和减少的动作。
struct IncreaseAction: Action {}
struct DecreaseAction: Action {}
struct LogInAction: Action {}
无论它是一个结构还是不是,只要它符合 Action
协议。
enum CounterAction: Action {
case increase, decrease
}
如果不想通知状态到存储,请使用 IgnorableAction
。
struct ResetAction: IgnorableAction {}
Reducer
必须准备符合 Reducer 协议的 Reducer。
struct AppReducer: Reducer {
func reduce(_ state: AppState, action: Action) -> AppState {
var state = state
switch action {
case is IncreaseAction:
state.counter += 1
case is DecreaseAction:
state.counter -= 1
case is ResetAction:
state.counter = 0
case is LogInAction:
state.user.loggedIn = true
default:
break
}
return state
}
}
可能需要在主 Reducer 中调用 Reducer。在这种情况下,没有必要符合 Reducer 协议。
struct CounterReduder {
static func reduce(_ state: Int, action: Action) -> Int {
switch action {
case is IncreaseAction:
return state + 1
case is DecreaseAction:
return state - 1
case is ResetAction:
return 0
default:
return state
}
}
}
struct UserReduder {
static func reduce(_ state: UserState, action: Action) -> UserState {
switch action {
case is LogInAction:
return UserState(loggedIn: true)
default:
return state
}
}
}
struct AppReducer: Reducer {
func reduce(_ state: AppState, action: Action) -> AppState {
return AppState(counter: CounterReduder.reduce(state.counter, action: action),
user: UserReduder.reduce(state.user, action: action))
}
}
存储
使用初始状态和主 Reducer
的实例进行初始化。
let store = Store(reducer: AppReducer(), state: AppState())
也可以制作共享实例。
extension Store where State == AppState {
static let shared = Store(reducer: AppReducer(), state: AppState())
}
订阅状态
它可以通过订阅状态改变。代码块 state
订阅所有状态变化。代码块 specifyState
订阅特定的状态变化。然后,当 Store
订阅了 state
和 specifyState
时,它观察当前状态,但当订阅 newState
和 specifyNewState
时,它不观察当前状态。
class ViewController: UIViewController {
let disposeBag = DisposeBag()
let store = Store(reducer: AppReducer(), state: AppState())
@IBOutlet weak var statusLabel: UILabel!
@IBOutlet weak var counterLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
store.state
.subscribe(onNext: {
print($0)
})
.disposed(by: disposeBag)
store.specifyNewState { $0.user.loggedIn }
.map { $0 ? "Log In" : "Log Out" }
.bind(to: statusLabel.rx.text)
.disposed(by: disposeBag)
store.specifyState { $0.counter }
.map { "\($0)" }
.bind(to: counterLabel.rx.text)
.disposed(by: disposeBag)
}
}
分发一个动作
它可以分发自定义动作以改变状态。
store.dispatch(IncreaseAction())
此外,还可以使用 dispatcher
绑定。
class ViewController: UIViewController {
let disposeBag = DisposeBag()
let store = Store(reducer: AppReducer(), state: AppState())
@IBOutlet weak var increaseButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
increaseButton.rx.tap
.map { IncreaseAction() }
.bind(to: store.dispatcher)
.disposed(by: disposeBag)
}
}
中间件
它与 Reducer 类似,但无法直接修改状态。Middleware
可以分发自定义动作并改变新的动作。
struct LoggingMiddleware: Middleware {
func on(_ store: Store<AppState>, action: Action, next: @escaping (Action) -> Void) -> Disposable {
print(action)
next(action)
return Disposables.create()
}
}
返回 Disposable
的原因是为了方便异步处理。它必须执行 next
关闭。所以如果遇到错误,也应该执行它。
struct LoginMiddleware: Middleware {
func on(_ store: Store<AppState>, action: Action, next: @escaping (Action) -> Void) -> Disposable {
switch action {
case is LogInAction:
store.dispatch(LoadingAction())
let request = URLRequest(url: URL(string: "YOUR_URL")!)
return URLSession.shared.rx.data(request: request)
.subscribe(onNext: {
next(LoadedAction(data: $0))
}, onError: {
next(LoadErrorAction(error: $0))
})
default:
next(action)
}
return Disposables.create()
}
}
可以创建多个 Middleware
,它们以创建的顺序执行。
let shared = Store(reducer: AppReducer(), state: AppState(), middlewares: LoggingMiddleware(), LoginMiddleware())
许可协议
遵循 MIT 许可协议。详情请参阅 LICENSE 文件。