美文网首页
记录Swift网络请求模块封装

记录Swift网络请求模块封装

作者: Mage | 来源:发表于2018-04-13 18:34 被阅读475次

    在使用Swift开发时,网络请求大多使用Alamofire,但使用时不是很方便,于是就计划封装下网络请求模块.
    实现目标:
    1.将通用参数和加密规则封装到底层,不要每次都填写;
    2.将网络请求的通用处理;如发生请求时显示NetworkActivityIndicator等;
    3.网络请求在调用时,尽可能简单,只需要关注需要使用的接口和必要的参数;
    4.支持一些可能出现的额外处理.
    调用的形式需要实现形如:

    UserRouter.user(id:123).request() { result in
       print(result)
    }
    

    一. Routerable

    首先将Request的header,params,url,method以及其他需要接口自定义的内容封装到protocol.由于header是一些固定的属性,所以不加入到协议中

    // MARK: - Routerable
    public protocol Routerable{
        // url, method, param, useCache
        var http : (String, RouterMethod, RouterParam, Bool) {get}
    }
    

    创建Routerable的extesion,将一些通用的属性设置出来,以下方法方便用于HttpManager调用

    // MARK: - 验证相关的信息
    extension Routerable {
        public var baseURL: String {
            return UserDefaults.hostName.value ?? ""
        }
        /// url
        public var url: String {
            return "\(baseURL)/\(http.0)"
        }
        /// method
        public var method: HTTPMethod {
            return http.1
        }
        /// 是否使用缓存
        public var useCache: Bool {
            return http.3
        }
        /// header
        public var headerFields: [String: String]{
            var header: [String:String] = [:]
            header["version"] = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? ""
            header["type"] = "ios"
            return header
        }
        /// parameters
        public var parameters: Parameters {
            var newParams: Parameters = self.http.2
            //添加服务器版本
            newParams["version"] = "1.9"
            // sign验证=
            newParams["sign"] = sign(newParams)
            return newParams
        }
    }
    

    二. HttpManager

    创建HttpManager用于管理HTTP请求

    import Foundation
    import Alamofire
    import SwiftyJSON
    import SVProgressHUD
    
    // MARK: - HTTP请求管理
    public final class HttpManager{
        private init(){}
        // 创建单例
        static let shared = HttpManager()
        // 缓存
        let urlCache = URLCache.init(memoryCapacity: 4*1024*1024, diskCapacity: 20*1024*1024, diskPath: nil)
        
        lazy var alamofireManager: SessionManager = {
            let config = URLSessionConfiguration.default
            config.timeoutIntervalForRequest = 30
            let session = SessionManager(configuration: config)
            return session
        }()
    }
    

    在HttpManager中自定义请求方法

    // MARK: - 基本网络请求
    extension HttpManager{
        /// 通用网络请求
        ///
        /// - Parameters:
        ///   - router: 路由地址
        ///   - queue: 线程
        ///   - completionHandler: 完成的回调,isEnd用于处理带缓存的请求,一般情况会先返回缓存,等请求完成在返回真实数据
        public static func requestResult(router: Routerable, queue: DispatchQueue? = DispatchQueue.global(),isShowError: Bool = true, completionHandler: @escaping (Result<JSON>, Bool) -> Void) -> DataRequest{
            let request = shared.alamofireManager.request(router.url, method: router.method, parameters: router.parameters, encoding: URLEncoding.default, headers: router.headerFields)
            
            var isEnd = false
            if router.useCache {// 如果使用cache
                DispatchQueue.after(queue, 0.4, {
                    if isEnd == false, let dataResponse = request.cacheRequest(router: router) {
                        LogInfo("*******\(router.url)使用缓存*******")
                        completionHandler(dataResponse.result, isEnd)
                    }
                })
            }
            
            // 将要发送请求
            willSend(request)
            request.responseDict(router: router, queue: queue, completionHandler: { response in
                // 保存结果
                response.saveCache(router: router)
                // 请求结束
                isEnd = true
                // 完成请求返回
                completionHandler(response.result, isEnd)
                // 接收到服务端的结果
                didReceive(response, isShowError: isShowError)
            })
            return request
        }
    }
    // MARK: - Default处理
    extension HttpManager {
        // MARK: - 网络指示器
        private static func NetworkActivityIndicatorVisible(_ isVisible: Bool){
            DispatchQueue.main.async {
                UIApplication.shared.isNetworkActivityIndicatorVisible = isVisible
            }
        }
        fileprivate static func willSend(_ request: DataRequest) {
            NetworkActivityIndicatorVisible(true)
        }
        fileprivate static func didReceive(_ response: DataResponse<JSON>, isShowError: Bool = true){
            NetworkActivityIndicatorVisible(false)
            LogInfo(response.debugDescription)
            if case let .failure(error) = response.result, isShowError{
                DispatchQueue.main.async {
                    SVProgressHUD.showError(withStatus: error.localizedDescription)
                }
            }
        }
    }
    

    三.Alamofire+Custom
    根据返回数据的格式,处理通用的错误返回,并打印详细的请求相关的数据用于调试.

    import Foundation
    import Alamofire
    import SwiftyJSON
    
    // MARK: - Alamofire扩展
    extension DataRequest {
        @discardableResult
        public func responseDict(
            router: Routerable, queue: DispatchQueue? = nil,
            completionHandler: @escaping (DataResponse<JSON>) -> Void) -> Self{
            
            response(queue: queue, responseSerializer: DataResponseSerializer { _, response, data, error in
                return DataRequest.dealResult(Request.serializeResponseData(response: response, data: data, error: error))
                }, completionHandler: { response in
                    response.result.ifSuccess {
                        Helper.saveJSON(.http, response.result.value!, router.fileName)
                    }
                    completionHandler(response)
            })
            return self
        }
        
        class func dealResult(_ data: Result<Data>) -> Result<JSON> {
            var result: Result<JSON>! = nil
            switch  data{
            case .success(let jsonData):
                do {
                    let json = try JSON(data: jsonData)
                    let errCode = json["error"].intValue
                    let message = json["message"].stringValue
                    if errCode == 0 {
                        result = Result.success(json)
                    }else{
                        let error = KFError.init(errorCode: errCode, message: message)
                        result = Result.failure(error)
                    }
                } catch let error{
                    result = Result.failure(error)
                }
            case .failure(let error):
                result =  Result.failure(error)
            }
            return result
        }
        
        /// 获取缓存
        ///
        /// - Parameter router: 请求路由
        /// - Returns: 返回结果
        func cacheRequest(router: Routerable) -> DataResponse<JSON>?  {
            if let request = self.request, request.httpMethod?.lowercased() == "get", let cachedReponse = HttpManager.shared.urlCache.cachedResponse(for: request){
                return DataResponse.init(request: request, response: (cachedReponse.response as! HTTPURLResponse), data: cachedReponse.data, result: DataRequest.dealResult(Result.success(cachedReponse.data)))
            }
            return nil
        }
    }
    
    extension DataResponse {
        
        /// 保存结果
        ///
        /// - Parameter router: 请求路由
        func saveCache(router: Routerable) {
            if router.useCache, let res = self.response, let data = self.data, let request = self.request, request.httpMethod?.lowercased() == "get" {// 使用cache的请求才需要缓存
                if self.result.isSuccess {
                    HttpManager.shared.urlCache.storeCachedResponse(CachedURLResponse.init(response: res, data: data), for: request)
                }else{
                    HttpManager.shared.urlCache.removeCachedResponse(for: request)
                }
            }
        }
        
        public var debugDescription: String {
            
            var output: [String] = []
            output.append("[Alamofire]")
            // Request
            output.append("[Request]:\(self.request?.description ?? "(invalid request)")")
            if let headers = self.request?.allHTTPHeaderFields {
                output.append("[Headers]:\(headers.description)")
            }
            if let bodyStream = self.request?.httpBodyStream {
                output.append("[BodyStream]:\(bodyStream.description)")
            }
            if let httpMethod = self.request?.httpMethod {
                output.append("[Method]:\(httpMethod)")
            }
            if let body = self.request?.httpBody, let stringOutput = String(data: body, encoding: .utf8) {
                output.append("[Body]:\(stringOutput)")
            }
            output.append("[Data]: \(data?.count ?? 0) bytes")
            output.append("[Time]: \(timeline.requestDuration)")
            if let data = data {
                do {
                    output.append("[Result]: \(try JSON.init(data: data))")
                }catch {}
            }else{
                output.append("[Result]: \(result.value.debugDescription)")
            }
            return output.joined(separator: "\n")
        }
        private func JSONResponseDataFormatter(_ data: Data) -> Data {
            do {
                let dataAsJSON = try JSONSerialization.jsonObject(with: data)
                let prettyData =  try JSONSerialization.data(withJSONObject: dataAsJSON, options: .prettyPrinted)
                return prettyData
            } catch {
                return data // fallback to original data if it can't be serialized.
            }
        }
    }
    
    

    三.UserRouter
    封装关于User的网络请求

    // MARK: - 用户请求
    public enum UserRouter : Routerable{
        ///获取用户列表
        case userlist
        /// 获取user模型, id为用户id
        case user(id: Int)
        
        public var http: (String, RouterMethod, RouterParam, Bool){
            var path: String!
            let method: RouterMethod = .get
            var parameters: RouterParam = [:]
            var useCache = true
            
            switch self{
                case . userlist:
                    path = "api/list"
                case let .user(id):
                    path = "api/users/\(id)"
                    parameters[ParamType.ticket_id.rawValue] = id
            }
            return (path, method, parameters, useCache)
        }
    }
    

    四.使用方式

    HttpManager.requestResult(router: UserRouter.user(id: 123)) {(result, isEnd) in
       print(result)
    }
    

    或者给Routerable添加网络请求方法

    extension Routerable {
        public func requestResult(queue: DispatchQueue? = DispatchQueue.global(),isShowError: Bool = true, completionHandler: @escaping (Result<JSON>, Bool) -> Void) -> DataRequest {
            return HttpManager.requestResult(router: self, queue: queue, isShowError: isShowError, completionHandler: completionHandler)
        }
    }
    

    这样也就可以直接通过router请求

    UserRouter.user(id: 123).request() { ( result, isEnd) in
       print(result)
    }
    

    五.添加RxSwift支持

    import Foundation
    import RxSwift
    import SwiftyJSON
    import Alamofire
    import SVProgressHUD
    
    extension Routerable {
        var rx_request: Observable<JSON> {
           return HttpManager.rx_requestResult(self)
        }
    }
    
    // MARK: - HttpManager的Rx扩展
    extension HttpManager {
        /// Rx版本的网络请求
        ///
        /// - Parameter router: 路由地址
        /// - Returns: 回调
        static func rx_requestResult(_ router: Routerable, isShowError: Bool = true) -> Observable<JSON>{
            return Observable.create({observer -> Disposable in
                let request = HttpManager.requestResult(router: router, completionHandler: { result, isEnd in
                    switch result {
                    case let .success(value):
                        observer.onNext(value)
                        if isEnd {
                          observer.onCompleted()
                        }
                    case let .failure(error):
                        observer.onError(error)
                    }
                })
                return Disposables.create {
                    request.cancel()
                }
            })
        }
    }
    
    // MARK: - 添加新的Observable类型
    extension ObservableType {
        /// 显示HUD
        func showHUD() -> Observable<Self.E> {
            return Observable.create { observer in
                // showHUD
                DispatchQueue.main.async {
                    SVProgressHUD.show()
                }
                return self.subscribe({ event in
                        // hideHUD
                        DispatchQueue.main.async {
                            SVProgressHUD.dismiss()
                        }
                    observer.on(event)
                })
            }
        }
        /// 绑定成功的值
        public func bindSuccess(to variable: Variable<E>) -> Disposable {
            return subscribe { event in
                switch event {
                case let .next(element):
                    variable.value = element
                case .error(_):
                    break
                case .completed:
                    break
                }
            }
        }
        
        /// 当next或error时执行
        public func doOnce(on: @escaping ((E?) -> Void)) -> Observable<E> {
            return self.do(onNext: { (element) in
                on(element)
            }, onError: { (_) in
                on(nil)
            })
        }
        /// 便利返回数据
        public static func next(_ element: E) -> Observable<E> {
            return Observable.create({ (observer) -> Disposable in
                observer.onNext(element)
                observer.onCompleted()
                return Disposables.create()
            })
        }
        /// 网络请求
        func httpRequest(_ router: Routerable) -> Observable<JSON>{
            return Observable.create({ observer -> Disposable in
                var request: DataRequest? = nil
                let dispose = self.subscribe({ event in
                    switch event {
                    case .next(_):
                        request = HttpManager.requestResult(router: router, completionHandler: { result, isEnd in
                            switch result {
                            case let .success(value):
                                observer.onNext(value)
                                if isEnd {
                                    observer.onCompleted()
                                }
                            case let .failure(error):
                                observer.onError(error)
                            }
                        })
                    case let .error(error):
                        observer.onError(error)
                    default:
                        break
                    }
                })
                return Disposables.create {
                    dispose.dispose()
                    request?.cancel()
                }
            })
        }
    }
    

    github地址:SwiftHTTPManager

    相关文章

      网友评论

          本文标题:记录Swift网络请求模块封装

          本文链接:https://www.haomeiwen.com/subject/xrkhkftx.html