說明:雖然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;
}
複製代碼