Swift 项目:ReactiveSwift+MVVM+RealmSwift+Alamofire+OM
Posted iOS开发精髓
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Swift 项目:ReactiveSwift+MVVM+RealmSwift+Alamofire+OM相关的知识,希望对你有一定的参考价值。
这篇文章先上我要说的项目的整体框架图吧,有兴趣的可以看下去。
swift.png
那么正片现在就要开始了。。。。
小样儿
我们一个一个介绍,先看看什么是MVVM模式。
当然在说这个之前,我们不得不先说说什么是MVC模式,当然这个模式是随着编码的积累慢慢形成的。
初入ios的小白,可能什么代码都会放在C(Controller)中,例如:一个视图中UILabel、UIButton、UITableView等视图对象,数据对象,各种逻辑判断的flag,各种if,else满天飞,这样就会发现C(Controller)中的代码越来越多,之后别人阅读和维护这样的项目,心里这种十万个“垃圾”,“写的像一坨屎”翻腾。
于是我们慢慢的形成一种模式,MVC。
MVC具体版
MVC简洁版
MVC模式
说明:从图中可以看出,C(Controller)是连接M(Model)和V(View)的重要中转站,
当然C也是作为项目中地位最为重要的一部分,负责的事务:
1.添加,显示,更新视图对象
2.处理view对象中的响应事件
3.网络请求,给M(Model)传入字典数据得到自定义的数据对象
4.将得到的数据对象(Model)传入view的对象中更新视图
5.视图之间的交互,跳转
为了能让你更具体了解什么是MVC,我们就拿简书界面来说吧。
IMG_0792.PNG
主要写一下思路,至于代码的部分,只是说明这个流程
class YourController: UIViewController {
//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 SubHomeScrollDataModel: NSObject {
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项目中网络层必须用到的框架了,具体的源码我这儿就不介绍了(毕竟我也不是很清楚啊)
哈哈
不过它的用法还是很简单的。
static func doPostRequest(_ param: [String: Any], URL: String, completeBlock: @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!, encoding: String.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 StoreRequestManager: NSObject {
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(map: map)
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源码解析 Atomic的代码实现以及其中的Defer延迟Posix互斥锁递归锁
iOS 项目从 ReactiveSwift 迁移到 openCombine
ReactiveSwift:使用 MutableProperty 观察托管对象的变化