RxDucks 0.2.0

RxDucks 0.2.0

Kyohei Ito 维护。



 
依托于
RxSwift~> 4.4
RxCocoa~> 4.4
 

RxDucks 0.2.0

  • Kyohei Ito

🦆RxDucks

Carthage compatible Build Status codecov Version License Platform

RxDucks 是什么?

RxDucks 是一个基于 RxSwift 类似 Redux 的框架。有多种 Redux 框架,这是一个专门为 RxSwift 定制的框架。

Redux 是现代应用架构之一。有关详细信息,请参考以下链接。

要求

  • Swift 5.0
  • RxSwift 4.4 或更高版本

如何安装

CocoaPods

将以下内容添加到您的 Podfile 中:

pod "RxDucks"

Carthage

将以下内容添加到您的 Cartfile 中:

github "cats-oss/RxDucks"

如何使用RxDucks

所需的最小元素包括 StateActionReducerStore

状态

对于 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 订阅了 statespecifyState 时,它观察当前状态,但当订阅 newStatespecifyNewState 时,它不观察当前状态。

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 文件。