iOS RxSwift+Moya+ObjectMapper实现网络请求数据与解析

抓取的是有妖气的API

学起来很麻烦、

1、下面是封装好的网络请求部分

项目地址 可以在这里下载代码
目前还在更新代码、、、、、
最近项目上很忙 没来得及继续更新了

//
//  YYQApi.swift
//  YYQCartoon
//
//  Created by kcl on 2018/3/21.
//  Copyright © 2018年 KCL. All rights reserved.
//

import Foundation
import Moya
import ObjectMapper
import RxSwift
import RxCocoa
import Result

final class RequestAlertPlugin: PluginType {

    private let viewController: UIViewController

    init(viewController: UIViewController) {
        self.viewController = viewController
    }

    func willSend(_ request: RequestType, target: TargetType) {

        guard let requestURLString = request.request?.url?.absoluteString else { return }

        let alertViewController = UIAlertController(title: "Sending Request", message: requestURLString, preferredStyle: .alert)
        alertViewController.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))

        viewController.present(alertViewController, animated: true, completion: nil)
    }

    func didReceive(_ result: Result, target: TargetType) {


        guard case Result.failure(_) = result else { return }


        let alertViewController = UIAlertController(title: "Error", message: "Request failed with status code: ", preferredStyle: .alert)
        alertViewController.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))

        viewController.present(alertViewController, animated: true, completion: nil)

    }
}

private let YYQEndpointMapping = {(target:YYQApi) -> Endpoint in

    let url = target.baseURL.appendingPathComponent(target.path).absoluteString
    let endpoint = Endpoint(url: url,
                                    sampleResponseClosure: {.networkResponse(200, target.sampleData)},
                                    method: target.method,
                                    task: target.task,
                                    httpHeaderFields: target.headers)

    print("url=\(url),param=\(String(describing: target.task))")
    let token = UserDefaults.standard.string(forKey: "accesstoken") ?? ""
    if token == "" {
        return endpoint.adding(newHTTPHeaderFields: [
            "Accept":"application/json",
            "Content-Type":"application/json",
            "Accept-Language":"en",
//            "Authorization":"key=\(MoveApi.apiKey)"
            ])
    }else{

        return endpoint.adding(newHTTPHeaderFields: [
            "Accept":"application/json",
            "Content-Type":"application/json",
            "Accept-Language":"en",
//            "Authorization":"key=\(MoveApi.apiKey),token=\(token)"
            ])
    }
}
//public final class func defaultRequestMapping(for endpoint: Endpoint, closure: RequestResultClosure) {
//    do {
//        let urlRequest = try endpoint.urlRequest()
//        closure(.success(urlRequest))
//    } catch MoyaError.requestMapping(let url) {
//        closure(.failure(MoyaError.requestMapping(url)))
//    } catch MoyaError.parameterEncoding(let error) {
//        closure(.failure(MoyaError.parameterEncoding(error)))
//    } catch {
//        closure(.failure(MoyaError.underlying(error, nil)))
//    }
//}

//private let YYQRequestTimeoutClosure = { (endpoint: Endpoint, done: @escaping MoyaProvider.RequestResultClosure) in
//    guard let request = endpoint.urlRequest else { return }
//    request.timeoutInterval = 10    //设置请求超时时间
//    request.httpShouldHandleCookies = false
//    done(.success(request))
//}


/// 网络封装
class YYQRequest{

    private static let dProvider = MoyaProvider()
    private static let YYQProvider = MoyaProvider(
        endpointClosure:YYQEndpointMapping
//        plugins:[RequestAlertPlugin(viewController: (UIApplication.shared.keyWindow?.rootViewController)!)]
//        requestClosure:YYQRequestTimeoutClosure,
        )
    //获取Vip列表
    final class func getVipList() ->Observable{

        return YYQProvider.rx.request(.vipList)
            .mapJSON()
            .asObservable()
            .mapObject(type:VipListModel.self)
    }
    //获取排行列表
    final class func getRankList() ->Observable{

        return YYQProvider.rx.request(.rankList)
            .mapJSON()
            .asObservable()
            .mapObject(type:RankListModel.self)
    }
    //获取推荐列表
    final class func getRecommendList() ->Observable{

        return YYQProvider.rx.request(.boutiqueList(sexType: 1))
            .mapJSON()
            .asObservable()
            .mapObject(type:RecommendListModel.self)
    }

    //获取订阅列表
    final class func getSubscribeList() ->Observable{

        return YYQProvider.rx.request(.subscribeList)
            .mapJSON()
            .asObservable()
            .mapObject(type:SubscribeListModel.self)
    }
}


enum YYQApi {

    case searchHot
    case searchRelative(inputText:String)   //相关搜索
    case searchResult(argCon:Int, q:String) //搜索结果
    case boutiqueList(sexType:Int)          //推荐列表
    case special(argCon:Int, page:Int)      //专题
    case vipList //VIP列表
    case subscribeList //订阅列表
    case rankList//排行列表
    case cateList//分类列表
    case comicList(argCon: Int, argName: String, argValue: Int, page: Int)//漫画列表
    case guessLike//猜你喜欢
    case detailStatic(comicid: Int)//详情(基本)
    case detailRealtime(comicid: Int)//详情(实时)
    case commentList(object_id: Int, thread_id: Int, page: Int)//评论
    case chapter(chapter_id: Int)//章节内容
}

extension YYQApi:TargetType
{
    private struct YYQApiKey {
        static var key = "fabe6953ce6a1b8738bd2cabebf893a472d2b6274ef7ef6f6a5dc7171e5cafb14933ae65c70bceb97e0e9d47af6324d50394ba70c1bb462e0ed18b88b26095a82be87bc9eddf8e548a2a3859274b25bd0ecfce13e81f8317cfafa822d8ee486fe2c43e7acd93e9f19fdae5c628266dc4762060f6026c5ca83e865844fc6beea59822ed4a70f5288c25edb1367700ebf5c78a27f5cce53036f1dac4a776588cd890cd54f9e5a7adcaeec340c7a69cd986:::open"
    }
    var baseURL: URL {
        return URL(string: "http://app.u17.com/v3/appV3_3/ios/phone")!

    }

    /// The path to be appended to `baseURL` to form the full `URL`.
    var path: String {
        switch self {

        case .searchHot: return "search/hotkeywordsnew"
        case .searchRelative: return "search/relative"
        case .searchResult: return "search/searchResult"
        case .boutiqueList: return "comic/boutiqueListNew"
        case .special: return "comic/special"
        case .vipList: return "list/vipList"
        case .subscribeList: return "list/newSubscribeList"
        case .rankList: return "rank/list"
        case .cateList: return "sort/mobileCateList"
        case .comicList: return "list/commonComicList"
        case .guessLike: return "comic/guessLike"
        case .detailStatic: return "comic/detail_static_new"
        case .detailRealtime: return "comic/detail_realtime"
        case .commentList: return "comment/list"
        case .chapter: return "comic/chapterNew"

        }
    }

    /// The HTTP method used in the request.
    var method: Moya.Method { return .get }

    /// Provides stub data for use in testing.
//    var sampleData: Data { get }

    /// The type of HTTP task to be performed.
    var task: Task {
        var parameters = ["time":Int32(Date().timeIntervalSince1970),
                          "device_id":UIDevice.current.identifierForVendor!.uuidString,
                          "key":YYQApiKey.key,
//                          "model": UIDevice.current.modelName,
                          "target": "U17_3.0",
                          "version": Bundle.main.infoDictionary!["CFBundleShortVersionString"]!]
        switch self {

            case .searchRelative(let inputText):
                parameters["inputText"] = inputText
            case .searchResult(let argCon, let q):
                parameters["argCon"] = argCon
                parameters["q"] = q
            case .boutiqueList(let sexType):
                parameters["sexType"] = sexType
                parameters["v"] = 3320101
            case .special(let argCon,let page):
                parameters["argCon"] = argCon
                parameters["page"] = max(1, page)
            case .cateList:
                parameters["v"] = 2

            case .comicList(let argCon, let argName, let argValue, let page):
                parameters["argCon"] = argCon
                if argName.count > 0 { parameters["argName"] = argName }
                parameters["argValue"] = argValue
                parameters["page"] = max(1, page)

            case .detailStatic(let comicid),
                 .detailRealtime(let comicid):
                parameters["comicid"] = comicid
                parameters["v"] = 3320101

            case .commentList(let object_id, let thread_id, let page):
                parameters["object_id"] = object_id
                parameters["thread_id"] = thread_id
                parameters["page"] = page

            case .chapter(let chapter_id):
                parameters["chapter_id"] = chapter_id
                default:break
        }

        return .requestParameters(parameters: parameters, encoding: URLEncoding.default)
    }

    /// The type of validation to perform on the request. Default is `.none`.
    var sampleData: Data { return "".data(using: String.Encoding.utf8)! }
    var headers: [String : String]? { return nil }
}

2、需要将ObjectMapper转换成所需要的Observer类型

//
//  YYQApi+Observable.swift
//  YYQCartoon
//
//  Created by kcl on 2018/3/21.
//  Copyright © 2018年 KCL. All rights reserved.
//

import Foundation
import RxCocoa
import RxSwift
import ObjectMapper

// 该文件将ObjectMapper->Observable

extension Observable {

    public func mapObject(type:T.Type) -> Observable {

        return self.map{ response in

            //if response is a dictionary, then use ObjectMapper to map the dictionary
            //if not throw an error
            guard let dict = response as? [String:Any] else {

                throw RxSwiftMoyaError.ParseJSONError
            }
            if let error = self.parseError(response: dict){
                throw error
            }
            print("response=\(dict)")
            return Mapper().map(JSON: dict)!
        }
    }
    public func mapArray(type:T.Type) ->Observable<[T]>{

        return self.map{ response in

            guard let array = response as? [[String:Any]] else{

                throw RxSwiftMoyaError.ParseJSONError
            }

            for dict:[String :Any] in array {

                if let error = self.parseError(response: dict){

                    throw error
                }
            }
            return Mapper().mapArray(JSONArray: array)
        }
    }

    final fileprivate func parseError(response:[String :Any]?) -> NSError?{

        var error:NSError?

        if let value = response{
            var code:Int?
            var msg:String?

            if let errorDic = value["error"] as? [String:Any]{

                code = errorDic["code"] as? Int
                msg = errorDic["msg"] as? String
                error = NSError(domain: "Network", code: code!, userInfo: [NSLocalizedDescriptionKey:msg ?? ""])
            }
        }
        return error
    }

    final fileprivate func parseServerError() -> Observable {

        return self.map{ (response) in

            let name = type(of: response)
            print(name)

            guard let dict = response as? [String:Any] else {

                throw RxSwiftMoyaError.ParseJSONError
            }
            if let error = self.parseError(response: dict){
                throw error
            }
            return self as! Element
        }
    }
}


enum RxSwiftMoyaError:String {

    case ParseJSONError
    case OtherError
}
extension RxSwiftMoyaError:Swift.Error{}

3、ObjectMapper的Model处理

//
//  RecommendList.swift
//  YYQCartoon
//
//  Created by kcl on 2018/3/21.
//  Copyright © 2018年 KCL. All rights reserved.
//

import Foundation
import ObjectMapper

struct RecommendListModel {

    var code:NSNumber?
    var data:RecommendList?
}

struct RecommendList {

    var stateCode:NSNumber?
    var message:String?
    var returnData:RecommendData?
}

struct RecommendData {

    var galleryItems:[GalleryItem]?
    var textItems:[TextItem]?
    var comicLists:[ComicList]?
    var editTime:String?
}

struct GalleryItem {

    var linkType:NSNumber?
    var cover:String?
    var id:NSNumber?
    var title:String?
    var content:String?
//    var ext:
}
struct TextItem {

}
struct ComicList {

    var comicType:NSNumber?
    var comics:[Comic]?
    var canedit:NSNumber?
    var sortId:String?
    var titleIconUrl:String?
    var newTitleIconUrl:String?
    var description:String?
    var itemTitle:String?
    var argName:String?
    var argValue:String?
    var argType:String?
}

struct Comic {
    //这里面有两块数据源
    var comicId:NSNumber?
    var wideCover:String?
    var tags:Array?
    var subTitle:String?
    var description:String?
    var cornerInfo:String?
    var short_description:String?
    var author_name:String?
    var is_vip:NSNumber?

    var cover:String?
    var name:String?
    var argName:String?
    var argValue:NSNumber?
    var itemTitle:String?

}
extension Comic: Mappable {

    init?(map: Map) {

    }
    mutating func mapping(map: Map) {


        comicId <- map["comicId"]
        wideCover <- map["wideCover"]
        tags <- map["tags"]
        subTitle <- map["subTitle"]
        description <- map["description"]
        cornerInfo <- map["cornerInfo"]
        short_description <- map["short_description"]
        author_name <- map["author_name"]
        is_vip <- map["is_vip"]

        cover <- map["cover"]
        name <- map["name"]
        argName <- map["argName"]
        argValue <- map["argValue"]
        itemTitle <- map["itemTitle"]
    }
}

extension GalleryItem: Mappable {

    init?(map: Map) {

    }
    mutating func mapping(map: Map) {
        linkType <- map["linkType"]
        cover <- map["cover"]
        id <- map["id"]
        title <- map["title"]
        content <- map["content"]
    }
}

extension RecommendList: Mappable {

    init?(map: Map) {

    }
    mutating func mapping(map: Map) {

        stateCode <- map["stateCode"]
        message <- map["message"]
        returnData <- map["returnData"]
    }
}


extension RecommendData: Mappable {

    init?(map: Map) {

    }
    mutating func mapping(map: Map) {
        galleryItems <- map["galleryItems"]
        textItems <- map["textItems"]
        comicLists <- map["comicLists"]
        editTime <- map["editTime"]
    }
}

extension RecommendListModel: Mappable {

    init?(map: Map) {

    }
    mutating func mapping(map: Map) {
        code <- map["code"]
        data <- map["data"]
    }
}



extension ComicList: Mappable {

    init?(map: Map) {

    }

    mutating func mapping(map: Map) {

        comicType <- map["comicType"]
        comics <- map["comics"]
        canedit <- map["canedit"]
        sortId <- map["sortId"]
        titleIconUrl <- map["titleIconUrl"]
        newTitleIconUrl <- map["newTitleIconUrl"]
        description <- map["description"]
        argName <- map["argName"]
        argValue <- map["argValue"]
        argType <- map["argType"]
    }
}

有兴趣学习的可以加群一起来探讨: 里面也有一些开发相关的PDF文档

群号:727323882

你可能感兴趣的:(iOS RxSwift+Moya+ObjectMapper实现网络请求数据与解析)