Alamofire结合SwiftyJSON、ObjectMapper二次封装网络请求
Alamofire
AFNetworking的作者Matt Thompson 提出了一个新的类似AFNetworking的网络基础库,并且专门使用最新的Swift语言写的,名为 Alamofire.
支持多种网络请求方式:
public enum Method: String {
case OPTIONS, GET, HEAD, POST, PUT, PATCH, DELETE, TRACE, CONNECT
}
本文基于Alamofire结合ObjectMapper、SwiftyJSON封装出来的一套网络请求体系,告别繁琐的书写格式。
直接上代码吧
1.请求方法代码
需要的头文件和block
import UIKit
import Alamofire
import SwiftyJSON
import ObjectMapper
import MBProgressHUD
//成功
typealias NetSuccessBlock<T: Mappable> = (_ value: YBaseModel<T>, JSON) -> Void
//失败
typealias NetFailedBlock = (AFSErrorInfo) -> Void
typealias AFSProgressBlock = (Double) -> Void//进度
单例方法:请求类:
class YRequestManager: NSObject {
private var sessionManager: SessionManager?
static let share = YRequestManager()
override init() {
super.init()
let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForRequest = 20
sessionManager = SessionManager.init(configuration: configuration, delegate: SessionDelegate.init(), serverTrustPolicyManager: nil)
// let delegate =
}
}
YRequestManager扩展请求方法:post、get、upload
暴露给外面调用两个方法
- 普通请求
- 上传文件
extension YRequestManager {
func requestByTargetType<T: Mappable>(targetType: YAPITargetType, model: T.Type, success: @escaping NetSuccessBlock<T>, failed: @escaping NetFailedBlock) -> Void {
let url = targetType.baseUrl + targetType.path
switch targetType.method {
case .get:
self.GET(url: url, param: targetType.pararms, headers: targetType.headers, isShowHUD: targetType.isShowHUD, success: success, failed: failed)
break
case .post:
self.POST(url: url, param: targetType.pararms, headers: targetType.headers, isShowHUD: targetType.isShowHUD, success: success, failed: failed)
break
case .bodyPost:
self.POST(url: url, paramBody: targetType.pararms, headers: targetType.headers, isShowHUD: targetType.isShowHUD, success: success, failed: failed)
break
default:
break
}
}
func uploadByTargetType<T: Mappable>(targetType: YAPITargetType, model: T.Type,progess:@escaping AFSProgressBlock , success: @escaping NetSuccessBlock<T>, failed: @escaping NetFailedBlock) -> Void {
let url = targetType.baseUrl + targetType.path
switch targetType.method {
case .uploadImage:
self.postImage(image: targetType.pararms["image"] as! UIImage, url: url, param: nil, headers: targetType.headers, isShowHUD: targetType.isShowHUD, progressBlock: progess, successBlock: success, faliedBlock: failed)
break
case .uploadMp4:
self.postVideo(video: targetType.pararms["video"] as! Data, url: url, param: nil, headers: targetType.headers, isShow: targetType.isShowHUD, progressBlock: progess, successBlock: success, faliedBlock: failed)
break
default:
break
}
}
}
扩展私有方法,封装请求
extension YRequestManager {
fileprivate func GET<T: Mappable>(url: String, param: Parameters?, headers: HTTPHeaders, isShowHUD: Bool, success: @escaping NetSuccessBlock<T>, failed: @escaping NetFailedBlock) -> Void {
// let encodStr = url.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
if isShowHUD {
let keyViewController = UIApplication.shared.keyWindow?.rootViewController
if (keyViewController != nil) {
MBProgressHUD.showAdded(to: keyViewController!.view, animated: true)
}
}
self.sessionManager?.request(url, method: .get, parameters: param, encoding: URLEncoding.httpBody , headers: headers).validate().responseJSON(completionHandler: { (response) in
let keyViewController = UIApplication.shared.keyWindow?.rootViewController
if (keyViewController != nil) {
MBProgressHUD.hide(for: keyViewController!.view, animated: true)
// MBProgressHUD.
}
self.handleResponse(response: response, successBlock: success, faliedBlock: failed)
})
}
fileprivate func POST<T: Mappable>(url: String, param: Parameters?, headers: HTTPHeaders, isShowHUD: Bool, success: @escaping NetSuccessBlock<T>, failed: @escaping NetFailedBlock) -> Void {
//let encodStr = url.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed);
// let headers: HTTPHeaders = ["Content-Type":"application/json;charset=utf-8"];
// http
if isShowHUD {
let keyViewController = UIApplication.shared.keyWindow?.rootViewController
if (keyViewController != nil) {
MBProgressHUD.showAdded(to: keyViewController!.view, animated: true)
}
}
self.sessionManager!.request(url, method: HTTPMethod.post, parameters: param, encoding: URLEncoding.httpBody, headers: headers)
.validate()
.responseJSON(completionHandler: { (response) in
let keyViewController = UIApplication.shared.keyWindow?.rootViewController
if (keyViewController != nil) {
MBProgressHUD.hide(for: keyViewController!.view, animated: true)
// MBProgressHUD.
}
self.handleResponse(response: response, successBlock: success, faliedBlock: failed)
})
}
// 内容放在body里面
func POST<T: Mappable>(url: String, paramBody: Dictionary<String, Any>?, headers: HTTPHeaders, isShowHUD: Bool, success: @escaping NetSuccessBlock<T>, failed: @escaping NetFailedBlock) -> Void {
//let encodStr = url.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed);
if isShowHUD {
let keyViewController = UIApplication.shared.keyWindow?.rootViewController
if (keyViewController != nil) {
MBProgressHUD.showAdded(to: keyViewController!.view, animated: true)
}
}
let json = JSON.init(paramBody as Any)
let urlReqest = URL.init(string: url)
var request = URLRequest.init(url: urlReqest!)
request.httpMethod = HTTPMethod.post.rawValue
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = json.description.data(using: .utf8)
// http
self.sessionManager!.request(request)
.validate()
.responseJSON(completionHandler: { (response) in
self.handleResponse(response: response, successBlock: success, faliedBlock: failed)
let keyViewController = UIApplication.shared.keyWindow?.rootViewController
if (keyViewController != nil) {
MBProgressHUD.hide(for: keyViewController!.view, animated: true)
// MBProgressHUD.
}
})
}
// 上传图片
func postImage<T: Mappable>(image: UIImage, url: String, param: Parameters?, headers: HTTPHeaders, isShowHUD: Bool, progressBlock: @escaping AFSProgressBlock, successBlock:@escaping NetSuccessBlock<T>,faliedBlock:@escaping NetFailedBlock) {
if isShowHUD {
let keyViewController = UIApplication.shared.keyWindow?.rootViewController
if (keyViewController != nil) {
MBProgressHUD.showAdded(to: keyViewController!.view, animated: true)
}
}
let imageData = image.jpegData(compressionQuality: 0.0001)
let headers = ["content-type":"multipart/form-data"];
self.sessionManager?.upload(multipartFormData: { (multipartFormData) in
//采用post表单上传
// 参数解释
let dataStr = DateFormatter.init()
dataStr.dateFormat = "yyyyMMddHHmmss"
let fileName = "\(dataStr.string(from: Date.init())).png"
multipartFormData.append(imageData!, withName: "file", fileName: fileName, mimeType: "image/jpg/png/jpeg")
}, to: url, headers: headers, encodingCompletion: { (encodingResult) in
let keyViewController = UIApplication.shared.keyWindow?.rootViewController
if (keyViewController != nil) {
MBProgressHUD.hide(for: keyViewController!.view, animated: true)
// MBProgressHUD.
}
switch encodingResult {
case .success(let upload, _, _):
//连接服务器成功后,对json的处理
upload.responseJSON { response in
//解包
self.handleResponse(response: response, successBlock: successBlock, faliedBlock: faliedBlock)
// print("json:\(result)")
}
//获取上传进度
upload.uploadProgress(queue: DispatchQueue.global(qos: .utility)) { progress in
progressBlock(progress.fractionCompleted);
print("图片上传进度: \(progress.fractionCompleted)")
}
break
case .failure(let encodingError):
self.handleRequestError(error: encodingError as NSError, faliedBlock: faliedBlock);
break
}
})
}
func postVideo<T: Mappable>(video: Data, url: String, param: Parameters?,headers: HTTPHeaders,isShow: Bool, progressBlock: @escaping AFSProgressBlock, successBlock:@escaping NetSuccessBlock<T>,faliedBlock:@escaping NetFailedBlock) {
// let headers = ["content-type":"multipart/form-data"];
if isShow {
let keyViewController = UIApplication.shared.keyWindow?.rootViewController
if (keyViewController != nil) {
MBProgressHUD.showAdded(to: keyViewController!.view, animated: true)
}
}
self.sessionManager?.upload(multipartFormData: { (multipartFormData) in
//采用post表单上传
// 参数解释
let dataStr = DateFormatter.init()
dataStr.dateFormat = "yyyyMMddHHmmss"
let fileName = "\(dataStr.string(from: Date.init())).mp4"
multipartFormData.append(video, withName: "file", fileName: fileName, mimeType: "video/mp4");
}, to: url, headers: headers, encodingCompletion: { (encodingResult) in
let keyViewController = UIApplication.shared.keyWindow?.rootViewController
if (keyViewController != nil) {
MBProgressHUD.hide(for: keyViewController!.view, animated: true)
// MBProgressHUD.
}
switch encodingResult {
case .success(let upload, _, _):
//连接服务器成功后,对json的处理
upload.responseJSON { response in
//解包
self.handleResponse(response: response, successBlock: successBlock, faliedBlock: faliedBlock)
// print("json:\(result)")
}
//获取上传进度
upload.uploadProgress(queue: DispatchQueue.global(qos: .utility)) { progress in
progressBlock(progress.fractionCompleted);
print("图片上传进度: \(progress.fractionCompleted)")
}
break
case .failure(let encodingError):
self.handleRequestError(error: encodingError as NSError, faliedBlock: faliedBlock);
break
}
})
}
}
请求数据返回响应方法
extension YRequestManager {
/** 处理服务器响应数据*/
private func handleResponse<T: Mappable>(response:DataResponse<Any>, successBlock: NetSuccessBlock<T> ,faliedBlock: NetFailedBlock){
if let error = response.result.error {
// 服务器未返回数据
self.handleRequestError(error: error as NSError , faliedBlock: faliedBlock)
}else if let value = response.result.value {
// 服务器又返回数h数据
if (value as? NSDictionary) == nil {
// 返回格式不对
self.handleRequestSuccessWithFaliedBlcok(faliedBlock: faliedBlock)
}else{
self.handleRequestSuccess(value: value, successBlock: successBlock, faliedBlock: faliedBlock);
}
}
}
/** 处理请求失败数据*/
private func handleRequestError(error: NSError, faliedBlock: NetFailedBlock){
var errorInfo = AFSErrorInfo();
errorInfo.code = error.code;
errorInfo.error = error;
if ( errorInfo.code == -1009 ) {
errorInfo.message = "无网络连接";
}else if ( errorInfo.code == -1001 ){
errorInfo.message = "请求超时";
}else if ( errorInfo.code == -1005 ){
errorInfo.message = "网络连接丢失(服务器忙)";
}else if ( errorInfo.code == -1004 ){
errorInfo.message = "服务器没有启动";
}else if ( errorInfo.code == 404 || errorInfo.code == 3) {
}
faliedBlock(errorInfo)
}
/** 处理请求成功数据*/
private func handleRequestSuccess<T: Mappable>(value:Any, successBlock: NetSuccessBlock<T>,faliedBlock: NetFailedBlock){
let json: JSON = JSON(value);
let baseModel = YBaseModel<T>.init(JSONString: json.description)
if baseModel?.status == 0 {
successBlock(baseModel!, json)
} else if baseModel?.status == 11 || baseModel?.status == 12 { // 获取服务器返回失败原因
var errorInfo = AFSErrorInfo();
errorInfo.code = baseModel!.status;
errorInfo.message = (baseModel?.msg)!;
faliedBlock(errorInfo);
}
}
/** 服务器返回数据解析出错*/
private func handleRequestSuccessWithFaliedBlcok(faliedBlock:NetFailedBlock){
var errorInfo = AFSErrorInfo();
errorInfo.code = -1;
errorInfo.message = "数据解析出错";
}
}
AFSErrorInfo结构体 和 YAPITargetType协议protocol
/** 访问出错具体原因 */
struct AFSErrorInfo {
var code = 0
var message = ""
var error = NSError()
}
public protocol YAPITargetType {
var method: RequestMed {get}
var baseUrl: String { get }
var path: String { get }
var pararms: Dictionary<String, Any>{get}
var headers: HTTPHeaders {get}
var isShowHUD: Bool {get}
}
public enum RequestMed: Int {
case post = 0
case get = 1
case bodyPost = 2
case uploadImage = 3
case uploadMp4 = 4
}
baseModel代码
YBaseModel.swift
//
// YBaseModel.swift
// YHuadingSwift
//
// Created by bruce yao on 2019/1/26.
// Copyright © 2019 bruce yao. All rights reserved.
//
import UIKit
import ObjectMapper
struct YBaseModel<T: Mappable>: Mappable {
var status: Int = 100 //编码
var msg: String = ""
var data: [T]? //返回数据
var dataAny: String?
var dataStr: [String]? //返回数据
var dicData: T?//返回数据
var success: Int = 1 // 1 成功
var pageNo: Int?
var totalPages: Int?
var hasNextPage: Bool = false
var total: Int? {
didSet {
if self.total == nil {
self.total = 0
}else {
if self.total! % 10 == 0 {
self.total = Int(self.total! / 10)
}else {
self.total = Int(self.total! / 10) + 1
}
}
}
}
init?(map: Map) {
}
mutating func mapping(map: Map) {
status <- map["status"]
status <- map["Code"]
data <- map["data"]
dataStr <- map["data"]
data <- map["data.data"]
data <- map["Data"]
dataAny <- map["Data"]
dataAny <- map["data"]
dicData <- map["data"]
dicData <- map["data.data"]
dicData <- map["Data"]
hasNextPage <- map["data.hasNextPage"]
success <- map["success"]
pageNo <- map["data.pageNo"]
totalPages <- map["data.totalPages"]
totalPages <- map["data.data.totalPages"]
total <- map["total"]
total <- map["data.totalRecord"]
msg <- map["msg"]
}
}
struct YNormalModel: Mappable {
init?(map: Map) {
}
init() {
}
mutating func mapping(map: Map) {
}
}
实例1
api文件:
//
// YSendArticelAPI.swift
// YHmallNews
//
// Created by bruce yao on 2019/4/28.
// Copyright © 2019 bruce yao. All rights reserved.
//
import UIKit
import Alamofire
import ObjectMapper
enum YSendArticelAPI {
case sendArticel(params:Dictionary<String, Any>)
case sendArticel3(params:Dictionary<String, Any>)
}
extension YSendArticelAPI: YAPITargetType {
var method: RequestMed {
switch self {
case .sendArticel(_):
return .bodyPost
default:
return .get
}
}
var modelType: Mappable.Type {
return YNormalModel.self
}
var baseUrl: String {
switch self {
case .sendArticel(_):
return HD_Search_Base
default:
return HD_Search_Base
}
}
var path: String {
switch self {
case .sendArticel(_):
return "manage/xtx/addXtxArticle"
default:
return HD_Search_Base
}
}
var pararms: Dictionary<String, Any> {
switch self {
case .sendArticel(let parprms):
return parprms
default:
return [:]
}
}
var headers: HTTPHeaders {
return ["Content-Type":"application/json;charset=utf-8"]
}
var isShowHUD: Bool {
return true
}
}
调用页面:
let dic = ["name": "xiaoLi", "age": 1]
YRequestManager.share.requestByTargetType(targetType: YSendArticelAPI.sendArticel(params: dic), model: YNormalModel.self, success: { [weak self](baseModel, json) in
debugPrint(baseModel)
self!.showTipsWithHUDDefaultTime(text: "发布成功")
self?.navigationController?.popViewController(animated: true)
}) { [weak self](error) in
debugPrint(error)
self!.showTipsWithHUDDefaultTime(text: "请求出错")
}
说明
- API 说明:如果是postBody请求,请求的参数会直接转化为data数据,发给服务器
- YAPITargetType协议,目的抽象出来网络层,不用把请求方式啊以及URL、参数、统统写在Controller。也是为了给Controller瘦身,一定程度上分化啦代码结构
- 结合SwiftyJSON、ObjectMapper直接把数据转化为Model对象输出、简化繁琐的json转model代码逻辑