ReSwift的使用
ReSwift是单向数据流
Redux(数据流动图)
Store
Store
是保存App
State
的对象(还负责dispatch
action
)
let mainStore = Store(reducer: handleAction, state: nil)
state
存放在Store
里的app
的页面的数据和状态
import Foundation
import ReSwift
struct EatojoyState: StateType {
var lists: [EatojoyModel]?
var isInitLoading: Bool
var isRefreshing: Bool
var isLoadingMore: Bool
var listsFetched: Bool
var isDelete: Bool
}
Action
1.用户或者程序触发的event
2.action
可以携带params
和当前state
//
// EatojoyAction.swift
// ReSwiftDemo
//
// Created by jp on 2018/12/27.
// Copyright © 2018 jp. All rights reserved.
//
import Foundation
import ReSwift
import RxCocoa
import RxSwift
enum ListsFetchingActionType {
case initiate, refresh, loadMore
}
enum ListsFetchedActionType {
case refresh, loadMore
}
struct ListsFetchingAction: Action {
let type: ListsFetchingActionType
}
struct ListsFetchedAction: Action {
let lists: [EatojoyModel]
let type: ListsFetchedActionType
}
struct ItemDeleteAction: Action {
let index: Int
}
let disposeBag = DisposeBag()
func getLists(isLoadMore: Bool, isInitial: Bool = false) {
if isInitial {
mainStore.dispatch(ListsFetchingAction(type: .initiate))
} else {
if isLoadMore {
mainStore.dispatch(ListsFetchingAction(type: .loadMore))
} else {
mainStore.dispatch(ListsFetchingAction(type: .refresh))
}
}
//
let urlString = ""
let url = URL(string: urlString)
let request = URLRequest(url: url!)
URLSession.shared.rx.json(request: request).observeOn(MainScheduler.instance).subscribe(onNext: { json in
print(json)
if let json = json as? [String: Any], let sourceData = json["data"] as? [String: Any], let lists = sourceData["list"] as? [[String: Any]] {
let models = lists.map({ item -> EatojoyModel in
var initialModel = EatojoyModel(name: "", vendorName: "")
initialModel.name = item["name"] as! String
initialModel.vendorName = item["vendor_name"] as! String
return initialModel
})
mainStore.dispatch(ListsFetchedAction(lists: models, type: isLoadMore ? .loadMore : .refresh))
}
}).disposed(by: disposeBag)
}
Reducer
1.Reducer
是一个纯函数
2.接受一个action
和当前的state
返回一个新的state
//
// EatojoyReducer.swift
// ReSwiftDemo
//
// Created by jp on 2018/12/27.
// Copyright © 2018 jp. All rights reserved.
//
import Foundation
import ReSwift
func handleAction(_ action: Action, _ state: EatojoyState?) -> EatojoyState {
return EatojoyState(lists: listsReducer(state: state?.lists, action: action),
isInitLoading: isInitLoadingReducer(state: state?.isInitLoading, action: action),
isRefreshing: isRefreshingReducer(state: state?.isRefreshing, action: action),
isLoadingMore: isLoadingMoreReducer(state: state?.isLoadingMore, action: action),
listsFetched: listsFetchedReducer(state: state?.listsFetched, action: action),
isDelete: isDeleteItemReducer(state: state?.isDelete, action: action))
}
func listsReducer(state: [EatojoyModel]?, action: Action) -> [EatojoyModel]? {
if let action = action as? ListsFetchedAction {
if action.type == .refresh {
return action.lists
} else {
var initialLists = state ?? [EatojoyModel]()
initialLists += action.lists
return initialLists
}
}
if let action = action as? ItemDeleteAction, var state = state {
state.remove(at: action.index)
return state
}
return state
}
func isInitLoadingReducer(state: Bool?, action: Action) -> Bool {
if let action = action as? ListsFetchingAction {
if action.type == .initiate {
return true
}
}
return false
}
func isRefreshingReducer(state: Bool?, action: Action) -> Bool {
if let action = action as? ListsFetchingAction {
if action.type == .refresh {
return true
}
}
return false
}
func isLoadingMoreReducer(state: Bool?, action: Action) -> Bool {
if let action = action as? ListsFetchingAction {
if action.type == .loadMore {
return true
}
}
return false
}
func listsFetchedReducer(state: Bool?, action: Action) -> Bool {
return action is ListsFetchedAction
}
func isDeleteItemReducer(state: Bool?, action: Action) -> Bool {
return action is ItemDeleteAction
}
view层监听数据的变化
//
// ViewController.swift
// ReSwiftDemo
//
// Created by jp on 2018/12/27.
// Copyright © 2018 jp. All rights reserved.
//
import UIKit
import ReSwift
import MJRefresh
import RxCocoa
import RxSwift
class ViewController: UIViewController, StoreSubscriber {
var foodstate: EatojoyState?
@IBOutlet weak var tableView: UITableView!
@IBOutlet weak var indicatorView: UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad()
getLists(isLoadMore: false, isInitial: true)
tableView.rowHeight = 100
tableView.mj_header = MJRefreshNormalHeader(refreshingBlock: {
getLists(isLoadMore: false, isInitial: false)
})
tableView.mj_footer = MJRefreshBackNormalFooter(refreshingBlock: {
getLists(isLoadMore: true, isInitial: false)
})
}
override func viewWillAppear(_ animated: Bool) {
mainStore.subscribe(self)
}
override func viewWillDisappear(_ animated: Bool) {
mainStore.unsubscribe(self)
}
func newState(state: EatojoyState) {
foodstate = state
if state.isInitLoading {
indicatorView.isHidden = false
indicatorView.startAnimating()
} else {
indicatorView.stopAnimating()
indicatorView.isHidden = true
}
if state.listsFetched {
tableView.mj_header.endRefreshing()
tableView.mj_footer.endRefreshing()
tableView.reloadData()
}
if state.isDelete {
tableView.reloadData()
}
}
}
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return foodstate?.lists?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as? TableViewCell {
if let model = foodstate?.lists?[indexPath.row] {
cell.updateCell(model: model)
}
// cell.deleteBtn.rx.tap.subscribe(onNext: { _ in
// mainStore.dispatch(ItemDeleteAction(index: indexPath.row))
// }).disposed(by: disposeBag)
cell.deleteBtn.rx.tap.asDriver().drive(onNext: { _ in
mainStore.dispatch(ItemDeleteAction(index: indexPath.row))
}).disposed(by: cell.bag)
return cell
}
return UITableViewCell()
}
}
总结
ReSwift有哪些优势
- 很强的约束力:把一些代码放在不合适的地方往往具有很强的诱惑性,虽然这样写很方便。ReSwift 通过很强的约束力来避免这种情况。
- 单向数据流:多向数据流的代码在阅读和debug上都可能变成一场灾难。一个改变可能会带来一系列的连锁反应。而单向数据流就能让程序的运行更加具有可预测性,也能够减少阅读这些代码的痛苦。
- 容易测试:大多数的业务逻辑都在Reducer 中,这些都是纯的功能。
- 父用性:ReSwift 中的每个组件—Store、Reducer、Action ,都是能在各个平台独立运行的,可以很轻松的在iOS、macOS、或者tvOS 中复用这些模块。