使用Swift创建通用的Alamofire请求(iOS)

5

最近我开始学习使用Swift进行iOS应用程序开发,所以我对此还很新。我想在Swift中实现REST API调用,并发现我们可以使用URLRequest来实现。因此,我编写了一个通用方法来调用所有类型(例如get, put, post)的REST API,如下所示。

import Foundation
//import Alamofire

public typealias JSON = [String: Any]
public typealias HTTPHeaders = [String: String];

public enum RequestMethod: String {
    case get = "GET"
    case post = "POST"
    case put = "PUT"
    case delete = "DELETE"
}
public enum Result<Value> {
    case success(Value)
    case failure(Error)
}
public class apiClient{
    private  var base_url:String = "https://api.testserver.com/"
    private func apiRequest(endPoint: String,
                            method: RequestMethod,
                            body: JSON? = nil,
                            token: String? = nil,
                            completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) {
        let url = URL(string: (base_url.self + endPoint))!
        var urlRequest = URLRequest(url: url)
        urlRequest.httpMethod = method.rawValue
        urlRequest.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
        if let token = token {
            urlRequest.setValue("bearer " + token, forHTTPHeaderField: "Authorization")
        }
        if let body = body {
            urlRequest.httpBody = try? JSONSerialization.data(withJSONObject: body)
        }
        let session = URLSession(configuration: .default)
        let task = session.dataTask(with: urlRequest) { data, response, error in
            //NSLog(error)
            completionHandler(data, response, error)
        }
        task.resume()
    }
    public func sendRequest<T: Decodable>(for: T.Type = T.self,
                                          endPoint: String,
                                          method: RequestMethod,
                                          body: JSON? = nil,
                                          token: String? = nil,
                                          completion: @escaping (Result<T>) -> Void) {
        return apiRequest(endPoint: endPoint, method: method, body:body, token: token) { data, response, error in
            guard let data = data else {
                return completion(.failure(error ?? NSError(domain: "SomeDomain", code: -1, userInfo: nil)))
            }
            do {
                let decoder = JSONDecoder()
                try completion(.success(decoder.decode(T.self, from: data)))
            } catch let decodingError {
                completion(.failure(decodingError))
            }
        }
    }
}

这是我从控制器中调用方法的方式

public func getProfile(userId :Int, objToken:String) -> Void {
        let objApi = apiClient()
        objApi.sendRequest(for: ProfileDetails.self,
                           endPoint:"api/user/profile/\(userId)",
                           method: .get,
                           token: objToken,
            completion:
            {(userResult: Result<ProfileDetails>) -> Void in
                switch userResult
                {
                case .success(let value):
                    if value.respCode == "01" {
                        print(value.profile)
                        do {
                            //... ddo some taks like store response in local db or else
                        } catch let error as NSError {
                            // handle error
                            print(error)
                        }
                    }
                    else {
                        //do some task
                    }
                    break
                case .failure(let error):
                    print(error)
                    break
                }
        })
    }

我正在以下模型中解码服务器响应

class ProfileDetails : Response, Decodable {    
    var appUpdate : AppUpdate?
    var profile : Profile?

    enum CodingKeys: String, CodingKey {
        case profile = "profile"
        case respCode = "resp_code"
        case respMsg = "resp_msg"
    }
    public required convenience init(from decoder: Decoder) throws {
        self.init()
        let values = try decoder.container(keyedBy: CodingKeys.self)
        self.profile = try values.decodeIfPresent(Profile.self, forKey: .profile)
        self.respCode = try values.decodeIfPresent(String.self, forKey: .respCode)!
        self.respMsg = try values.decodeIfPresent(String.self, forKey: .respMsg)
    }
}

这段代码无法处理来自服务器的401、404等错误响应。因此,我要做的是将这个URLRequest请求转换为带有401、404等错误处理机制的通用Alamofire请求。我已经安装了Alamofire pods,有没有人开发过带有解码和错误处理的通用Alamofire请求方法?
提前感谢:)

我不知道为什么这个问题会收到踩和关闭请求。我只是简单地说明了我的当前代码和我正在寻找的内容。 - Ajay
我已经添加了一个答案并链接到Github项目,你可以下载和测试这个类。如果你需要其他的东西,请告诉我。 - Sahil Manchanda
9个回答

8

Git链接:https://github.com/sahilmanchanda2/wrapper-class-for-alamofire

这是我的版本(使用 Alamofire 5.0.2):

import Foundation
import Alamofire

class NetworkCall : NSObject{

    enum services :String{
        case posts = "posts"
    }
    var parameters = Parameters()
    var headers = HTTPHeaders()
    var method: HTTPMethod!
    var url :String! = "https://jsonplaceholder.typicode.com/"
    var encoding: ParameterEncoding! = JSONEncoding.default

    init(data: [String:Any],headers: [String:String] = [:],url :String?,service :services? = nil, method: HTTPMethod = .post, isJSONRequest: Bool = true){
        super.init()
        data.forEach{parameters.updateValue($0.value, forKey: $0.key)}
        headers.forEach({self.headers.add(name: $0.key, value: $0.value)})
        if url == nil, service != nil{
            self.url += service!.rawValue
        }else{
            self.url = url
        }
        if !isJSONRequest{
            encoding = URLEncoding.default
        }
        self.method = method
        print("Service: \(service?.rawValue ?? self.url ?? "") \n data: \(parameters)")
    }

    func executeQuery<T>(completion: @escaping (Result<T, Error>) -> Void) where T: Codable {
        AF.request(url,method: method,parameters: parameters,encoding: encoding, headers: headers).responseData(completionHandler: {response in
            switch response.result{
            case .success(let res):
                if let code = response.response?.statusCode{
                    switch code {
                    case 200...299:
                        do {
                            completion(.success(try JSONDecoder().decode(T.self, from: res)))
                        } catch let error {
                            print(String(data: res, encoding: .utf8) ?? "nothing received")
                            completion(.failure(error))
                        }
                    default:
                     let error = NSError(domain: response.debugDescription, code: code, userInfo: response.response?.allHeaderFields as? [String: Any])
                        completion(.failure(error))
                    }
                }
            case .failure(let error):
                completion(.failure(error))
            }
        })
    }
}

上面的类使用了最新版本的Alamofire(截止到2020年2月),这个类几乎涵盖了所有的HTTP方法,可以选择以应用程序/JSON格式或正常方式发送数据。使用这个类可以获得很多灵活性,并且它会自动将响应转换为您的Swift对象。
查看这个类的init方法,它包含以下内容:
  1. data: [String,Any] = 在这里放置表单数据。

  2. headers: [String:String] = 您可以在此处发送要随请求一起发送的自定义头文件

  3. url = 在这里可以指定完整的URL,如果您已经在类中定义了baseurl,则可以将其留空。当您想要使用第三方提供的REST服务时,它会很方便。注意:如果您正在填写url,则应该将下一个参数service设置为nil

  4. service: services = 它是在NetworkClass本身中定义的枚举。它们作为端点。查看init方法,如果url为nil但service不为nil,则它将在base url的末尾附加以形成完整的URL,将提供示例。

  5. method: HTTPMethod = 在这里可以指定请求应使用哪个HTTP方法。

  6. isJSONRequest = 默认设置为true。如果要发送普通请求,请将其设置为false。

在init方法中,您还可以指定要与每个请求一起发送的常用数据或标题,例如:您的应用程序版本号,iOS版本等。
现在看看execute方法:它是一个通用函数,如果响应成功,它将返回您选择的Swift对象。如果无法将响应转换为您的Swift对象,则会以字符串形式打印响应。如果响应代码不在200-299范围内,则会失败并提供完整的调试说明以获取详细信息。
用法:
假设我们有以下结构体:
struct Post: Codable{
    let userId: Int
    let id: Int
    let title: String
    let body: String
}

注意 NetworkClass 中定义的基本网址 https://jsonplaceholder.typicode.com/

示例1:发送内容类型为 Application/JSON 的 HTTP Post 请求

let body: [String : Any] = ["title": "foo",
                                          "body": "bar",
                                          "userId": 1]
        NetworkCall(data: body, url: nil, service: .posts, method: .post).executeQuery(){
            (result: Result<Post,Error>) in
            switch result{
            case .success(let post):
                print(post)
            case .failure(let error):
                print(error)
            }
        }

输出:

Service: posts 
data: ["userId": 1, "body": "bar", "title": "foo"]
Post(userId: 1, id: 101, title: "foo", body: "bar")
  1. HTTP 400 请求

    网络调用(数据:["email":"peter@klaven"],URL:https://reqres.in/api/login,方法:.post,是否为JSON请求:false)。executeQuery(){ (result: Result) in switch result{ case .success(let post): print(post) case .failure(let error): print(error) } }

输出:

Service: https://reqres.in/api/login 
 data: ["email": "peter@klaven"]
Error Domain=[Request]: POST https://reqres.in/api/login
[Request Body]: 
email=peter%40klaven
[Response]: 
[Status Code]: 400
[Headers]:
Access-Control-Allow-Origin: *
Content-Length: 28
Content-Type: application/json; charset=utf-8
Date: Fri, 28 Feb 2020 05:41:26 GMT
Etag: W/"1c-NmpazMScs9tOqR7eDEesn+pqC9Q"
Server: cloudflare
Via: 1.1 vegur
cf-cache-status: DYNAMIC
cf-ray: 56c011c8ded2bb9a-LHR
expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
x-powered-by: Express
[Response Body]: 
{"error":"Missing password"}
[Data]: 28 bytes
[Network Duration]: 2.2678009271621704s
[Serialization Duration]: 9.298324584960938e-05s
[Result]: success(28 bytes) Code=400 "(null)" UserInfo={cf-ray=56c011c8ded2bb9a-LHR, Access-Control-Allow-Origin=*, Date=Fri, 28 Feb 2020 05:41:26 GMT, expect-ct=max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct", Server=cloudflare, Etag=W/"1c-NmpazMScs9tOqR7eDEesn+pqC9Q", x-powered-by=Express, Content-Type=application/json; charset=utf-8, Content-Length=28, Via=1.1 vegur, cf-cache-status=DYNAMIC}
  1. 使用自定义标题

    NetworkCall(data: ["username":"sahil.manchanda2@gmail.com"], headers: ["custom-header-key" : "custom-header-value"], url: "https://httpbin.org/post", method: .post).executeQuery(){(result: Result) in switch result{ case .success(let data): print(data) case .failure(let error): print(error) } }

输出:

Service: https://httpbin.org/post 
 data: ["username": "sahil.manchanda2@gmail.com"]
{
  "args": {}, 
  "data": "{\"username\":\"sahil.manchanda2@gmail.com\"}", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "br;q=1.0, gzip;q=0.9, deflate;q=0.8", 
    "Accept-Language": "en;q=1.0", 
    "Content-Length": "41", 
    "Content-Type": "application/json", 
    "Custom-Header-Key": "custom-header-value", 
    "Host": "httpbin.org", 
    "User-Agent": "NetworkCall/1.0 (sahil.NetworkCall; build:1; iOS 13.2.2) Alamofire/5.0.2", 
    "X-Amzn-Trace-Id": "Root=1-5e58a94f-fab2f24472d063f4991e2cb8"
  }, 
  "json": {
    "username": "sahil.manchanda2@gmail.com"
  }, 
  "origin": "182.77.56.154", 
  "url": "https://httpbin.org/post"
}

typeMismatch(Swift.String, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode String but found a dictionary instead.", underlyingError: nil))

在最后一个示例中,您可以看到末尾的"typeMismatch"错误。我试图在executeQuery中传递[String:Any],但由于Any不符合可编码要求,我不得不使用String。

@Ajay,你试过我提到的GIT项目了吗? - Sahil Manchanda
请确保您正在使用最新版本的Alamofire。 - Sahil Manchanda
是的,我正在使用Alamofire 5。它一直给出HttpHeader错误。 - Ajay
pod 'Alamofire','~> 5.0' - Ajay
@Ajay,你在新项目中尝试了我的答案吗? - Sahil Manchanda
显示剩余4条评论

2

我使用EVReflection配合alamofire,我认为这是最好的组合之一。

使用Alamofire的URLRequestConvertible协议。

这是我遵循的步骤。

仅供参考。

为您的所有终点创建枚举,并将确认该枚举符合URLRequestConvertible协议。

enum Router: URLRequestConvertible { 

//your all endpoint
static var authToken = ""
case login([String:Any])

var route: Route {
        switch self {
        case .Login(let dict):
            return Route(endPoint: "api/addimagedata", httpMethod: .post)
        }
    }

func asURLRequest() throws -> URLRequest {

        var requestUrl = EnvironmentVariables.baseURL
        if let queryparams = route.queryParameters {
            requestUrl.appendQueryParameters(queryparams)
        }
        var mutableURLRequest = URLRequest(url: requestUrl.appendingPathComponent(route.endPath))
        mutableURLRequest.httpMethod = route.method.rawValue


        //FIXME:- Change the Userdefault Key
        if Router.authToken.isEmpty, let token = UserDefaults.standard.string(forKey: "Key"), !token.isEmpty {
            Router.authToken = token
        }

        //FIXME:- Set Mutable Request Accordingly
        mutableURLRequest.setValue("Bearer \(Router.authToken)", forHTTPHeaderField: "Authorization")
        mutableURLRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
        mutableURLRequest.setValue("application/json", forHTTPHeaderField: "Accept")

        if route.method == .get {
            return try Alamofire.URLEncoding.default.encode(mutableURLRequest, with: route.parameters)
        }
        return try Alamofire.JSONEncoding.default.encode(mutableURLRequest, with: route.parameters)
    }


}

根据您的需求创建一个结构。
struct Route {

    let endPath: String
    let method: Alamofire.HTTPMethod
    var parameters: Parameters?
    var queryParameters : [String:String]?

    var encoding: Alamofire.ParameterEncoding {
        switch method {
        case .post, .put, .patch, .delete:
            return JSONEncoding()
        default:
            return URLEncoding()
        }
    }
}

现在创建一个通用函数,接受URLRequestConvertible并在闭包中返回你的模型。就像这样:
func GenericApiCallForObject<T : URLRequestConvertible, M : EVObject>(router : T, showHud : Bool = true ,responseModel : @escaping (M) -> ()) {

    view.endEditing(true)

    if !isConnectedToInternet {
        showNetworkError()
        return
    }

    if showhud ? showHud() : ()

    Alamofire.request(router).responseObject { (response: DataResponse<M>) in

        self.HandleResponseWithErrorForObject(response: response) { (isSuccess) in
            if isSuccess {
                if let value = response.result.value {
                    responseModel(value)
                }
            }
        })
    }
}

现在创建一个通用函数,接收您的响应并为您处理错误。像这样。
func HandleResponseWithErrorForObject<M : EVObject>(response : DataResponse<M>, isSuccess : @escaping (Bool) -> ()) {

        print(response)
        hideHud()
        switch response.response?.statusCode ?? 0 {
        case 200...299:
            isSuccess(true)
        case 401:
            isSuccess(false)
            showSessionTimeOutError()
        case -1005,-1001,-1003:
            break
        default:
            isSuccess(false)
            // Parse your response and show error in some way.

        }
    }

现在最后,如何正确使用它?!事实上,现在非常简单,只需两行代码,您就可以开始使用。
GenericApiCallForObject(router: Router.Login(["xyz":"xyz"])) { (response : GeneralModel) in
    print(response)
}

请注意,此方法仅适用于您收到响应对象的情况。如果是数组或字符串,您需要编写单独的函数,处理过程与上述相同。只有成功时才会收到响应,否则 HandleResponseWithErrorForObject 函数将自动处理。另外,以上说明可能缺少某些变量。

谢谢您的回复。我会尝试您的解决方案并与您联系。 - Ajay

1
我在我的 REST API 中分享了一个特定的错误处理部分。 它将在以下块中进行解码,你可能可以用它作为参考。
正如你所看到的,将代码转换为枚举非常简单。 Alamofire 允许这样做,但这取决于你使用的库版本。 有时候,取决于你的 REST API 如何在内部处理错误,它们可能无法抛出代码,例如如果它的后端是 Java,则它们可以封装异常。
public enum RESTError: Error {
    case BadRequest(String, [String]?)
    case InternalError(String)
    case UnAuthorized(String, [String]?)
    case NotFound(String)
    case Success

    /// <#Description#>
    ///
    /// - Parameters:
    ///   - code: <#code description#>
    ///   - message: <#message description#>
    ///   - globalErrors: <#globalErrors description#>
    /// - Returns: <#return value description#>
    public static func fromCode(code: Int, message: String, globalErrors: [String]? = nil) -> RESTError {
        switch code {
        case 400: return RESTError.BadRequest(message, globalErrors)
        case 401: return RESTError.UnAuthorized(message, globalErrors)
        case 500: return RESTError.InternalError(message)
        case 404: return RESTError.NotFound(message)
        default: break
        }
        return RESTError.Success
    }
}

Alamofire.request(urlRequest)
                        .validate(statusCode: 200...500)
                        .responseJSON(completionHandler: { (response: (DataResponse<Any>)) in
                            if let statusCode = response.response?.statusCode {
                                if statusCode != 200 {
                                    // call handler errors function with specific message
                                    if let arrayDictionary = response.result.value as? Dictionary<String,AnyObject> {
                                        var error: RESTError?
                                        if let code = arrayDictionary["status"] as? Int {
                                            let message = arrayDictionary["message"] as! String
                                            let globalErrors = arrayDictionary["globalErrors"] as? [String]
                                            error = RESTError.fromCode(code: code, message: message, globalErrors: globalErrors)
                                        } else {
                                            // Build from error message without code.
                                            let message = arrayDictionary["error_description"] as! String
                                            let codeMsg = arrayDictionary["error"] as! String
                                            let globalErrors = arrayDictionary["globalErrors"] as? [String]
                                            if codeMsg == "invalid_token" && message.starts(with: "Access token expired") {

                                                return
                                            } else {
                                                error = RESTError.fromCode(code: codeMsg, message: message, globalErrors: globalErrors)
                                            }
                                        }
                                        if let _ = error {
                                            errorHandler(error!)
                                        } else {
                                            errorHandler(RESTError.InternalError("Internal API rest error."))
                                        }
                                    } else {
                                        errorHandler(RESTError.fromCode(code: statusCode, message: ""))
                                    }
                                } else {
                                    if let arrayDictionary = response.result.value as? Dictionary<String,AnyObject> {
                                        handler(arrayDictionary)
                                    }
                                }
                            } else {
                                if let error = response.error {
                                    errorHandler(RESTError.InternalError(error.localizedDescription))
                                }
                            }
                        })

谢谢你的回复。正如你在我的代码中所看到的,我正在Decodable类中解析服务器响应。而我认为你的代码是在字典中解析响应。我说得对吗? - Ajay
是的,但我是基于自己的REST API进行开发的,这不应该对您造成任何影响,您可以忽略这部分。 - JBarros35

0

你可能需要使用alamofilre Session Manager执行请求的函数。您还可以将cookie和标头等设置为此会话管理器,以便在其余请求中使用它们。

import Alamofire

class NetworkManager : NSObject {
internal typealias SuccessCompletion = (Int?, Any?) -> Void?
internal typealias FailCompletion = (Int?, Error, Any?) -> Void?
var sessionManager : SessionManager!
var request : Request?
var headers : HTTPHeaders! = [:]


    override init() {
        let configuration = URLSessionConfiguration.default
        configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders
        sessionManager = SessionManager(configuration: configuration)
     }


func sendRequest(url: String?, method: String, parameters: [String: Any], success: SuccessCompletion?, fail: FailCompletion?){
    var encoding : ParameterEncoding!
    if  HTTPMethod(rawValue: method) == HTTPMethod.post {
        encoding = JSONEncoding.default
    } else {
        encoding = URLEncoding.default
    }

    request = sessionManager.request(url ?? "", method: HTTPMethod(rawValue: method)!, parameters: parameters, encoding: encoding, headers: headers)
    .validate()
    .responseData{response in
        switch (response.result) {
        case .success:
            let statusCode = response.response?.statusCode
            success?(statusCode, response.result.value)
            self.request = nil
            break
        case .failure(let error):
            let statusCode = response.response?.statusCode
            fail?(statusCode, error, response.data)
            self.request = nil
            break
        }
    }
}

}

编辑

要添加标题,您可以添加此类函数..

func updateJSONHeader(token: String) {
        self.clearHeaders()
        headers["AuthorizationToken"] = "\(token)"
    }

关于cookie

func setCookie(_ cookie : HTTPCookie?){
    if let cookie = cookie {
        HTTPCookieStorage.shared.setCookie(cookie)
    }
}

清除头信息
func clearHeaders(){
    headers = [:]
}

记住,这是一个单例类,所以无论何时你改变任何东西,除非你的服务器做出一些改变,否则你仍然拥有你的配置,例如头部信息。


我该如何设置像授权这样的标头?就像你在我的代码中看到的那样。 - Ajay

0
最好的方法是使用 DataRequest 扩展创建自定义验证方法:
func customValidate() -> Self {
        return self.validate { _, response, data -> Request.ValidationResult in
            guard (400...599) ~= response.statusCode else { return .success(()) }
            guard let data = data else { return .failure(MyAppGeneralError.generalResponseError) }

            guard let errorResponse = try? JSONDecoder().decode(MyAppResponseError.self, from: data) else {
                return .failure(MyAppGeneralError.generalResponseError)
            }

            if response.statusCode == 401 {
                return .failure(MyAppGeneralError.unauthorizedAccessError(errorResponse))
            }

            return .failure(MyAppGeneralError.responseError(errorResponse))
        }
    }

使用具有通用功能的客户端,其中通用部分可以使用我们的自定义验证进行解码。

class APIClient {
    var session: Session

    init(session: Session = Session.default) {
        self.session = session
    }

    @discardableResult
    func performRequest<T: Decodable>(request: URLRequestConvertible,
                                      decoder: JSONDecoder = JSONDecoder(),
                                      completion: @escaping (Result<T, AFError>) -> Void) -> DataRequest {
        return AF.request(request).customValidate().responseDecodable(decoder: decoder, completionHandler: { (response: DataResponse<T, AFError>) in
            completion(response.result)
        })
    }

    func getProfile(userID: Int, _ completion: @escaping (Result<UserToken, AFError>) -> Void) {
        performRequest(request: APIRouter.profile(userID: userID), completion: completion)
    }
}

使用路由器 a:

enum APIRouter: URLRequestConvertible {
    case profile(userId :Int)

    static let baseURLString = "https://myserver.com"

    var method: HTTPMethod {
        switch self {
        case .profile:
            return .get
        }
    }

    var path: String {
        switch self {
        case .profile(let userID):
            return "profile/\(userID)"
        }
    }

    var body: Parameters {
        return [:]
    }

    // MARK: URLRequestConvertible
    func asURLRequest() throws -> URLRequest {
        let url = try APIRouter.baseURLString.asURL()

        var urlRequest = URLRequest(url: url.appendingPathComponent(path))
        urlRequest.httpMethod = method.rawValue

        // Common Headers
        urlRequest.setValue("application/json", forHTTPHeaderField: "Accept")


        // Encode body
        urlRequest = try JSONEncoding.default.encode(urlRequest, with: body)

        return urlRequest
    }
}

0
import Foundation

导入UIKit 导入Alamofire 导入SwiftyJSON

类AFWrapper:NSObject {

static let sharedInstance = AFWrapper()

//TODO :-
/* Handle Time out request alamofire */


func requestGETURL(_ strURL: String, success:@escaping (JSON) -> Void, failure:@escaping (Error) -> Void)
{
    Alamofire.request(strURL).responseJSON { (responseObject) -> Void in
        //print(responseObject)
        if responseObject.result.isSuccess {
            let resJson = JSON(responseObject.result.value!)
            //let title = resJson["title"].string
            //print(title!)
            success(resJson)
        }

        if responseObject.result.isFailure {
            let error : Error = responseObject.result.error!
            failure(error)
        }
    }
}

func requestPOSTURL(_ strURL : String, params : [String : AnyObject]?, headers : [String : String]?, success:@escaping (JSON) -> Void, failure:@escaping (Error) -> Void){
    Alamofire.request(strURL, method: .post, parameters: params, encoding: JSONEncoding.default, headers: headers).responseJSON { (responseObject) -> Void in
        //print(responseObject)
        if responseObject.result.isSuccess {
            let resJson = JSON(responseObject.result.value!)
            success(resJson)
        }
        if responseObject.result.isFailure {
            let error : Error = responseObject.result.error!
            failure(error)
        }
    }
}

}


responseJSON 已经被弃用,请使用 responseDecodable 替代。 - Birju

0

这是我一直在努力的事情!尚未完成,但可以解决您的问题。您可以将其升级为任何您想要的。

类型别名

 typealias Closure<T> = (T)->()
 typealias JSON = [String: Any]

扩展

extension JSONDecoder{
func decode<T : Decodable>(_ model : T.Type,
                           result : @escaping Closure<T>) ->Closure<Data>{
    return { data in
        if let value = try? self.decode(model.self, from: data){
            result(value)
        }
    }
}

协议 //MARK:- 协议 APIResponseProtocol

protocol APIResponseProtocol{
    func responseDecode<T: Decodable>(to modal : T.Type,
                              _ result : @escaping Closure<T>) -> APIResponseProtocol
    func responseJSON(_ result : @escaping Closure<JSON>) -> APIResponseProtocol
    func responseFailure(_ error :@escaping Closure<String>)
}

请求:

       let configuration = URLSessionConfiguration.default
        configuration.timeoutIntervalForRequest = 300 // seconds
        configuration.timeoutIntervalForResource = 500
        alamofireManager = Alamofire.SessionManager(configuration: configuration)

  func getRequest(forAPI api: String, params: JSON) -> APIResponseProtocol {
          let responseHandler = APIResponseHandler()
            var parameters = params
             parameters["token"] = preference.string(forKey: USER_ACCESS_TOKEN)
          alamofireManager.request(api,
                          method: .get,
                          parameters: parameters,
                          encoding: URLEncoding.default,
                          headers: nil)
              .responseJSON { (response) in
                  print("Å api : ",response.request?.url ?? ("\(api)\(params)"))


                  switch response.result{
                  case .success(let value):
                      let json = value as! JSON
                      let error = json.string("error")
                      guard error.isEmpty else{

                          responseHandler.handleSuccess(value: value,data: response.data ?? Data())
                  case .failure(let error):
                      responseHandler.handleFailure(value: error.localizedDescription)
                  }
          }


          return responseHandler
      }

响应处理程序:

class APIResponseHandler:APIResponseProtocol {

init(){
}
var jsonSeq : Closure<JSON>?
var dataSeq : Closure<Data>?
var errorSeq : Closure<String>?

func responseDecode<T>(to modal: T.Type, _ result: @escaping Closure<T>) -> APIResponseProtocol where T : Decodable {

    let decoder = JSONDecoder()
    self.dataSeq =  decoder.decode(modal, result: result)
    return self
}

func responseJSON(_ result: @escaping Closure<JSON>) -> APIResponseProtocol {
    self.jsonSeq = result
    return self
}
func responseFailure(_ error: @escaping Closure<String>) {
    self.errorSeq = error

  }




func handleSuccess(value : Any,data : Data){
    if let jsonEscaping = self.jsonSeq{
        jsonEscaping(value as! JSON)
    }
    if let dataEscaping = dataSeq{
        dataEscaping(data)

    }
}
func handleFailure(value : String){
    self.errorSeq?(value)
 }

}

使用方法:

self?.apiInteractor?
        .getRequest(forAPI: "https://maps.googleapis.com/maps/api/directions/json",
                    params: [
                        "origin" : "\(pickUpLatitude),\(pickUpLongitude)",
                        "destination" :"\(dropLatitude),\(dropLongitude)",
                        "mode" : "driving",
                        "units" : "metric",
                        "sensor" : "true",
                        "key" : "\(UserDefaults.value(for: .google_api_key) ?? "")"
        ])
        .responseDecode(to: GoogleGeocode.self, { [weak self] (googleGecode) in
            guard let welf = self,
                let route = googleGecode.routes.first,
                let leg = route.legs.first else{return}
            welf.tripDetailModel?.arrivalFromGoogle = leg.duration.text ?? ""
            welf.drawRoute(forRoute: route)
            welf.calculateETA()
        })
        .responseJSON({ (json) in
            debugPrint(json.description)
        })
        .responseFailure({ (error) in
            debug(print: error)
        })

0

您需要在项目中添加Pod文件 添加POD

pod 'Alamofire'
pod 'SwiftyJSON'

接下来,您需要创建一个通用的对象文件名,命名为AlamofireManager.swift

然后,您需要添加以下代码:

import Foundation
import SwiftyJSON
import Alamofire
class AlamoFireCommon:  NSObject
{
    class func callWebServiceWithParameterPostPut(methodName: String, withParameter parameters: Parameters, httpMethod: HTTPMethod, completion: @escaping ((_ respose: [String: AnyObject],_ success: Bool) -> Void ))
    {
        let headers: HTTPHeaders = ["Content-Type" : "application/json; charset=utf-8"]
        
        var strURL = ""
        strURL = APIURL.MAINHOSTURL + methodName
        strURL = strURL.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!

        print("API: \(strURL)")
        print("Parameters: \(parameters)")
        
       
        AF.request(strURL, method: httpMethod, parameters: parameters, encoding: JSONEncoding.default, headers: headers).responseJSON { (response) in
           
            switch response.result {
                
            case .success( _):
                //to get JSON return value
                if let result = response.value {
                    if let JSON = result as? NSDictionary {
                        print("\(methodName) = \(JSON)")
                        completion(JSON as! [String : AnyObject], true)
                    }
                }
                break
                
            case .failure(let encodingError ):
                
                print("Error = \(encodingError)")
                let temp = NSDictionary.init(object: encodingError.localizedDescription, forKey: "message" as NSCopying)
                completion(temp as! [String : AnyObject], false)
                break
            }
        }
    }
    
    class func callWebServiceWithoutParameterGet(methodName: String, completion: @escaping ((_ respose: [String: AnyObject],_ success: Bool) -> Void ))
    {
        let headers: HTTPHeaders = ["Content-Type" : "application/json"]
        
        var strURL = ""
        strURL = APIURL.MAINHOSTURL + methodName

        strURL = strURL.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!

        
        print("API: \(strURL)")
        
        AF.request(strURL, method: .get, encoding: URLEncoding.httpBody, headers: headers).responseJSON { response in
            
            switch response.result {
                
            case .success( _):
                //to get JSON return value
                if let result = response.value {
                    let JSON = result as! NSDictionary
                    print("\(methodName) = \(JSON)")
                    completion(JSON as! [String : AnyObject], true)
                }
                break
                    
            case .failure(let encodingError ):
                
                print("Error = \(encodingError)")
                let temp = NSDictionary.init(object: encodingError.localizedDescription, forKey: "message" as NSCopying)
                completion(temp as! [String : AnyObject], false)
                break
            }
        }
    }
    
    class func callWebServiceWithParameterGet(methodName: String, withParameter parameters: Parameters, completion: @escaping ((_ respose: [String: AnyObject],_ success: Bool) -> Void ))
    {
        let headers: HTTPHeaders = ["Content-Type" : "application/json"]
        
        var strURL = ""
        strURL = APIURL.MAINHOSTURL + methodName

        
        for (index,value) in parameters.enumerated()
        {
            if index == 0
            {
                strURL = "\(strURL)?\(value.key)=\(value.value)"
            }
            else
            {
                strURL = "\(strURL)&\(value.key)=\(value.value)"
            }
        }
        strURL = strURL.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!


        print("API: \(strURL)")
        print("Parameters: \(parameters)")
        
        AF.request(strURL, method: .get, encoding: URLEncoding.httpBody, headers: headers).responseJSON { (response) in
            
            switch response.result {
            
            case .success( _):
                //to get JSON return value
                if let result = response.value {
                    let JSON = result as! NSDictionary
                    print("\(methodName) = \(JSON)")
                    completion(JSON as! [String : AnyObject], true)
                }
                break
                
            case .failure(let encodingError ):
                
                print("Error = \(encodingError)")
                let temp = NSDictionary.init(object: encodingError.localizedDescription, forKey: "message" as NSCopying)
                completion(temp as! [String : AnyObject], false)
                break
            }
        }
    }
    
    class func callWebServiceWithoutParameterDelete(methodName: String, completion: @escaping ((_ respose: [String: AnyObject],_ success: Bool) -> Void ))
    {
        let headers: HTTPHeaders = ["Content-Type" : "application/json"]
        
        var strURL = ""
        strURL = APIURL.MAINHOSTURL + methodName

        strURL = strURL.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!

        
        print("API: \(strURL)")
        
        AF.request(strURL, method: .delete, encoding: URLEncoding.httpBody, headers: headers).responseJSON { response in
            
            switch response.result {
                
            case .success( _):
                //to get JSON return value
                if let result = response.value {
                    let JSON = result as! NSDictionary
                    print("\(methodName) = \(JSON)")
                    completion(JSON as! [String : AnyObject], true)
                }
                break
                    
            case .failure(let encodingError ):
                
                print("Error = \(encodingError)")
                let temp = NSDictionary.init(object: encodingError.localizedDescription, forKey: "message" as NSCopying)
                completion(temp as! [String : AnyObject], false)
                break
            }
        }
    }
    
    class func callWebServiceWithParameterDelete(methodName: String, withParameter parameters: Parameters, completion: @escaping ((_ respose: [String: AnyObject],_ success: Bool) -> Void ))
    {
        let headers: HTTPHeaders = ["Content-Type" : "application/json"]
        
        var strURL = ""
        strURL = APIURL.MAINHOSTURL + methodName

        strURL = strURL.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!

        print("API: \(strURL)")
        print("Parameters: \(parameters)")
        
        AF.request(strURL, method: .delete, parameters: parameters, encoding: JSONEncoding.prettyPrinted, headers: headers).responseJSON { (response) in
            
            switch response.result {
                
            case .success( _):
                //to get JSON return value
                if let result = response.value {
                    let JSON = result as! NSDictionary
                    print("\(methodName) = \(JSON)")
                    completion(JSON as! [String : AnyObject], true)
                }
                break
                
            case .failure(let encodingError ):
                
                print("Error = \(encodingError)")
                let temp = NSDictionary.init(object: encodingError.localizedDescription, forKey: "message" as NSCopying)
                completion(temp as! [String : AnyObject], false)
                break
            }
        }
    }
    
    class func callWebServiceWithParameterToUploadMedia(methodName: String, withParameter parameters: Parameters, httpMethod: HTTPMethod, completion: @escaping ((_ respose: [String: AnyObject],_ success: Bool) -> Void ))
    {
        let headers: HTTPHeaders = ["Content-Type" : "application/json"]
        
        var strURL = ""
        
        strURL = APIURL.MAINHOSTURL + methodName

        strURL = strURL.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!

        print("API: \(strURL)")
        print("Parameters: \(parameters)")
        
        AF.upload(multipartFormData: { multipartFormData in
            //multipartFormData.append(mediaData, withName: mediaName , fileName: "file.jpeg", mimeType: "image/jpeg")
            if parameters.count > 0
            {
                for (key, value) in parameters
                {
                    if let data = ((value as AnyObject) as? [[String:Any]])
                    {
                        let arrData =  try! JSONSerialization.data(withJSONObject: data, options: .prettyPrinted)
                        multipartFormData.append(arrData, withName: key as String)
                    }
                    else if let data = ((value as AnyObject) as? [Any])
                    {
                        //multipartFormData.append(data.description.data(using: String.Encoding.utf8)!, withName: key)

                        let arrData =  try! JSONSerialization.data(withJSONObject: data, options: .prettyPrinted)
                        multipartFormData.append(arrData, withName: key as String)

                        //multipartFormData.append(arrData.description.data(using: String.Encoding.utf8)!, withName: key as String)

                    }
                    else
                    {
                        multipartFormData.append("\(value)".data(using: String.Encoding.utf8)!, withName: key as String)
                    }
                }
            }
        }, to: strURL, method: httpMethod , headers: headers) .response { (response) in
            
            switch response.result {
               
            case .success( _):
                //to get JSON return value
                if let data = response.value {
                    if data != nil {
                        do {
                            let dicFromData = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers)
                            
                            if let JSON = dicFromData as? [String: AnyObject] {
                                print("\(methodName) = \(JSON)")
                                completion(JSON, true)
                            }
                        } catch{
                            print(error)
                            let temp = NSDictionary.init(object: error.localizedDescription, forKey: "message" as NSCopying)
                            completion(temp as! [String : AnyObject], false)
                        }
                    }
                }
                break
                
            case .failure(let encodingError ):
                
                print("Error = \(encodingError)")
                let temp = NSDictionary.init(object: encodingError.localizedDescription, forKey: "message" as NSCopying)
                completion(temp as! [String : AnyObject], false)
                break
            }
        }
    }
}

在你的视图控制器中使用...

let params: Parameters =
        [
            "param1": "data1",
            "param2": "data2"
        ]
            AlamoFireCommon.callWebServiceWithParameterToUploadMedia(methodName: "", withParameter: params,httpMethod: .post) { (response, success) in
              
              if success
              {
                  var status = ""
                  if let code = response["status"] as? String
                  {
                      status = code
                  }
                  
                  if status == "true"
                  {
                    if let Documents = response["data"] as? [[String : Any]]
                    {
                        //Your Data
                    }
                  }
                  else
                  {
                      if let str = response["message"] as? String
                      {
                        //Show alert
                      }
                      else
                      {
                        //show alert
                      }
                  }
              }
              else
              {
                  if let str = response["message"] as? String
                  {
                    //Show alert
                  }
                  else
                  {
                      //Show alert
                  }
              }
          }

-1

只是代码的一部分,但试试看

let req = Alamofire.request(url, method: .get, parameters: nil)

然后,您可以通过使用以下方式处理响应代码:

req.response?.statusCode

并通过例如处理响应

req.responseString(completionHandler: <#T##(DataResponse<String>) -> Void#>)
  or
req.responseJSON(completionHandler: <#T##(DataResponse<Any>) -> Void#>)

你有很好的例子 这里


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接