RxSwift項目實踐

RxSwift-MVVM

這個項目是入坑RxSwift以來的一些收獲,歷經(jīng)多個真實項目的實踐。我也一直在為寫出簡潔易懂的代碼而努力學習和實踐,當中難免有不足之處希望得到社區(qū)開源愛好者的指點,期待在與你的探討中也能獲得一些收獲

項目介紹

  • iOS 界面業(yè)務邏輯

    /*
     ViewController(action)
             ? 
     Reactor(transform(action:))
             ?
     Reactor(mutate(action:)) 
             ?
     Reactor(transform(mutation:))
             ?
     Reactor(reduce(state: State, mutation: Mutation)) 
             ?
     Reactor(transform(state:))
             ?
     ViewController(state)
    */
    
    func bind(reactor: RepoListViewReactor) {
        // Action
        self.rx.viewDidLoad
            .map { Reactor.Action.refresh }
            .bind(to: reactor.action)
            .disposed(by: disposeBag)
        
        // State
        reactor.state.map { $0.repos }
            .filterEmpty()
            .distinctUntilChanged()
            .map { [Section(model: (), items: $0)] }
            .bind(to: tableView.rx.items(dataSource: dataSource))
            .disposed(by: disposeBag)
    }
    
    
  • Utility 通用工具集合

  • MVVMBase 項目基類

  • Networking 網(wǎng)絡層封裝

    /*
     service
        ?
     API(MVVMTargetType)
        ?
     Moya(TargetType)
    */ 
        service
            .repos(username: currentState.name, page: 1)
            .asObservable()
            .map(Mutation.setRepos)
    
  • Namespace 鏈式語法調(diào)用

        tableView
            .mvvm.adhere(toSuperView: view)
            .mvvm.layout(snapKitMaker: { (make) in
                make.edges.equalToSuperview()
            })
    

項目依賴

  • RxSwift/RxCocoa - Reactive Programming in Swift
  • RxOptional - RxSwift extensions for Swift optionals and "Occupiable" types
  • RxDataSources - UITableView and UICollectionView Data Sources for RxSwift (sections, animated updates, editing ...)
  • Moya/RxSwift - Network abstraction layer written in Swift
  • CocoaLumberjack/Swift - A fast & simple, yet powerful & flexible logging framework for Mac and iOS
  • Kingfisher - A lightweight, pure-Swift library for downloading and caching images from the web
  • SnapKit - A Swift Autolayout DSL for iOS & OS X
  • MJRefresh - An easy way to use pull-to-refresh
  • ReactorKit - A framework for a reactive and unidirectional Swift application architecture

ReactorKit is a combination of Flux and Reactive Programming. The user actions and the view states are delivered to each layer via observable streams. These streams are unidirectional: the view can only emit actions and the reactor can only emit states.

image

View

A View displays data. A view controller and a cell are treated as a view. The view binds user inputs to the action stream and binds the view states to each UI component. There's no business logic in a view layer. A view just defines how to map the action stream and the state stream.

To define a view, just have an existing class conform a protocol named View. Then your class will have a property named reactor automatically. This property is typically set outside of the view.

class ProfileViewController: UIViewController, View {
 var disposeBag = DisposeBag()
}

profileViewController.reactor = UserViewReactor() // inject reactor

When the reactor property has changed, bind(reactor:) gets called. Implement this method to define the bindings of an action stream and a state stream.

func bind(reactor: ProfileViewReactor) {
  // action (View -> Reactor)
  refreshButton.rx.tap.map { Reactor.Action.refresh }
    .bind(to: reactor.action)
    .disposed(by: self.disposeBag)

  // state (Reactor -> View)
  reactor.state.map { $0.isFollowing }
    .bind(to: followButton.rx.isSelected)
    .disposed(by: self.disposeBag)
}

Reactor

A Reactor is an UI-independent layer which manages the state of a view. The foremost role of a reactor is to separate control flow from a view. Every view has its corresponding reactor and delegates all logic to its reactor. A reactor has no dependency to a view, so it can be easily tested.

Conform to the Reactor protocol to define a reactor. This protocol requires three types to be defined: Action, Mutation and State. It also requires a property named initialState.

class ProfileViewReactor: Reactor {
  // represent user actions
  enum Action {
    case refreshFollowingStatus(Int)
    case follow(Int)
  }

 // represent state changes
  enum Mutation {
    case setFollowing(Bool)
  }

 // represents the current view state
  struct State {
    var isFollowing: Bool = false
  }

 let initialState: State = State()
}

An Action represents a user interaction and State represents a view state. Mutation is a bridge between Action and State. A reactor converts the action stream to the state stream in two steps: mutate() and reduce().

image
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容