基於Alamofire的網絡封裝

2019/12/16更新 修改重試邏輯 說明:雖然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;
        }

複製代碼
相關文章
相關標籤/搜索