Swift 项目:ReactiveSwift+MVVM+RealmSwift+Alamofire+OM

Posted iOS开发精髓

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Swift 项目:ReactiveSwift+MVVM+RealmSwift+Alamofire+OM相关的知识,希望对你有一定的参考价值。

这篇文章先上我要说的项目的整体框架图吧,有兴趣的可以看下去。


swift.png

那么正片现在就要开始了。。。。

Swift 项目:ReactiveSwift+MVVM+RealmSwift+Alamofire+OM

小样儿

我们一个一个介绍,先看看什么是MVVM模式。

当然在说这个之前,我们不得不先说说什么是MVC模式,当然这个模式是随着编码的积累慢慢形成的。

初入ios的小白,可能什么代码都会放在C(Controller)中,例如:一个视图中UILabel、UIButton、UITableView等视图对象,数据对象,各种逻辑判断的flag,各种if,else满天飞,这样就会发现C(Controller)中的代码越来越多,之后别人阅读和维护这样的项目,心里这种十万个“垃圾”,“写的像一坨屎”翻腾。

于是我们慢慢的形成一种模式,MVC。

Swift 项目:ReactiveSwift+MVVM+RealmSwift+Alamofire+OM

MVC具体版


Swift 项目:ReactiveSwift+MVVM+RealmSwift+Alamofire+OM

MVC简洁版

MVC模式

 说明:从图中可以看出,C(Controller)是连接M(Model)和V(View)的重要中转站,
      当然C也是作为项目中地位最为重要的一部分,负责的事务:
  1.添加,显示,更新视图对象
  2.处理view对象中的响应事件
  3.网络请求,给M(Model)传入字典数据得到自定义的数据对象
  4.将得到的数据对象(Model)传入view的对象中更新视图
  5.视图之间的交互,跳转

为了能让你更具体了解什么是MVC,我们就拿简书界面来说吧。

Swift 项目:ReactiveSwift+MVVM+RealmSwift+Alamofire+OM

IMG_0792.PNG

主要写一下思路,至于代码的部分,只是说明这个流程


class YourControllerUIViewController {

  //View 对象(也可以是自定义视图对象)
  private lazy var tableView : UITableView = {
        () -> UITableView in

        let _tableView = tableViewConfig(.zero, self, self, .plain)
        _tableView.rowHeight = RS_SCALE_LENGTH(value: 64.0)
        _tableView.backgroundColor = .clear
        registerCell(_tableView, RSScenesCell.self)
        return _tableView
    }()

//网络请求返回字典数据
  func dataRequest() {

        Alamofire.request(URL, method: .post, parameters: mutParam, encoding: URLEncoding.default).responseJSON { response in

            if response.result.error == nil {

                 //  请求成功
                let text: NSString = NSString(data: response.data!, encoding: String.Encoding.utf8.rawValue)! as NSString
                print("Data\(text as AnyObject)")

              //向Model中传入字典得到自定义数据对象
              //刷新tableView
            } else {

                print("error\((response.result.error)! as AnyObject)")
            }
        }
    }
}

extension YourController : UITableViewDelegate, UITableViewDataSource {

    //delegate or dataSource

}

//Model 部分
class SubHomeScrollDataModelNSObject {

    var isViewShow: Bool?  //数据对象属性

    class func simpleModel(dic: [String: Any]) -> SubHomeScrollDataModel {

        let model = SubHomeScrollDataModel()
        model.isViewShow = dic[""]
        return model
    }

}

这样一个很简单的MVC设计模式就出现了,当然这样的好处:

1.视图对象的封装
2.控制器代码相应的减少

当然也有它的缺点:

1.网络层的代码并没有抽离出来
2.逻辑业务层的处理

基于上面的设计缺点,我个人的理解,就是抽离出来网络层放到ViewModel中,这样就形成了我这个项目的MVVM的模式。

Alamofire

这个算是Swift项目中网络层必须用到的框架了,具体的源码我这儿就不介绍了(毕竟我也不是很清楚啊)

Swift 项目:ReactiveSwift+MVVM+RealmSwift+Alamofire+OM

哈哈

不过它的用法还是很简单的。

    static func doPostRequest(_ param: [String: Any], URLStringcompleteBlock: @escaping (_ response: DataResponse<Any>) -> Void) {

        let header = ["TOKEN": RSUserManager.shared().accessToken]

        Alamofire.request(URL, method: .post, parameters: param, encoding: URLEncoding.default, headers: header).responseJSON { response in

            if response.result.error == nil {

                /**
                 *  请求成功
                 */

                let text: NSString = NSString(data: response.data!, encodingString.Encoding.utf8.rawValue)! as NSString
                print("Data\(text as AnyObject)")
            } else {

                /**
                 请求出错了

                 - print: 错误信息
                 */

                print("error\((response.result.error)! as AnyObject)")
            }

            completeBlock(response)
        }

    }
//注: 这里是底层的与Alamofire接口相连接的BaseRequestManager

既然与Alamofire连接好了,接着我们再封装一层网络的管理者,例如首页获取数据的网络管理者,这个我们需要关心的是:

1.传入的字典
2.传出的自定义数据Model

那这个怎么玩呢?我这里是请来了ReactiveSwift,ObjectMapper这两位大哥镇场子。

class StoreRequestManagerNSObject {

    class func storeDataRequest(page: Int, segType: RSStoreSegmentType) -> SignalProducer<RSStoreDataModel, NoError> {

        return SignalProducer<RSStoreDataModel, NoError>.init { (observer, _) in

            var params = [String : String]()
            params["page"] = "1"
            params["type"] = "\(segType.hashValue)"
            BaseRequestManager.doPostRequest(params,
                                             URL:RSRequestUrl.STORE_MAIN_URL) { (response) -> Void in

                                                if response.result.error == nil {

                                                    let responseModel = Mapper<RSStoreDataModel>().map(JSONObject: response.result.value)
                                                    observer.send(value: responseModel!)

                                                }else {
                                                    //网络出错
                                                    let responseModel = RSStoreDataModel()
                                                    responseModel.netErrorResultModel()
                                                    observer.send(value: responseModel)
                                                }
            }
        }
    }

}
//注:RSStoreDataModel传出的数据模型,这个对象是实现了Mappable协议的,
//具体的可以看下面对ObjectMapper的介绍,通过ReactiveSwift中的观察者机制传出。

ObjectMapper

说得通俗一点,它的功能就是做数据对象的映射,把服务器的字典数据转成自定义对象。

。。。。。什么?陈独秀同学你说用字典的键值对获取的方法也可以生成自定义对象,那么老司机我就用代码来说说吧。

字典的格式
 /* { 
    key1: value1,
    key2: value2,
    key3: value3,
    key4: value4,
    key5:[
              {
                    key1: value1,
                    key2: value2,
                    key3: value3,
                    key4: value4,
                },
                {
                   key1: value1,
                   key2: value2,
                   key3: value3,
                   key4: value4,
                },
              ]
    msg: "成功"
    code:1
}*/


class ProductNormModel: BaseResultModel {   //商品规格数据模型

    var colorModels = [ProductColorModel]()  //重点
    var goodId: NSNumber = 0
    var normId: NSNumber = 0
    var normName: String = ""

    func mapping(map: Map) {

        super.mapping(mapmap)
        colorModels <- map["colors"]      //数组数据的直接映射
        goodId <- map["goodId"]
        normId <- map["id"]
        normName <- map["name"]
    }
}

class ProductColorModel: Mappable {   //商品颜色数据模型

    var count: NSNumber = 0
    var colorId : NSNumber = 0
    var name: String = ""
    var normId: NSNumber = 0

    var price: NSNumber = 0

    init(){}
    required init?(map: Map){}

    func mapping(map: Map) {

        count <- map["count"]
        colorId <- map["id"]
        name <- map["name"]
        normId <- map["normId"]
        price <- map["price"]
    }
}

//当然如果用常规的键值对获取的方法,也可以办到,但那样就感觉很繁琐了。
//数组的映射直接是火箭式封装

用这个框架的时候,我这边经过了一层数据结构的封装,怎么说呢,就是上面的BaseResultModel,这个是做什么用的呢,容我慢慢道来。

原因:每个API接口都会有msg,code/status 这些固定字段,但如果我们去解析每个接口时都去定义对象中相应的属性(对应msg,code),这样就感觉就是做重复的工作,所以我把这个工作放在底层的BaseResultModel这个对象中。

import ObjectMapper

class BaseResultModel: Mappable {

    var type: RequestResultType?
    var msg : String = ""
    var obj = Dictionary<String, Any>()

    init(){}
    required init?(map: Map){}

    func mapping(map: Map) {

        var stauts : NSNumber?
        stauts <- map["res"]
        if stauts == 1 {
            type = .RequestSuccessful
        }else if stauts == 301 {
            type = .RequestTokenIsInvalid
        }else {
            type = .RequestFail
        }
        msg <- map["msg"]
        obj <- map["obj"]
    }

    func netErrorResultModel() {

        type = RequestResultType.RequestNetError
        msg = "网络请求超时"
    }
}
// 这样我只需要继承这个对象即可,是不是很机智

以上是关于Swift 项目:ReactiveSwift+MVVM+RealmSwift+Alamofire+OM的主要内容,如果未能解决你的问题,请参考以下文章

reactiveswift 空信号用于干吗

ReactiveSwift源码解析 Atomic的代码实现以及其中的Defer延迟Posix互斥锁递归锁

iOS 项目从 ReactiveSwift 迁移到 openCombine

ReactiveSwift:使用 MutableProperty 观察托管对象的变化

iOS 项目从 ReactiveSwift 迁移到 openCombine

适用于Swift 3.0的Reactive Cocoa