Alamofire的二次封装

2,107 阅读6分钟

Alamofire的二次封装

最近开始在学习Swift,在网络工具的选择中,我选择了AFNetworking的Swift版本--->Alamofire,为了更加方便的项目的开发,我对Alamofire做了一些封装, 话不多说 直接上码

工具类属性定义

//网络请求方式的Key
let BWRequestMethodKey = "BWRequestMethodKey"

//网络请求成功的回调
typealias BWNetworkSuccess = (_ isSuccess: Bool, _ code: String, _ hint: String, _ list: AnyObject, _ responseData: AnyObject) -> Void
//网络请求失败的回调
typealias BWNetworkFail = (_ error: AnyObject) -> Void
//网络状态
typealias BWNetworkStatusBlock = (_ networkStatus: UInt32) -> Void

enum BWNetworkStatus: Int32 {
    case unkonw             = -1    //未知网络
    case notReachable       = 0     //网络无连接
    case wwan               = 1     //2,3,4G网络
    case wifi               = 2     //wifi网络
}

class BWNetworkingService: NSObject {
    
    //MARK: var & let
    
    static let shareService  = BWNetworkingService()
    //记录当前请求路由
    private var currentRequestUrl: String = ""
    //记录当前请求方式
    private var currentRequestMethod: HTTPMethod = .get
    //记录当前请求接口参数
    private var currentParams: Dictionary<String,Any>?
    //记录当前接口是否需要缓存到本地
    private var shouldCache: Bool = false
    //超时时间
    private let requestTimeout: TimeInterval = 20
    //获取资源超时时间
    private let resourceTimeout: TimeInterval = 20
    //header
    private lazy var httpHeader: [String: Any] = {
        let header : HTTPHeaders = SessionManager.defaultHTTPHeaders
//        header["Authorization"] = ""
//        header.updateValue("application/json", forKey: "Accept")
        return header
    }()
    //数据缓存路径
    private let cachePath = NSHomeDirectory() + "/Documents/NetworkingCaches/"
    //当前网络状态
    private var networkStatus: BWNetworkStatus = .wifi
    //sessionManager
    private lazy var sessionManager: SessionManager = {
        #warning("此处换成其他的SessionManager在任务组中处理时第一次请求会报cancel错误,暂时换成default,以后再处理此处")
        return SessionManager.default
        
//        let configuration = URLSessionConfiguration.default
//        configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders
//        configuration.timeoutIntervalForRequest = self.requestTimeout
//        configuration.timeoutIntervalForResource = self.resourceTimeout
//        return SessionManager(configuration: configuration)
    }()

基础请求

 //MARK: 基础网络请求
    
    /// 基础网络请求
    ///
    /// - Parameters:
    ///   - url: 请求链接
    ///   - method: 请求方式
    ///   - shouldCache: 是否需要缓存此处数据
    ///   - success: 请求成功回调
    ///   - fail: 请求失败回调
    public func baseRequestWith(url: String,
                                method: HTTPMethod,
                                shouldCache: Bool,
                                params: [String : Any]?,
                                success: @escaping BWNetworkSuccess,
                                fail: @escaping BWNetworkFail) {
        var urlPath: String = url
        //拼接域名
        urlPath = CWAppConfig.shared.routerUrl + urlPath
        if networkStatus.rawValue == BWNetworkStatus.unkonw.rawValue || networkStatus.rawValue == BWNetworkStatus.notReachable.rawValue {
            if shouldCache == true {
                //从缓存中取出此处的数据
                let cacheData = self.cacheDataFrom(urlPath: urlPath)
                if cacheData != nil {
                    //数据处理
                    self.fetchData(cacheData as AnyObject, success)
                }
                if self.networkStatus == .notReachable || self.networkStatus == .unkonw {
                    return;
                }
            }
        }
        //清除空格
        if urlPath.contains(" ") {
            urlPath = urlPath.replacingOccurrences(of: " ", with: "")
        }
        var tempParams = params
        if tempParams == nil {
            tempParams = [String: Any]()
        }
        //设置token
        tempParams?["token"] = USERINFO.token ?? ""
        self.currentRequestUrl = urlPath
        self.currentRequestMethod = method
        self.currentParams = tempParams
        self.shouldCache = shouldCache
        BWLog("当前请求接口:\(self.currentRequestUrl) 请求参数:\(String(describing: self.currentParams))")
        //从服务器获取数据
        self.sessionManager.request(self.currentRequestUrl, method: self.currentRequestMethod, parameters: self.currentParams, encoding: URLEncoding.default, headers: nil).responseJSON { (responseData) in
            switch responseData.result {
            case .success:
                if let value = responseData.result.value as? [String : Any] {
                    //是一个字典  此处可自定义一些处理 (判断接口是否返回了正确数据 接口是否报错 接口报错类型 是否需要重新登录。。。)
                    self.fetchData(value as AnyObject, success)
                    let resultDic: [String: AnyObject] = value as [String: AnyObject]
                    let code: String = resultDic["code"] as! String
                    let isSuccess = code == "40000"
                    if code == "40004" {
                        //删除用户信息 重新登录
                        USERINFO.removeLocalUserInfo()
                        CWLoginHandle.showLoginVC()
                    }
                    //判断是否需要缓存数据
                    if shouldCache == true && isSuccess == true {
                        //缓存当前接口返回的数据
                        self.cacheResponseDataWith(responseData: value as AnyObject, urlPath: urlPath)
                    }
                }
            case .failure(let error):
                fail(error as AnyObject)
                debugPrint("接口请求失败--\(error)")
            }
        }
    }
    
    //数据第一次处理
    func fetchData(_ responseData: AnyObject, _ success: BWNetworkSuccess) {
        let resultDic: [String: AnyObject] = responseData as! [String: AnyObject]
        let code: String = resultDic["code"] as! String
        let hint: String = resultDic["hint"] as! String
        let list: AnyObject = resultDic["list"] as AnyObject
        let isSuccess: Bool = code == "40000"
        success(isSuccess,code,hint,list,responseData as AnyObject)
    }

GET & POST

 //MARK: GET请求--不带接口缓存
    
    /// GET请求
    ///
    /// - Parameters:
    ///   - url: 请求链接
    ///   - params: 请求参数
    ///   - success: 成功的回调
    ///   - fail: 失败的回调
    public func requestGetWith(url: String,
                               params: [String: Any]?,
                               success: @escaping BWNetworkSuccess,
                               fail: @escaping BWNetworkFail) {
        self.requestGetWith(url: url, params: params, shouldCache: false, success: success, fail: fail)
    }
    
    //MARK: POST请求--不带接口缓存
    
    /// POST请求
    ///
    /// - Parameters:
    ///   - url: 请求链接
    ///   - params: 请求参数
    ///   - success: 成功的回调
    ///   - fail: 失败的回调
    public func requestPostWith(url: String,
                                params: [String: Any]?,
                                success: @escaping BWNetworkSuccess,
                                fail: @escaping BWNetworkFail) {
        self.requestPostWith(url: url, params: params, shouldCache: false, success: success, fail: fail)
    }
    
    //MARK: GET请求--携带是否缓存参数
    
    /// GET请求---携带是否缓存参数
    ///
    /// - Parameters:
    ///   - url: 请求路由
    ///   - params: 请求参数
    ///   - shouldCache: 是否需要缓存数据
    ///   - success: 成功的回调
    ///   - fail: 失败的回调
    public func requestGetWith(url: String,
                               params: [String: Any]?,
                               shouldCache: Bool,
                               success: @escaping BWNetworkSuccess,
                               fail: @escaping BWNetworkFail) {
        self.baseRequestWith(url: url, method: .get, shouldCache: shouldCache, params: params, success: success, fail: fail)
    }
    
    //MARK: POST请求---携带是否缓存参数
    
    /// POST请求---携带是否缓存参数
    ///
    /// - Parameters:
    ///   - url: 请求路由
    ///   - params: 请求参数
    ///   - shouldCache: 是否需要缓存数据
    ///   - success: 成功的回调
    ///   - fail: 失败的回调
    public func requestPostWith(url: String,
                                params: [String: Any]?,
                                shouldCache: Bool,
                                success: @escaping BWNetworkSuccess,
                                fail: @escaping BWNetworkFail) {
        self.baseRequestWith(url: url, method: .post, shouldCache: shouldCache, params: params, success: success, fail: fail)
    }

并发请求多接口---返回接口以已传入的urlPath作为key存在字典中

/// 异步多接口请求--返回的数据由[urlPath: responseData]的字典数组形式按传入mutableUrls中的顺序返回
    ///
    /// - Parameters:
    ///   - mutableUrls: 多个请求接口
    ///   - params: 各接口请求参数  如果是POST请求 一定要传BWRequestMethodKey=.posto 否则将当做get请求处理
    ///   - completionHandle: 回调
    public func asyncMutablRequestWith(mutableUrls: [String],
                                       shouldCache: Bool,
                                       params:[Dictionary<String, Any>]?,
                                       completionHandle: @escaping ([String: Any]) -> Void) {
        if params != nil && params?.count ?? 0 > 0 {
            //抛出异常  参数字典数组(params)与请求路由数组(mutableUrls)数量需一致
            if params?.count != mutableUrls.count {
                 assert(false, "参数字典数组(params)与请求路由数组(mutableUrls)数量需一致")
            }
        }
        //创建字典于接收服务器返回的数据
        var resultDictionary: Dictionary = [String: AnyObject].init()
        //创建调度组
        let dispatchGroup = DispatchGroup.init()
        //创建并发队列
        let concurrentQueue = DispatchQueue.init(label: "com.mutableNetwork.xiongbenwan", qos: .default, attributes: .concurrent, autoreleaseFrequency: .inherit, target: nil)
        for (index, url) in mutableUrls.enumerated() {
            var urlPath = url
            if urlPath.contains(" ") {
                //清除urlPath中的空格
                urlPath = urlPath.replacingOccurrences(of: " ", with: "")
            }
            if urlPath.count <= 0 {
                //抛出异常 传入的url不能为空字符串
                assert(false, "下标为\(index)的url不能为空字符串")
            }
            var paramDic: Dictionary<String, Any>?
            var httpMethod: HTTPMethod = .get
            if params != nil && params?.count ?? 0 > 0 {
                 paramDic = params?[index]
                if paramDic != nil {
                    //取出字典中的请求方式的value  如果没有 则默认为GET请求
                    let requestMethod: HTTPMethod? = paramDic![BWRequestMethodKey] as? HTTPMethod
                    if requestMethod == HTTPMethod.post {
                        httpMethod = .post
                    }
                }
            }
            dispatchGroup.enter()
            concurrentQueue.async {
                self.baseRequestWith(url: urlPath, method: httpMethod, shouldCache: shouldCache, params: paramDic, success: { (isSuccess, code, hint, list, responseData) in
                    //将服务器返回的数据按照url:data的形式放在字典中
                    resultDictionary[urlPath] = responseData
                    dispatchGroup.leave()
                }, fail: { (error) in
                    resultDictionary[urlPath] = error
                    dispatchGroup.leave()
                })
            }
        }
        //线程通知的方式等待任务组完成---回到主线程中处理数据
        dispatchGroup.notify(queue: DispatchQueue.main) {
            completionHandle(resultDictionary)
        }
    }

数据缓存相关

extension BWNetworkingService {
    
    /// 从缓存中获取数据
    ///
    /// - Parameter urlPath: 请求路由
    /// - Returns: 缓存数据
    public func cacheDataFrom(urlPath: String) -> Any? {
        //对请求路由base64编码
        let base64Path = urlPath.md5String()()
        //拼接缓存路径
        var directorPath: String = cachePath
        directorPath.append(base64Path)
        let data: Data? = FileManager.default.contents(atPath: directorPath)
        let jsonData: Any?
        if data != nil {
            print("从缓存中获取数据:\(directorPath)")
            do {
                jsonData = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers)
                return jsonData
            } catch {
                return data
            }
        }
        return nil
    }

    /// 将接口请求成功的数据缓存到本地
    ///
    /// - Parameters:
    ///   - responseData: 服务器返回的数据
    ///   - urlPath: 请求路由
    ///   - params: 请求参数
    public func cacheResponseDataWith(responseData: AnyObject,
                                      urlPath: String) {
        var directorPath: String = cachePath
        //创建目录
        createRootCachePath(path: directorPath)
        //对请求的路由进行base64编码
        let base64Path = urlPath.md5String()
        //拼接路径
        directorPath.append(base64Path)
        //将返回的数据转换成Data
        var data: Data?
        do {
            try data = JSONSerialization.data(withJSONObject: responseData, options: .prettyPrinted)
        } catch {
            
        }
        //将data存储到指定的路径
        if data != nil {
            let cacheSuccess = FileManager.default.createFile(atPath: directorPath, contents: data, attributes: nil)
            if cacheSuccess == true {
                debugPrint("当前接口缓存成功:\(directorPath)")
            } else {
                debugPrint("当前接口缓存失败:\(directorPath)")
            }
        }
    }
    
    //新建数据缓存路径
    func createRootCachePath(path: String) {
        if !FileManager.default.fileExists(atPath: path, isDirectory: nil) {
            do {
                try FileManager.default.createDirectory(atPath: path, withIntermediateDirectories: true, attributes: nil)
            } catch let error {
                debugPrint("create cache dir error:" + error.localizedDescription + "\n")
                return
            }
        }
    }
}

网络状态监听

extension BWNetworkingService {
    
    /// 网络状态监听
    public func monitorNetworkingStatus() {
        let reachability = NetworkReachabilityManager()
        reachability?.startListening()
        reachability?.listener = { [weak self] status in
            guard let weakSelf = self else { return }
            if reachability?.isReachable ?? false {
                switch status {
                case .notReachable:
                    weakSelf.networkStatus = BWNetworkStatus.notReachable
                case .unknown:
                    weakSelf.networkStatus = BWNetworkStatus.unkonw
                case .reachable(.wwan):
                    weakSelf.networkStatus = BWNetworkStatus.wwan
                case .reachable(.ethernetOrWiFi):
                    weakSelf.networkStatus = BWNetworkStatus.wifi
                }
            } else {
                weakSelf.networkStatus = BWNetworkStatus.notReachable
            }
        }
    }
}

收工!!!