说明:虽然Alamofire内置了RequestRetrier协议帮助咱们实现重试,可是呢,基于下载以及提交时,服务器获取到咱们的提交的内容可是暂时没法处理的状况下,继续重试可能会形成错误,所以咱们须要本身去对于重试的触发作实现,emmm....😊就行了
咱们做为开发,可能从一个项目到另外一个项目的时候,避免不了要把一些通用的工具好比(UIKit,Foundation等一些扩展又要写一遍),那么网络操做也是如此。你可能会说:😳瓜,我使用的是三方库Alamofire,AFN...,可是你有没有意识到你的各类处理业务的代码中充斥着Alamofire./AFNetworking.,这不只让你不爽,估计你的小伙伴也不会很开心。那么咱们的目的就是对于三方库进行再一次封装,一方面尽可能减小这种代码对于业务的影响(代码量),还有就是减小由于三方库的更新等对咱们项目的影响,咱们要作的只是修改咱们封装的那部分代码
###上菜以前的说明 封装的这个代码仅仅是结合本公司以及我的一点点经验作出来的,不足的地方但愿大佬多提意见,后面我也会不足的地方提出来。git
swift语言开发整理的工具三方库,包括 字符串 日期 校验 http请求 网格菜单 列表菜单 钥匙串 本地图片浏览 在线图片浏览 设备信息 基础控制器封装(tableview scrollview collectionview wkwebview) 本地推送 基础视图(根据提供key value,生成view) 选择器 分页视图 seachcontroller+tableview AVPlayer 弹出的筛选框。 ❤️ 传送门 ❤️ 喜欢点个赞吧~github
import Foundation
import Alamofire
import SwiftyJSON
//MARK:- 获取须要数据节点名称
public enum DataKey: String {
case all = "all"
case dataMap = "dataMap"
case dataList = "dataList"
case listDataMap = "listDataMap"
}
//MARK:- 错误码
//public enum ErrorCode{
//200...299
// case invalidResponse(String)
//actionResult中的 ’success‘为false
// case sysError(String)
//其余错误 如 ‘failure’ 返回HTTP状态码 以及 错误信息描述
// case networkUnavailable(String?,Int?)
//}
//MARK:- 错误码
public enum ErrorCode{
//200...299
case invalidResponse(String)
//actionResult中的 ’success‘为false
case sysError(String)
//其余错误 如 ‘failure’ 返回HTTP状态码 以及 错误信息描述
case networkUnavailable(String?,Int?)
//重试
/** @param URLRequest 失败的request
* @param NetWorkType 请求类型
* @param DataKey 获取数据的参数
* @param String 错误信息
*/
case needRetrier(URLRequest,NetWorkType,DataKey?,String)
/**
* 上传失败 回调参数
* @param String 请求地址
* @param [String] 参数
* @param JSON 整个数据
* @param [Data] 数据信息
* @param [String] 数据信息描述
* @param String? 错误信息
*/
case uploadError(String,[String],JSON,[Data],[String],String?)
}
public typealias Success<T> = (T) -> Void
public typealias Failure = (ErrorCode) -> Void
复制代码
2019/12/16修改 ErrorCode的枚举类型修改
重试须要咱们拿到对应的request,对于表单提交咱们须要拿到对应的参数
解释一下:上面的DataKey
是后台返回的数据后咱们想要的部分,好比咱们只想要返回数据的dataMap
里面的东西,那么咱们就指定dataMap好了,这么作的好处是不须要一遍遍硬写这个参数,不足的地方是可能枚举不全
,下面这个是错误码的处理,针对存在Alamofire中存在的错误,咱们须要扔出去的东西,我的感受这个地方处理的还不是很好。线面就是成功和失败的闭包,success<T>
代表他返回的多是任何类型,多是Json,多是Bool值等,看您心情。web
###基本请求(不要以为多/不要以为多/不要以为多 😝😝 )json
public final class NetWorkTools {
private init() {}
static let shared = NetWorkTools()
var sessionManger: SessionManager = {
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 30
let manger = Alamofire.SessionManager(configuration: config)
return manger
}()
}
extension NetWorkTools {
/**
* 基本请求方法
* @param url 请求地址
* @param param 参数
* @param method post get
* @param dataKey 获取的数据部分 例如 data 下面的 ‘dataList’部分
* @param headers 请求头
* @param isNeedReConnect 须要重连 默认true 重连3次
* @param success 成功回调
* @param failure 失败回调
*/
public static func getNormalRequestWith(url: String,
param: Parameters,
networkType:NetWorkType,
method: HTTPMethod = .post,
dataKey: DataKey = .all,
success: @escaping Success<JSON>,
failure: @escaping Failure) {
var headers: [String:String] {
if let headerValue = UserDefaults.standard.value(forKey: networkStaticHeaderKey) as? String {
return [Authorization : headerValue]
}
return [:]
}
shared.sessionManger.request(url, method: method, parameters: param, encoding: URLEncoding.default, headers: headers)
.validate() //200...299
.responseJSON { (response) in
switch response.result {
case .success:
if let data = response.data{
let actionReuslt = JSON(data)[ConstantsHelp.actionReuslt]
if actionReuslt[ConstantsHelp.success].boolValue {
let json = JSON(data)[dataKey.rawValue]
//MARK:-存token
if !JSON(data)[ConstantsHelp.dataMap][token].stringValue.isEmpty {
UserDefaults.standard.set(JSON(data)[ConstantsHelp.dataMap][token].stringValue, forKey: networkStaticHeaderKey)
}
success(json)
} else {
let message = (actionReuslt[ConstantsHelp.message].stringValue)
failure(.sysError(message))
}
}
case .failure(let error):
if let code = response.response?.statusCode {
if code == 500 || code == 502 || code == 503 || code == 504{
if let request = response.request {
failure(.needRetrier(request, networkType, dataKey, error.localizedDescription))
}
} else {
failure(.sysError(error.localizedDescription))
}
} else {
failure(.sysError(error.localizedDescription))
}
}
}
}
}
复制代码
针对Http状态码下的 500/502/503/504扔出来了重试,具体看本身的业务需求
基本的请求和下载/上传处理的错误时不一样的 基本的请求和下载/上传处理的错误时不一样的 基本的请求和下载/上传处理的错误时不一样的 具体下面的所有代码贴出来了 能够本身看一下
swift
###前方高能 所有代码:api
import Foundation
import Alamofire
import SwiftyJSON
//MARK: - headers
let networkStaticHeaderKey = "topscommToken"
let Authorization = "Authorization"
let token = "token"
//MARK:- 获取须要数据节点名称
public enum DataKey: String {
case all = "all"
case dataMap = "dataMap"
case dataList = "dataList"
case listDataMap = "listDataMap"
case actionResult = "actionResult"
case none = "none"
}
//MARK:-网络请求类型
public enum NetWorkType {
case normalRequest
case upload
case download
}
//MARK:- 错误码
public enum ErrorCode{
//200...299
case invalidResponse(String)
//actionResult中的 ’success‘为false
case sysError(String)
//其余错误 如 ‘failure’ 返回HTTP状态码 以及 错误信息描述
case networkUnavailable(String?,Int?)
//重试
/** @param URLRequest 失败的request
* @param NetWorkType 请求类型
* @param DataKey 获取数据的参数
* @param String 错误信息
*/
case needRetrier(URLRequest,NetWorkType,DataKey?,String)
/**
* 上传失败 回调参数
* @param String 请求地址
* @param [String] 参数
* @param JSON 整个数据
* @param [Data] 数据信息
* @param [String] 数据信息描述
* @param String? 错误信息
*/
case uploadError(String,[String],JSON,[Data],[String],String?)
}
public typealias Success<T> = (T) -> Void
public typealias Failure = (ErrorCode) -> Void
public final class NetWorkTools {
private init() {}
static let shared = NetWorkTools()
var sessionManger: SessionManager = {
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 30
let manger = Alamofire.SessionManager(configuration: config)
return manger
}()
}
extension NetWorkTools {
/**
* 基本请求方法
* @param url 请求地址
* @param param 参数
* @param method post get
* @param dataKey 获取的数据部分 例如 data 下面的 ‘dataList’部分
* @param headers 请求头
* @param isNeedReConnect 须要重连 默认true 重连3次
* @param success 成功回调
* @param failure 失败回调
*/
public static func getNormalRequestWith(url: String,
param: Parameters,
networkType:NetWorkType,
method: HTTPMethod = .post,
dataKey: DataKey = .all,
success: @escaping Success<JSON>,
failure: @escaping Failure) {
var headers: [String:String] {
if let headerValue = UserDefaults.standard.value(forKey: networkStaticHeaderKey) as? String {
return [Authorization : headerValue]
}
return [:]
}
shared.sessionManger.request(url, method: method, parameters: param, encoding: URLEncoding.default, headers: headers)
.validate() //200...299
.responseJSON { (response) in
switch response.result {
case .success:
if let data = response.data{
let actionReuslt = JSON(data)[ConstantsHelp.actionReuslt]
if actionReuslt[ConstantsHelp.success].boolValue {
let json = JSON(data)[dataKey.rawValue]
//MARK:-存token
if !JSON(data)[ConstantsHelp.dataMap][token].stringValue.isEmpty {
UserDefaults.standard.set(JSON(data)[ConstantsHelp.dataMap][token].stringValue, forKey: networkStaticHeaderKey)
}
success(json)
} else {
let message = (actionReuslt[ConstantsHelp.message].stringValue)
failure(.sysError(message))
}
}
case .failure(let error):
if let code = response.response?.statusCode {
if code == 500 || code == 502 || code == 503 || code == 504{
if let request = response.request {
failure(.needRetrier(request, networkType, dataKey, error.localizedDescription))
}
} else {
failure(.sysError(error.localizedDescription))
}
} else {
failure(.sysError(error.localizedDescription))
}
}
}
}
/**
* 基本上传方法
* @param url 请求地址
* @param keys 参数
* @param parameters 整个数据
* @param datasArr 数据信息
* @param datasInfoArr 数据信息描述
* @param isNeedReConnect 须要重连 默认true 重连3次
* @param success 成功回调
* @param failure 失败回调
*/
public static func uploadRequestWith(url: String,
keys: [String],
parameters: JSON,
datasArr:[Data],
dataKey: DataKey = .actionResult,
datasInfoArr:[String],
networkType:NetWorkType,
success: @escaping Success<JSON>,
failure: @escaping Failure){
var headers: [String:String] {
if let headerValue = UserDefaults.standard.value(forKey: networkStaticHeaderKey) as? String {
return [Authorization : headerValue]
}
return [:]
}
shared.sessionManger.upload(multipartFormData: { (multipartFormData) in
//拼接数据
var count = datasArr.count
count = datasArr.count <= datasInfoArr.count ? datasArr.count:datasInfoArr.count
for index in 0..<count {
let data = datasArr[index]
let withName = !VerifyHelp.checkImageInfo(imageName: datasInfoArr[index]) ? "withName" + String(index) + data.getImageFormat()!: datasInfoArr[index]
let fileName = !VerifyHelp.checkImageInfo(imageName: datasInfoArr[index]) ? "fileName" + String(index) + data.getImageFormat()! : datasInfoArr[index]
multipartFormData.append(data, withName: withName, fileName: fileName, mimeType: "application/octet-stream")
}
if keys.count > 0{
for value in keys{
let data:Data = parameters[value].stringValue.data(using: String.Encoding(rawValue: String.Encoding.utf8.rawValue))!
multipartFormData.append(data, withName:value)
}
}
multipartFormData.append(URL(string:"sss")!, withName: "ss", fileName: "ssd", mimeType: "jpeg/jpg")
}, to: url, headers: headers) { (request) in
switch request {
case .success(let upload, _ , _):
upload.responseJSON { (response) in
//是否存在错误
if let error = response.error {
if let code = response.response?.statusCode {
if code == 500 || code == 502 || code == 503 || code == 504 || code == 404{
if let request = response.request {
failure(.needRetrier(request, networkType, dataKey, error.localizedDescription))
}
} else {
failure(.sysError(error.localizedDescription))
}
} else {
//offline 无状态码
failure(.networkUnavailable(error.localizedDescription, nil))
}
} else {
//成功
if let data = response.result.value as? [String : AnyObject] {
let actionResult = JSON(data)[ConstantsHelp.actionReuslt]
if actionResult[ConstantsHelp.success].boolValue {
success(actionResult)
} else {
let message = (actionResult[ConstantsHelp.message].stringValue)
failure(.sysError(message))
}
}
}
}
//MARK:- 验证准备上传的数据是否合法
case .failure:
failure(.sysError("上传的数据不合法"))
}
}
}
/**
* 基本下载方法
* @param url 请求地址
* @param isNeedReConnect 须要重连 默认true 重连3次
* @param method HTTPMethod
* @param params Parameters
* @param headers [String:String]
* @param success 成功回调
* @param failure 失败回调
*/
public static func downloadFileWith(url: String,
method: HTTPMethod = .post,
dataKey: DataKey = .none,
params: Parameters,
networkType:NetWorkType,
success: @escaping Success<String>,
failure: @escaping Failure) {
var headers: [String:String] {
if let headerValue = UserDefaults.standard.value(forKey: networkStaticHeaderKey) as? String {
return [Authorization : headerValue]
}
return [:]
}
let destination: DownloadRequest.DownloadFileDestination = { _, response in
let documentsURL = FileManager.default.urls(for: .documentDirectory,in: .userDomainMask)[0]
let fileURL = documentsURL.appendingPathComponent(response.suggestedFilename!)
return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}
shared.sessionManger.download(url, method: method, parameters: params, encoding: URLEncoding.default, headers: headers, to: destination).responseData { (response) in
switch response.result {
case .success:
if let path = response.destinationURL?.path{
if path.hasSuffix("action") {
failure(.sysError("下载的文件不存在"))
} else {
success(path)
}
}else {
if let error = response.error {
if let code = response.response?.statusCode {
if code == 500 || code == 502 || code == 503 || code == 504 || code == 504{
if let request = response.request {
failure(.needRetrier(request, networkType, dataKey, error.localizedDescription))
}
} else {
failure(.sysError(error.localizedDescription))
}
}
}
}
case .failure(let error):
if let code = response.response?.statusCode {
if code == 500 || code == 502 || code == 503 || code == 504 || code == 504{
if let request = response.request {
failure(.needRetrier(request, networkType, dataKey, error.localizedDescription))
}
} else {
failure(.sysError(error.localizedDescription))
}
} else {
failure(.sysError(error.localizedDescription))
}
}
}
}
//MARK:- 重试方法
public static func getRetrierRequest(request:URLRequest,
dataKey: DataKey?,
networkType: NetWorkType,
success: @escaping Success<Any>,
failure: @escaping Failure) {
switch networkType {
case .normalRequest:
shared.sessionManger.request(request).validate().responseJSON { (response) in
//在200...299以外
if let error = response.error {
failure(.invalidResponse(error.localizedDescription))
}
switch response.result {
case .success:
if let data = response.data{
let actionReuslt = JSON(data)[ConstantsHelp.actionReuslt]
if actionReuslt[ConstantsHelp.success].boolValue {
let json = JSON(data)[dataKey!.rawValue]
success(json)
} else {
let message = (actionReuslt[ConstantsHelp.message].stringValue)
failure(.sysError(message))
}
}
case .failure(let error):
if let code = response.response?.statusCode {
if code == 500 || code == 502 || code == 503 || code == 504 {
if let request = response.request {
failure(.needRetrier(request, networkType, dataKey, error.localizedDescription))
}
} else {
failure(.sysError(error.localizedDescription))
}
} else {
failure(.sysError(error.localizedDescription))
}
}
}
case .download:
let destination: DownloadRequest.DownloadFileDestination = { _, response in
let documentsURL = FileManager.default.urls(for: .documentDirectory,in: .userDomainMask)[0]
let fileURL = documentsURL.appendingPathComponent(response.suggestedFilename!)
return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}
shared.sessionManger.download(request, to: destination).validate().responseData { (response) in
switch response.result {
case .success:
if let path = response.destinationURL?.path{
if path.hasSuffix("action") {
failure(.sysError("下载的文件不存在"))
} else {
success(path)
}
}else {
if let error = response.error {
if let code = response.response?.statusCode {
if code == 500 || code == 502 || code == 503 || code == 504 || code == 504{
if let request = response.request {
failure(.needRetrier(request, networkType, dataKey, error.localizedDescription))
}
} else {
failure(.sysError(error.localizedDescription))
}
}
}
}
case .failure(let error):
if let code = response.response?.statusCode {
if code == 500 || code == 502 || code == 503 || code == 504{
if let request = response.request {
failure(.needRetrier(request, networkType, dataKey, error.localizedDescription))
}
} else {
failure(.sysError(error.localizedDescription))
}
} else {
failure(.sysError(error.localizedDescription))
}
}
}
default:
break;
}
复制代码