抓取的是有妖气的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文档