iOS создать универсальный c запрос Alamofire с использованием swift - PullRequest
3 голосов
/ 21 февраля 2020

Недавно я начал изучать iOS разработку приложений с использованием swift, поэтому я новичок в этом. Я хочу реализовать rest api call в swift и обнаружил, что мы можем добиться этого, используя URLRequest. Поэтому я написал generic method для вызова всех типов (например, get, put, post) остальных 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))
            }
        }
    }
}

это то, как я называю его методом из controller

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 et c с сервера. Итак, я ищу, чтобы преобразовал этот запрос API (URLRequest) в обобщенный c Alamofire запрос с обработкой ошибок, такой как 401, 404 et c. У меня установлено Alamofire стручков. Есть ли кто-нибудь, кто разработал обобщенный c Alamofire метод запроса с декодированием и обработкой ошибок ?

Заранее спасибо:)

Ответы [ 9 ]

2 голосов
/ 24 февраля 2020

Я использую EVReflection с alamofire, и я думаю, что это одна из лучших комбинаций для работы.

Используйте URLRequestConvertible протокол Alamofire.

Это то, что я следую.

Только для справочных целей.

Создайте перечисление для всей вашей конечной точки и подтвердите это перечисление для 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()
        }
    }
}

Теперь создайте одну универсальную c функцию, которая принимает 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)
                }
            }
        })
    }
}

Теперь создайте одну обобщенную функцию c, которая примет ваш ответ и обработает для вас ошибку. Как-то так.

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.

        }
    }

Теперь, наконец, как правильно его использовать ??! В самом деле, теперь это очень просто, всего две строки кода, и вы готовы к go.

GenericApiCallForObject(router: Router.Login(["xyz":"xyz"])) { (response : GeneralModel) in
    print(response)
}

Обратите внимание, что это будет работать только в том случае, если вы получаете объект в ответ. Если есть массив или строка, вы должны сделать отдельную функцию для этого, и процедура для этого такая же, как указано выше. Вы получите ответ только в случае успеха, иначе функция HandleResponseWithErrorForObject автоматически обработает его для вас. Кроме того, некоторые переменные могут отсутствовать в приведенном выше объяснении.

1 голос
/ 28 февраля 2020

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 года). Этот класс охватывает почти каждый метод HTTP с возможностью отправки данных в формате Application / JSON или обычном режиме. С этим классом вы получаете большую гибкость, и он автоматически преобразует ответ в ваш объект Swift.

Посмотрите на метод init этого класса:

  1. data: [String, Any] = В этом случае вы поместите данные своей формы.

  2. headers: [String: String] = Здесь вы можете отправлять пользовательские заголовки, которые хотите отправить, вместе с request

  3. url = Здесь вы можете указать полный URL, вы можете оставить его пустым, если вы уже определили baseurl в классе. это удобно, когда вы хотите использовать REST-сервис, предоставленный третьей стороной. Примечание: если вы заполняете URL, тогда следующим параметром service должно быть nil

  4. service: services = Это перечисление, определенное в самом NetworkClass. они служат конечными точками. Посмотрите в методе init, если URL-адрес равен nil, но служба не равна nil, то в конце базового URL-адреса добавляется полный URL-адрес, пример будет предоставлен.

  5. method: HTTPMethod = здесь вы можете указать, какой метод HTTP должен использовать запрос.

  6. isJSONRequest = по умолчанию установлено в значение true. если вы хотите отправить обычный запрос, установите для него значение false.

В методе init вы также можете указать общие данные или заголовки, которые вы хотите отправлять при каждом запросе, например номер версии вашего приложения, iOS Version et c

Теперь рассмотрим метод execute: это универсальная c функция, которая будет возвращать объект swift по вашему выбору, если ответ успешен. Он напечатает ответ в виде строки в случае, если не удалось преобразовать ответ в ваш быстрый объект. если код ответа не попадает в диапазон 200-299, то произойдет сбой, и вы получите полное описание отладки для получения подробной информации.

Использование:

говорит, что мы имеем следующую структуру:

struct Post: Codable{
    let userId: Int
    let id: Int
    let title: String
    let body: String
}

Обратите внимание на базовый URL, определенный в NetworkClass https://jsonplaceholder.typicode.com/

Пример 1. Отправка сообщения HTTP с типом содержимого Application / JSON

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")

HTTP 400 Запрос

NetworkCall (данные: ["email": "peter@klaven"], URL: "https://reqres.in/api/login", метод: .post, isJSONRequest: false) .executeQuery () {(result: Result) в результате переключения {case .success (let post): печать (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}

с пользовательскими заголовками

NetworkCall (data: ["username": "sahil. manchanda2@gmail.com"], заголовки: ["custom-header-key": "custom-header-value" "], url:" https://httpbin.org/post ", метод: .post) .executeQuery () {(result: Result) в результате переключения {case .success (let data): print (data) case. ошибка (пусть ошибка): печать (ошибка)}}

вывод:

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 в конце, я пытался передать [String: Any] в executeQuery, но поскольку Any не подтверждает возможность кодирования, мне пришлось использовать String.

1 голос
/ 25 февраля 2020

Я разделяю указанную c часть для обработки ошибок на моем REST API. Он будет декодироваться внутри следующего блока, и, вероятно, вы можете использовать его для справки.

Как видите, очень просто получить код и перевести его в перечисление. Alamofire позволяет это, но это зависит от вашей версии библиотеки. Иногда ваш API REST зависит от того, как обрабатывать ошибки внутренне, они не могут генерировать код, например, если его 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))
                                }
                            }
                        })
0 голосов
/ 28 февраля 2020

Это то, над чем я работаю! Еще не закончено, но может решить вашу проблему. Вы можете обновить его до того, что хотите.

typealias

 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
      }

Указатель ответа:

класс 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 голосов
/ 27 февраля 2020
import Foundation

import UIKit import Alamofire import Swifty JSON

класса 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)
        }
    }
}

}

0 голосов
/ 27 февраля 2020

только часть кода, но попробуйте

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#>)

у вас есть хороший пример здесь

0 голосов
/ 25 февраля 2020

Лучший способ - создать пользовательский метод проверки с использованием расширения 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))
        }
    }

С клиентом с функцией generi c, где generic c можно декодировать с помощью нашей пользовательской проверки.

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 голосов
/ 25 февраля 2020

Я создаю Generi c Сетевой уровень в своих проектах. Например, проверьте эту ссылку

0 голосов
/ 21 февраля 2020

Возможно, вам нужна эта функция, которая использует alamofilre Session Manager для выполнения запросов. Вы также можете установить cookie ant headers et c .. для этого менеджера сеансов, чтобы они оставались для ваших остальных запросов.

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)"
    }

Для приготовления ie

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

Очистить заголовки

func clearHeaders(){
    headers = [:]
}

И имейте в виду, что это одноэлементный класс, поэтому, когда вы что-либо изменяете, если сервер не вносит какие-либо изменения, у вас все еще есть конфигурация, напр. заголовки

...