RxSwift/Moya/Codable在MVVM中的使用

我要在河邊釣一整天的魚發表於2017-12-19

參考資料

1.MVVM-維基百科
2.MVVM with RxSwift
3.RxSwift
4.RxSwift 學習指導
5.Moya
5.Codable

實現

這裡只解釋我對MVVM的理解,不再對RxSwift、Moya、Codable、MVVM做過多的敘述,相信Google會讓你知道的更多。

一、目錄結構

目錄結構.png

二、網路請求

1、相同格式資料的處理

對於Json的資料返回會有一個統一的返回格式

我們公司的Json格式,這三個欄位是鐵打不會變的,改變的只是data的資料。

{
	code:Int,
	data:[],
	message:String
}
複製程式碼

練習使用的是豆瓣的一個介面 ("https://api.douban.com/v2/movie/top250")

class ReponseData<T: Codable>: Codable {
    var count: Int?
    var start: Int?
    var total: Int?
    var subjects: T?
}

複製程式碼

3.對Moya的封裝

Moya官方有一個和RxSwfit對接的擴充套件也可以直接使用哪個;
Json轉Model使用的Codable。

(1)Moya URL的配置檔案

Moya對URL的配置

import Foundation
import Moya

enum APIConfig {
    case top
}

extension APIConfig: TargetType {
    var sampleData: Data {
        return "{}".data(using: .utf8)!
    }
    
    var task: Task {
        return .requestPlain
    }
    
    var headers: [String : String]? {
        return nil
    }
    
    var baseURL: URL {
        return URL.init(string: "https://api.douban.com/v2/movie/")!
    }
    
    var path: String {
        switch self {
        case .top:
            return "top250"
        }
    }
    
    var method: Moya.Method {
        switch self {
        case .top:
            return .get
        }
    }
}
複製程式碼

(2)Request封裝

func request <Element: Codable> (config: APIConfig) -> Observable<ResponseResult<Element>> {
        return Observable.create({[weak self] (observable) -> Disposable in
            let provider = MoyaProvider<APIConfig>()
            let callBack = provider.request(config, completion: { (responseResult) in
                switch responseResult {
                case let .success(response):
                    do {
                        let decoder = JSONDecoder()
                        let data = try decoder.decode(ReponseData<Element>.self, from: response.data)
                        let subjects = data.subjects
                        
                        let result = (subjects == nil) ? ResponseResult.empty : ResponseResult.succeed(data: subjects!)
                        observable.onNext(result)
                    }catch let error {
                        self?.requestError(message: error.localizedDescription)
                        observable.onNext(ResponseResult.failed(message: error.localizedDescription))
                    }
                case let .failure(error):
                    self?.requestError(message: error.localizedDescription)
                    observable.onNext(ResponseResult.failed(message: error.localizedDescription))
                }
            })
            return Disposables.create {
                callBack.cancel()
            }
        })
    }
複製程式碼

錯誤這樣處理是為了一些彈框的統一處理,且使用物件也獲得錯誤資訊可以做一些特殊處理。

三、Model

它只對各種資料和業務進行處理然後更新ViewModel;
不要含有任何UIKit類.

class Model: NSObject {

    public func getData() -> Observable<ResponseResult<[Subjects]>> {
        return HttpClient().request(config: APIConfig.top)
    }
}
複製程式碼

四、View

包含ViewController、View、storyboard檔案,view是對業務是無感的,只做介面展示和使用者互動。

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    @IBOutlet private weak var tableView: UITableView!
    private var viewModel: ViewModel = ViewModel()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.tableFooterView = UIView()
        viewModel.getData {[weak self] (status) in
            self?.tableView.reloadData()
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return viewModel.getItemCount()
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell: TopTableViewCell = tableView.dequeueReusableCell(withIdentifier: "TopTableViewCell", for: indexPath) as! TopTableViewCell
        cell.title.text = viewModel.getBindData(index: indexPath.row).title.value
        return cell
    }
}
複製程式碼

五、ViewModel

協調View和Model、資料的儲存。

class ViewModel: NSObject {
    private var model = Model()
    private var list = [Subjects]()
    private var errorMessage: String = ""
    
    func getData(callBack: @escaping (_ result: RequestStatus) -> Void) {
        model.getData().subscribe(onNext: {[weak self] (result) in
            switch result {
            case .empty:
                callBack(.empty)
            case .succeed(let data):
                self?.list = data
                callBack(.succees)
            case .failed(let errorMessage):
                self?.errorMessage = errorMessage
                callBack(.failed)
            }
        }).disposed(by: rx_disposeBag)
    }
    
    func getItemCount() -> Int {
        return list.count
    }
    
    func getBindData(index: Int) -> BindingData {
        return BindingData.init(list[index].title)
    }
    // 獲取網路錯誤資訊
    func getErrorMessage() -> String {
        return errorMessage
    }
}
複製程式碼

以上是我對MVVM的理解,每個人對MVVM都有自己不同的理解,有什麼疑問或者問題或者好的建議歡迎指正。 專案地址


謝謝觀看

相關文章