RXSwift基础网络请求封装

一、创建项目,集成cocoapods文件

target 'TestAPI' do
    use_frameworks!

    pod 'Alamofire'
    pod 'SwiftyJSON'
    pod 'RxSwift',    '~>4.1.1'
    pod 'RxCocoa',    '~>4.1.1'
    pod 'RxDataSources'
    pod 'Moya/RxSwift', '~> 11.0'
    pod 'NSObject+Rx'
    pod 'SVProgressHUD'
end

这里使用的是Moya、SwiftyJSON、Alamofire进行网络请求的封装。

二、配置网络请求

//1、根据Moya要求,网络请求需要创建一个Provider,MoyaProvider需要我们传入一个实现TargetType协议的对象,这个协议里面包含了请求的路由地址和具体路径、请求方式等网络请求相关基本信息
let ComSmaManProvider = MoyaProvider<ComSmaManApi>(plugins:[AuthPlugin()])
//路由地址
let KBaseURL = "https://www.douban.com"

public enum ComSmaManApi{
      //基本路由地址get、post请求
      case post(suffixUrl:String, params:[String:Any])
      case get(suffixUrl:String, params:[String:Any])

      //其它路由地址get、post请求
      case otherRequst(baseUrl:String, type:OtherBaseURLRequst)
      public enum OtherBaseURLRequst {
            case post(suffixUrl:String, params:[String:Any])
            case get(suffixUrl:String, params:[String:Any])
      }

}
//实现TargetType协议,配置基本信息
extension ComSmaManApi:TargetType{
    //路由地址
    public var baseURL: URL {
          let result = self.getConfigure()
          return URL(string: result.1)!
    }
    //具体地址
    public var path: String {
          let result = self.getConfigure()
          return result.2
    }
    //请求方式get、post
    public var method: Moya.Method {
          let result = self.getConfigure()
          return result.0
    }
    //单元测试所用
    public var sampleData: Data {
          return "{}".data(using: .utf8)!
    }
    //请求任务事件(这里附带上参数)
    public var task: Task {
        //request上传、upload上传、download下载
       let result = self.getConfigure()
       return .requestParameters(parameters: result.3, encoding: URLEncoding.default)
    }
    //请求头
    public var headers: [String : String]? {
        return nil
    }

    private func getConfigure() -> (Moya.Method,String,String,[String:Any]) {
        switch self {
        case .get(suffixUrl: let suffixUrl, params: let params):
            return (.get,KBaseURL,suffixUrl,params)
        case .post(suffixUrl: let suffixUrl, params: let params):
            return (.post,KBaseURL,suffixUrl,params)
        case .otherRequst(baseUrl: let baseUrl, type: let type):
            switch type{
                case .get(suffixUrl: let suffixUrl, params: let params):
                      return (.get,baseUrl,suffixUrl,params)
                case .post(suffixUrl: let suffixUrl, params: let params):
                      return (.post,baseUrl,suffixUrl,params)
             }
        }
    }

}
//当需要在header里面添加请求token或者时间戳等信息时可以传入这些配置
struct AuthPlugin: PluginType {

func prepare(_ request: URLRequest, target: TargetType) -> URLRequest {
    /*
    var request = request
    UserDefaults.standard.synchronize()
    let accessToken = UserDefaults.standard.value(forKey: "accessToken") as? String
    let timestamp: Int = Int.getTimeStamp()
    request.addValue("\(timestamp)", forHTTPHeaderField: "timestamp")
    if accessToken != nil {
        request.addValue(accessToken!, forHTTPHeaderField: "accessToken")
    }
    print(request.allHTTPHeaderFields)
     */
    return request
}
}

三、配合SwiftyJSONMapper实现Data转JSON,JSON转Model

1、先创建一个JSONMappable协议,让我们创建的model都遵守这个协议
Snip20180810_1.png
2、对Response和PrimitiveSequence扩展
Snip20180810_2.png

Response+SwiftyJSONMapper文件

public extension Response {

/// Maps data received from the signal into an object which implements the ALSwiftyJSONAble protocol.
/// If the conversion fails, the signal errors.
public func map<T: JSONMappable>(to type:T.Type) throws -> T {
    let jsonObject = try mapJSON()
    
    guard let mappedObject = T(fromJson: JSON(jsonObject)) else {
        throw MoyaError.jsonMapping(self)
    }
    
    return mappedObject
}

/// Maps data received from the signal into an array of objects which implement the ALSwiftyJSONAble protocol
/// If the conversion fails, the signal errors.
public func map<T: JSONMappable>(to type:[T.Type]) throws -> [T] {
    let jsonObject = try mapJSON()
    
    let mappedArray = JSON(jsonObject)
    let mappedObjectsArray = mappedArray.arrayValue.flatMap { T(fromJson: $0) }
    
    return mappedObjectsArray
}

}

extension Response {

@available(*, unavailable, renamed: "map(to:)")
public func mapObject<T: JSONMappable>(type:T.Type) throws -> T {
    return try map(to: type)
}

@available(*, unavailable, renamed: "map(to:)")
public func mapArray<T: JSONMappable>(type:T.Type) throws -> [T] {
    return try map(to: [type])
}
}

PrimitiveSequence+SwiftyJSONMapper文件

/// Extension for processing Responses into Mappable objects through ObjectMapper
extension PrimitiveSequence where TraitType == SingleTrait, ElementType == Response {

/// Maps data received from the signal into an object which implements the ALSwiftyJSONAble protocol.
/// If the conversion fails, the signal errors.
public func map<T: JSONMappable>(to type: T.Type) -> Single<T> {
    return flatMap { response -> Single<T> in
        return Single.just(try response.map(to: type))
    }
}

/// Maps data received from the signal into an array of objects which implement the ALSwiftyJSONAble protocol.
/// If the conversion fails, the signal errors.
public func map<T: JSONMappable>(to type: [T.Type]) -> Single<[T]> {
    return flatMap { response -> Single<[T]> in
        return Single.just(try response.map(to: type))
    }
}
}
3、创建model
import SwiftyJSON
struct Douban: JSONMappable{
var channels :[Channel]?
init(fromJson json: JSON) {
    channels = json["channels"].arrayValue.map({Channel(fromJson: $0)})
}
}

struct Channel: JSONMappable {
var name :String?
var nameEn :String?
var channelId :String?
var seqId :String?
var abbrEn :String?

init(fromJson json: JSON) {
    name = json["name"].stringValue
    nameEn = json["nameEn"].stringValue
    channelId = json["channel_id"].stringValue
    seqId = json["seqId"].stringValue
    abbrEn = json["abbrEn"].stringValue
}
}

struct Playlist:JSONMappable {

var r :Int!
var isShowQuickStart: Int!
var song:[Song]!

init(fromJson json: JSON) {
    r = json["r"].intValue
    isShowQuickStart = json["is_show_quick_start"].intValue
    song = json["song"].arrayValue.map({Song(fromJson: $0)})
}

}

struct Song:JSONMappable {
var title: String!
var artist: String!
init(fromJson json: JSON) {
    title = json["title"].stringValue
    artist = json["artist"].stringValue
}
}
4、在viewcontroller中利用ComSmaManProvider请求数据
  let data = ComSmaManProvider.rx.request(.get(suffixUrl: "/j/app/radio/channels", params: [:]))
        .map(to: Douban.self)  //转换成model
        .map{$0.channels ?? []} //获取数组channels作为tableview的数据源
        .asObservable()
    //将数据绑定到tableview上
    data.bind(to: tableView.rx.items){ (tableView,row,element) in
        
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell")!
        cell.textLabel?.text = element.name
        cell.accessoryType = .disclosureIndicator
        return cell
    }.disposed(by: disposeBag)
5、最终效果
Snip20180810_3.png

四、在实际开发项目中,我们都与后台约定一个数据返回成功的标识,比如status为1是成功,0为失败等等其他状态。

创建ResponseMapper文件,内容如下

let RESULT_CODE = "status"
let RESULT_DATA = "data"


enum XYRequestStatus:String {
    case RequstSuccess = "200"
    case RequstError
}


enum XYError : Error {
    case noRepresentor
    case notSuccessfulHTTP
    case noData
    case couldNotMakeObjectError
    case bizError(resultCode: String?, resultMsg: String?)
}


extension Observable{

    private func resultFromJSON<T:JSONMappable>(jsonData:JSON,classType:T.Type) -> T? {
    return T(fromJson: jsonData)
}

func mapResponseToObj<T:JSONMappable>(type:T.Type) -> Observable<T?> {
    return map{ representor in
        guard let response = representor as? Moya.Response else{
            throw XYError.noRepresentor
        }
        guard ((200...209) ~= response.statusCode) else{
            throw XYError.notSuccessfulHTTP
        }
        
        let json = try? JSON.init(data: response.data)
        if let code = json?[RESULT_CODE].string{
            if code == XYRequestStatus.RequstSuccess.rawValue{
                
                return self.resultFromJSON(jsonData: json![RESULT_DATA], classType: type)
            } else {
                
                throw XYError.bizError(resultCode: json?["code"].string, resultMsg: json?["msg"].string)
            }
        }else{
            throw XYError.couldNotMakeObjectError
        }
    }
}

func mapResponseToObjArray<T:JSONMappable>(type:T.Type) -> Observable<[T]> {
    return map{ representor in
        guard let response = representor as? Moya.Response else{
            throw XYError.noRepresentor
        }
        guard ((200...209) ~= response.statusCode) else{
            throw XYError.notSuccessfulHTTP
        }
        
        let json = try? JSON.init(data: response.data)
        
        if let code = json?[RESULT_CODE].string{
            if code == XYRequestStatus.RequstSuccess.rawValue{
                
                var objects = [T]()
                let objectsArray = json?[RESULT_DATA].array
                if let array = objectsArray{
                    for object in array{
                        if let obj = self.resultFromJSON(jsonData: object, classType: type){
                            objects.append(obj)
                        }
                    }
                    return objects
                }else{
                    throw  XYError.noData
                }
                
            }else{
                throw XYError.bizError(resultCode: json?["code"].string, resultMsg: json?["msg"].string)
            }
            
        }else{
            throw XYError.couldNotMakeObjectError
        }
    }
}

}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容

  • 1、通过CocoaPods安装项目名称项目信息 AFNetworking网络请求组件 FMDB本地数据库组件 SD...
    阳明先生_X自主阅读 15,969评论 3 119
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,066评论 4 62
  • 谈自然疗法似乎是件很抽象的事,然而对于2015年带着“做自然疗法信差”使命而诞生的平衡族健康科技公司而言,这是一件...
    平衡族阅读 322评论 0 0
  • 有时候会莫名的有些小情绪,以前一个人的时候喜欢忍者,所以在别人眼中我一直都是一个很坚强很倔强的小姑娘,因为。我...
    会飞的龙猫猫阅读 196评论 0 0
  • 今日事件 1.服务签约产业空间站站长组场邀约 2.确定一个产业空间站一个厂家,明日签约 3.学习宁静远导师的产业生...
    全息心空间阅读 134评论 0 0