This article is based on the ios-RXSwift project experience. If you haven’t read it yet, it is recommended that you read it first and download the Demo to familiarize yourself with it:)

preface

MVVM mode, the role of ViewModel, logical processing, network requests and other complex operations removed from the ViewController, ViewController is slimmed down. Combined with the RxSwift architecture, we generally define an input in the ViewModel to collect the information needed for complex operations, pass the input as a parameter through a transform method, and then get an output for the controller to use.

We use this form a lot when developing with RxSwift, including our network requests. In conjunction with the “use of MJRefresh in RxSwift” as described in the ios-RXSWIFT project field log, a variable is defined in Output

let refreshStatus = Variable<LXFRefreshStatus> (.none)
Copy the code

The controller listens to it through output, so that when the value changes, the controller can get the current refresh state in real time

vmOutput.refreshStatus.asObservable().subscribe(onNext: {[weak self] status in
    switch status {
    case .beingHeaderRefresh:
        self? .tableView.mj_header.beginRefreshing()case .endHeaderRefresh:
        self? .tableView.mj_header.endRefreshing()case .beingFooterRefresh:
        self? .tableView.mj_footer.beginRefreshing()case .endFooterRefresh:
        self? .tableView.mj_footer.endRefreshing()case .noMoreData:
        self? .tableView.mj_footer.endRefreshingWithNoMoreData()default:
        break
    }
}).addDisposableTo(rx_disposeBag)
Copy the code

If this approach is used in multiple areas of a project, we can see the downsides – repetitive code, too much redundancy.

Do we have to do this every time in controller?

For agreement

Ios-swift Protocol Oriented Programming (1) iOS protocol Oriented Programming (2)

Two main functions of protocol are summarized: 1, specification 2, customization ability

Define the protocol Refreshable

/* ============================ Refreshable ================================ */
// the controller that needs to use MJExtension

protocol Refreshable {}extension Refreshable where Self : UIViewController {
    func initRefreshHeader(_ scrollView: UIScrollView, _ action: @escaping (a) -> Void) - >MJRefreshHeader {
        scrollView.mj_header = MJRefreshNormalHeader(refreshingBlock: { action() })
        return scrollView.mj_header
    }
    
    func initRefreshFooter(_ scrollView: UIScrollView, _ action: @escaping (a) -> Void) - >MJRefreshFooter {
        scrollView.mj_footer = MJRefreshAutoNormalFooter(refreshingBlock: { action() })
        return scrollView.mj_footer
    }
}
Copy the code

Refreshable protocol in controller, give tableView or collectionView header or tail refresh ability through initRefreshHeader method or initRefreshFooter method, And write the code that needs to be executed during the pull-down refresh

For example, pull refresh
let refreshHeader = initRefreshHeader(liveCollectionView) { [weak self] in
    // Operation to be performed after the drop - down
    self? .vmOutput? .requestCommand.onNext(()) }Copy the code

Output. Wherever there is a network request, you need to listen for the request status. In this case, you can define a protocol for Output, OutputRefreshProtocol, that specifies the properties that must be declared

/* ============================ OutputRefreshProtocol ================================ */
// Output is used in viewModel

protocol OutputRefreshProtocol {
    // Tell the external tableView the current refresh state
    var refreshStatus : Variable<LXFRefreshStatus> {get}}Copy the code

Output is then told to follow the protocol and initialized to refresh with a value of.None

struct LXFLiveOutput: OutputRefreshProtocol {
    var refreshStatus: Variable<LXFRefreshStatus>
    
    let sections: Driver<[LXFLiveSection]>
    init(sections: Driver<[LXFLiveSection]>) {
        self.sections = sections
        refreshStatus = Variable<LXFRefreshStatus>(.none)
    }
}
Copy the code

At this point, it’s essentially the same as before, except to make it easier for the Controller to initialize the refresh control. This is the focus of this article.

Focus on

There are only a few states of refresh, pull-down reloads data, pull-up loads more, pull-down or pull-up ends when the request is complete, and so on… So why should we manage such trivia in each controller? The state of the refresh control is determined by the variable refreshStatus, which is declared in the OutputRefreshProtocol. Leave the state of the refresh control to refreshStatus itself

extension OutputRefreshProtocol {
    func autoSetRefreshHeaderStatus(header: MJRefreshHeader? , footer: MJRefreshFooter?) -> Disposable {
        return refreshStatus.asObservable().subscribe(onNext: { (status) in
            switch status {
            case.beingHeaderRefresh: header? .beginRefreshing()case.endHeaderRefresh: header? .endRefreshing()case.beingFooterRefresh: footer? .beginRefreshing()case.endFooterRefresh: footer? .endRefreshing()case.noMoreData: footer? .endRefreshingWithNoMoreData()default:
                break}}}})Copy the code

At this time, we need to pass the object header/footer of the refresh control into the method to realize automatic control of the refresh control state.

Conclusion using

RefreshStatus (none) : output complies with the OutputRefreshProtocol

struct LXFLiveOutput: OutputRefreshProtocol {
    var refreshStatus: Variable<LXFRefreshStatus>
    
    let sections: Driver"[LXFLiveSection] >init(sections: Driver"[LXFLiveSection] >) {self.sections = sections
        refreshStatus = Variable<LXFRefreshStatus> (.none)}}Copy the code

2. Controller complies with the protocol Refreshable, initializes the refresh control and its corresponding operation through the method in the protocol, and passes the refresh control object as a parameter to the automatic processing state method

extension LXFLiveViewController: Refreshable 
Copy the code
let refreshHeader = initRefreshHeader(liveCollectionView) { [weak self] in
    self? .vmOutput? .requestCommand.onNext(()) } vmOutput? .autoSetRefreshHeaderStatus(header: refreshHeader, footer:nil).disposed(by: rx.disposeBag)
Copy the code

Update the refreshStatus of refreshStatus in the viewModel in real time according to the actual situation

Open source library

LXFProtocolTool

Recommended LXFProtocolTool/Refreshable, will be constantly updated, more powerful

pod 'LXFProtocolTool/Refreshable'
Copy the code

case

Protocol: Refreshable. Swift

The ViewModel: LXFLiveViewModel

Controller: LXFLiveViewController

LXFBiliBili