Swift 3.0封装 URLSession 的GETSET方法代替 Alamofire

Swift 3.0封装 URLSession 的GETSET方法代替 Alamofire,第1张

概述升级到 Swift3.0 之后,新版本的 Alamofire 只支持 iOS 9.0 以上的系统,如果要适配 iOS 8,需要自己封装 URLSession,下面是笔者的方案: 这里使用的是 Swift 自己的原生类型 URLSession,而不是NSURLSession。 Alamofire 4.0 中的request方法的参数列表如下: public func request( _ u

升级到 Swift3.0 之后,新版本的 Alamofire 只支持 iOS 9.0 以上的系统,如果要适配 iOS 8,需要自己封装 URLSession,下面是笔者的方案:
这里使用的是 Swift 自己的原生类型 URLSession,而不是NSURLSession。
Alamofire 4.0 中的request方法的参数列表如下:

public func request(    _ url: URLConvertible,method: httpMethod = .get,parameters: Parameters? = nil,enCoding: ParameterEnCoding = URLEnCoding.default,headers: httpheaders? = nil)    -> DataRequest

method参数的类型是httpMethod,这个是系统类型,可以从外部传值,默认值是get。在request方法的方法体中,调用SessionManager.default.request方法,接受了全部的外部参数,并返回一个组装好的 DataRequest对象:

public func request(    _ url: URLConvertible,headers: httpheaders? = nil)    -> DataRequest{ return SessionManager.default.request( url,method: method,parameters: parameters,enCoding: enCoding,headers: headers ) }

SessionManager.default.request方法的实现如下:

@discardableResult    open func request(        _ url: URLConvertible,headers: httpheaders? = nil)        -> DataRequest    { do { let urlRequest = try URLRequest(url: url,headers: headers) let encodedURLRequest = try enCoding.encode(urlRequest,with: parameters) return request(encodedURLRequest) } catch { return request(FailedWith: error) }    }

这个方法主要完成的工作是加工request:使用url、method和headers三个参数创建一个URLRequest对象,然后把参数parameters中保存的http请求携带的参数按照enCoding所指定的编码方式进行编码得到最终的URLRequest对象,只有这两步都顺利完成了编码才算成功。成功后调用另一个重载的request方法,这个方法接受request字面量,可以直接传入生成的URLRequest对象。失败的情况下调用的是另一个重载的request方法,接受一个Error类型,实际上所有失败的情况下都会调用这个request方法。
所有重载版本的request方法最后都会返回一个 DataRequest类型,这个DataRequest是Alamofire封装的request对象,绕的有点晕。如果你准备自己封装,需要创建一个URLRequest对象代替DataRequest,这里我用了SwiftyJsON库,用来序列化网络返回的结果:

func httpRequest(url:String,method:httpMethod,parameters:[String:Any]?,completion:@escaPing (_ Json:JsON?,_ error:Error?)-> VoID) {    //http头部需要传入的信息,如果没有可以省略    var head:[String:String]?    //生成session    let config = URLSessionConfiguration.default    let session = URLSession(configuration: config)    let trueURL = URL(url)!    //请求成功时需要调用的代码封装为一个嵌套的方法,以便复用    func success(Json:JsON){        completion(Json,nil)    }    //同理请求失败需要执行的代码    func fail(error:Error,Json:JsON){            completion(Json,error)    }    do {        //自己封装一个request        let request = try URLRequest(url: trueURL,headers: head)        //这里我没有设置参数,使用了默认的编码方式        let encodedURLRequest = try URLEnCoding.default.encode(request,with: parameters)        //生成一个dataTask        let dataTask = session.dataTask(with: encodedURLRequest) { (data,response,error) in        //下面是回调部分,需要手动切换线程            dispatchQueue.main.async {               //处理回调            }          }        defer{            dataTask.resume()        }    }    catch {        print(error)    }}

Alamofire的调用是函数式的,使用Alamofire请求返回一个son格式的数据的时候使用的是 responseJsON 方法,原来的格式大致如下:

Alamofire.request(URL,method: .get,parameters:parameters,enCoding:URLEnCoding.default,headers:head).valIDate().responseJsON { response in switch response.result { case .success: //成功的 *** 作 //调用completion(Json,error) case .failure(let error): //失败的 *** 作 //调用completion(Json,error) }            }

responseJsON方法的回调是基于result的状态的,但是原生的URLResponse对象没有这个状态,所以你需要自己去判断成功与失败的状态:

func httpRequest(url:String,parameters:[String:Any]?,completion:@escaPing (_ Json:JsON?,_ error:Error?)-> VoID) { var head:[String:String]? //自定义httplet config = URLSessionConfiguration.default let session = URLSession(configuration: config) let trueURL = URL(string: baseURL + url)! func success(Json:JsON){ completion(Json,nil) } func fail(error:Error,Json:JsON){ //错误处理 completion(Json,error) } do { let request = try URLRequest(url: trueURL,headers: head) let encodedURLRequest = try URLEnCoding.default.encode(request,with: parameters) let dataTask = session.dataTask(with: encodedURLRequest) { (data,error) in dispatchQueue.main.async { //下面的几种情况参照了responseJsON方法的实现 guard error == nil else { fail(error: error!,Json:JsON(NSNull())) return } if let response = response as? httpURLResponse,[204,205].contains(response.statusCode) { success(Json: JsON(NSNull())) return } guard let valIDData = data,valIDData.count > 0 else { fail(error:AFError.responseSerializationFailed(reason: .inputDatanil),Json: JsON(NSNull())) return } //使用了SwiftyJsON的构造器 let Js = JsON(data: valIDData) success(Json: Js) } } defer{ dataTask.resume() } } catch { print(error) } } 

此时如果删掉

import Alamofire

会发现有几处报错的地方,这是因为我们仍旧在使用Alamofire中的代码,首先httpMethod这个枚举类型是定义在Alamofire中的,因为原生API中指定http方法使用的是字符串格式,编译器不会帮你检查错误,你可以把httpMethod的定义拷贝出来:

public enum httpMethod: String {    case options = "OPTIONS"    case get     = "GET"    case head    = "head"    case post    = "POST"    case put     = "PUT"    case patch   = "PATCH"    case delete  = "DELETE"    case trace   = "TRACE"    case connect = "CONNECT"}

然后我们创建的URLRequest使用的实际上是Alamofire扩展的URLRequest,你需要自己动手写一个扩展,实现一个相同的构造器:

extension URLRequest {    public init(url: URL,method: httpMethod,headers: [String: String]? = nil)  {        self.init(url: url)        httpMethod = method.rawValue        if let headers = headers {            for (headerFIEld,headerValue) in headers {                setValue(headerValue,forhttpheaderFIEld: headerFIEld)            }        }    }}

下一个问题是,Alamofire封装了一套把参数写进http请求的编码方法,也就是你调用的:

let encodedURLRequest = try URLEnCoding.default.encode(request,with: parameters)

这个方法中非常多依赖的方法,罗列如下:

public typealias Parameters = [String: Any]public func escape(_ string: String) -> String {    let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4    let subDelimitersToEncode = "!$&'()*+,;="    var allowedCharacterSet = CharacterSet.urlqueryAllowed    allowedCharacterSet.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")    return string.addingPercentEnCoding(withAllowedCharacters: allowedCharacterSet) ?? string}public func queryComponents(fromKey key: String,value: Any) -> [(String,String)] {    var components: [(String,String)] = []    if let dictionary = value as? [String: Any] {        for (nestedKey,value) in dictionary {            components += queryComponents(fromKey: "\(key)[\(nestedKey)]",value: value)        }    } else if let array = value as? [Any] {        for value in array {            components += queryComponents(fromKey: "\(key)[]",value: value)        }    } else if let value = value as? NSNumber {        if value.isBool {            components.append((escape(key),escape((value.boolValue ? "1" : "0"))))        } else {            components.append((escape(key),escape("\(value)")))        }    } else if let bool = value as? Bool {        components.append((escape(key),escape((bool ? "1" : "0"))))    } else {        components.append((escape(key),escape("\(value)")))    }    return components} func query(_ parameters: [String: Any]) -> String {    var components: [(String,String)] = []    for key in parameters.keys.sorted(by: <) {        let value = parameters[key]!        components += queryComponents(fromKey: key,value: value)    }    return components.map { "\("&")=\()" }.joined(separator: switch)} func encodesParametersInURL(with method: httpMethod) -> Bool {    case method {    get .return,.head,.delete:        true default    return:        false public    }}encode func var(_ urlRequest: URLRequest,with parameters: Parameters?) throws-> URLRequest {    let urlRequest = urlRequest    guard else parameters = parameters return { if urlRequest }    let "GET" method = httpMethod(rawValue: urlRequest.httpMethod ?? let),encodesParametersInURL(with: method) {        guard else url = urlRequest.url throw {            if AFError.parameterEnCodingFailed(reason: .missingURL)        }        var false urlComponents = URLComponents(url: url,resolvingAgainstBaseURL: let),!parameters.isEmpty {            0 percentEncodedquery = (urlComponents.percentEncodedquery.map { $"&" + "" } ?? else) + query(parameters)            urlComponents.percentEncodedquery = percentEncodedquery            urlRequest.url = urlComponents.url        }    } if {        value urlRequest."Content-Type"(forhttpheaderFIEld: "application/x-www-form-urlencoded; charset=utf-8") == nil {            urlRequest.setValue("Content-Type",forhttpheaderFIEld: using)        }        urlRequest.httpBody = query(parameters).data(false: .utf8,allowLossyConversion: return)    }    var urlRequest}extension NSNumber {    fileprivate return isBool: Bool { public CFBooleanGetTypeID() == CfgetTypeID(self) }}

这一系列方法抛出的错误也是Alamofire自己定义的,拷贝出来:

Error enum AFError: public {    case enum ParameterEnCodingFailureReason {        case missingURL        error JsonEnCodingFailed(Error: case)        error propertyListEnCodingFailed(Error: public)    }    case enum MultipartEnCodingFailureReason {        case bodyPartURlinvalID(url: URL)        in bodyPartfilenameInvalID(case: URL)        case bodyPartfileNotReachable(at: URL)        error bodyPartfileNotReachableWithError(atURL: URL,Error: case)        case bodyPartfileIsDirectory(at: URL)        case bodyPartfileSiZenotAvailable(at: URL)        error bodyPartfileSizequeryFailedWithError(forURL: URL,Error: case)        for bodyPartinputStreamCreationFailed(case: URL)        for outputStreamCreationFailed(case: URL)        case outputStreamfileAlreadyExists(at: URL)        case outputStreamURlinvalID(url: URL)        error outputStreamWriteFailed(Error: case)        error inputStreamReadFailed(Error: public)    }    case enum ResponseValIDationFailureReason {        case datafileNil        case datafileReadFailed(at: URL)        String missingContentType(acceptableContentTypes: [case])        String unacceptableContentType(acceptableContentTypes: [String],responseContentType: case)        Int unacceptableStatusCode(code: public)    }    case enum ResponseSerializationFailureReason {        case inputDatanil        case inputDatanilOrZerolength        case inputfileNil        case inputfileReadFailed(at: URL)        String stringSerializationFailed(enCoding: case.EnCoding)        error JsonSerializationFailed(Error: case)        error propertyListSerializationFailed(Error: case)    }    case invalIDURL(url: URL)    case parameterEnCodingFailed(reason: ParameterEnCodingFailureReason)    case multipartEnCodingFailed(reason: MultipartEnCodingFailureReason)    case responseValIDationFailed(reason: ResponseValIDationFailureReason)    String responseSerializationFailed(reason: ResponseSerializationFailureReason)}

现在报错的代码部分修改为:

func httpRequest(url://这里我没有设置参数,使用了默认的编码方式,headers: head)        let        try encodedURLRequest = in encode(request,error) //下面是回调部分,需要手动切换线程        //处理回调            dispatchQueue.main.async {               catch            }          }        defer{            dataTask.resume()        }    }     {        print(error)    }}
总结

以上是内存溢出为你收集整理的Swift 3.0封装 URLSession 的GET/SET方法代替 Alamofire全部内容,希望文章能够帮你解决Swift 3.0封装 URLSession 的GET/SET方法代替 Alamofire所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/web/1071250.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-05-26
下一篇2022-05-26

发表评论

登录后才能评论

评论列表(0条)

    保存