This is the 22nd day of my participation in the Challenge. For details, see:More article challenges

What is the MVVM

What is it for MVVM? There are too many articles and materials. It’s definitely better than mine. It’s beautiful. So forgive me for my unprofessional and unflattering summary.

As an iOS developer, I’ve had little exposure to the MVVM pattern from a native development perspective, because the Cocoa framework of iOS is a natural MVC architecture pattern. So let’s start with MVC. The following picture is widely circulated:

M is the Model, which is used to represent business data.

V is a UIView in iOS, and it’s used for displaying pages.

C is the UIViewController in iOS, which is used to process the received interaction events, and after processing, it wants to pass the changed data Model to the View, update the page, etc.

As far as iOS developers are concerned, well, everything is fine!

And then it’s going to write UI in Controller, write the network request….. Then, in the OC era, it was easy for a Controller to break thousands of rows without good sorting and encapsulation. Writing code is fun, but CodeReview is a thousand tears.

In fact, we often fall into the misconception that UIViewController is a Controller, but look at the prefix UIView/Controller, it carries the mission of UIView at the same time, it has more than one job, it is not bloated.

Even in the most mundane of page jumps in code, such as in a View page, we click to push to the next page, we have to call the view event back to the Controller layer and push it through the Controller (of course this is how iOS is designed).

But in other developments (Vue and Flutter), there is no Controller at all and any page event can be skipped.

So narrow view of the problem, we always live separate UIView and UIViewController, obviously they are a family! So you can say that UIViewController is a UIView that gets delayed by processing data and logic!

Since UIViewController is not suitable for data processing and logic, let’s make a layer that does this, and MVVM appears:

Model <=> ViewModel <=> UIView/UIViewController

The purpose of UIView/UIViewController is just to interact with events and display data, data binding.

The ViewModel receives the events from the UIView/UIViewController, does the Model data and logic business, and then feeds the processed data to the UIView/UIViewController. Which in turn drives changes to the UIView/UIViewController view.

Model is the same Model used to represent the business data.

By redividing functions, we see things in a new way and in a new direction.

In iOS, MVVM treats BOTH UIView and UIViewController as the View layer, and creates a new ViewModel layer to handle what the previous Controller is doing. Since the page is driven by data binding, the interaction -> page changes are natural.

Why MVVM

We call it the ViewModel layer, just out of habit, you just think of it as a middle layer, you name it, it’s just everybody calls it that so it just keeps on calling it.

In fact, MVVM has been widely used in development, especially in the front end. Basically, the mainstream framework is the MVVM model, and it has also withstood the test, proving the superiority of this model.

However, in general iOS development, there are few good native support for data binding and drivers, so the MVVM model does not work. RxSwift is made for the MVVM mode!

Here’s an article about native MVVM support written by a big guy, so you can see how hard it is to be native — MVC and MVVM in detail.

Write and use the ViewModel

Write: detangle data from business logic

We create a new class, called RxSwiftCoinRankListViewModel, to pull away and encapsulation:

Class RxSwiftCoinRankListViewModel {/ / / initializes the page 1 private var page: Int = 1 / / / DisposeBag private let DisposeBag: DisposeBag the BehaviorSubject let dataSource is a BehaviorSubject let dataSource for both the listener sequence and the observer. BehaviorRelay<[CoinRank]> = BehaviorRelay(value: []) BehaviorRelay(value: []) BehaviorRelay(value: []) BehaviorSubject < MJRefreshAction > = BehaviorSubject (value: begainRefresh) initialization of / / / / / / - Parameter disposeBag: DisposeBag Init (disposeBag: DisposeBag) {self. DisposeBag = DisposeBag} / / / the drop-down refresh behavior func refreshAction {resetCurrentPageAndMjFooter () () Func loadMoreAction() {page = page + 1 getCoinRank(page: page)} The parameters of the page)} / / / the drop-down and reset behavior private func resetCurrentPageAndMjFooter () {page = 1 refreshSubject. OnNext (. ResetNomoreData)} Private func getCoinRank(page: Int) {myprovider.rx.request (myservice.coinrank (page)) /// turn model.map (BaseModel< page < coinRank >>.self) /// $0.data.datas.map {$0.data.data} /// unpack.compactMap {$0} /// Transform operation.asObservable() .subscribe {event == 1; // subscribe {event == 1; // subscribe {event == 1; // subscribe {event == 1; self.refreshSubject.onNext(.stopRefresh) : self.refreshSubject.onNext(.stopLoadmore) switch event { case .success(let pageModel): / / / unpack data if the let datas. = pageModel datas {/ / / on the page of value judgment is a drop-down or pull, do data processing, in order to facilitate comment here, not using the ternary operator if page = = 1 {/ / / the drop-down do assignment operation Self.datasource. Accept (datas)}else {self.datasource. Accept (self.datasource. Value + datas)}} /// Unpack curPage with pageCount if let curPage = pagemodel.curpage, Let pageCount = pagemodel.pagecount {if curPage == pageCount {if curPage == pageCount { Self. RefreshSubject. OnNext (. ShowNomoreData)}} case. The error (_) : / / / the error of not do processing break}}. Disposed (by: disposeBag)}}Copy the code

use

import UIKit import RxSwift import RxCocoa import NSObject_Rx import MJRefresh class RxSwiftCoinRankListController: Private lazy var tableView = UITableView(frame:.zero, style: .plain) override func viewDidLoad() { super.viewDidLoad() setupTableView() } private func setupTableView() { /// Set tableFooterView tableView. TableFooterView = UIView () / / / set the proxy tableView. Rx. SetDelegate (self) disposed (by: Rx. DisposeBag) / / / create the vm let vm = RxSwiftCoinRankListViewModel (disposeBag: Rx.disposebag) /// set the header refresh control tableView.mj_header = MJRefreshNormalHeader() tableView.mj_header? .rx.refresh .subscribe { _ in vm.refreshAction() }.disposed(by: Rx. DisposeBag) / / / set the tail refresh control tableView. Mj_footer = MJRefreshBackNormalFooter () tableView. Mj_footer? .rx.refresh .subscribe { _ in vm.loadMoreAction() }.disposed(by: Rx. DisposeBag) / / / simple layout the addSubview (tableView) tableView. SNP. MakeConstraints {make in the make. Edges. EqualTo (view)} /// dataSource driver vm.datasource.asdriver (onErrorJustReturn: []) .drive(tableView.rx.items) { (tableView, row, coinRank) in if let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") { cell.textLabel?.text = coinRank.username cell.detailTextLabel?.text = coinRank.coinCount?.toString return cell  }else { let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "Cell") cell.textLabel?.text = coinRank.username cell.detailTextLabel?.text = coinRank.coinCount?.toString return cell } }.prompt (by: rX.disposebag) /// Bind to tableView VM. RefreshSubject. Bind (to: tableView.rx.refreshAction) .disposed(by: rx.disposeBag) } }Copy the code

Does this make the code in Controller simpler and clearer?

The vm’S dataSource is unbound to the tableView, and refreshSubject in the VM is unbound to the tableView’s dropdown and pull-up state.

That’s all the logic.

conclusion

In a four-day update, I basically walked you through the process of building a page with RxSwift:

  • Write the same page in Swift and RxSwift, respectively, and use Moya and RxMoya to show the differences.

  • Add drop – down refresh and pull – up load to the page.

  • Encapsulate MJRefresh over RxSwift for pages, making encoding cleaner and more Rx.

  • Encapsulate the ViewModel from the business logic in the page and invoke it in the page.

To this, a page of writing and optimization is completed.

As I have said before, most of the pages in the Android App are lists. Through the updates and knowledge points in these four days, many pages are very general.

I will not go into the basic network requests, dropdowns, and pull-ups of the page when I cover other pages, so please be aware.

Tomorrow to continue

As summarized above, this page is finished, many pages can also follow the gourd painting gourd.

The home page ViewModel and page writing will be explained later.

Come on, everybody!