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

编程入门 行业动态 更新时间:2024-10-23 06:19:02
本文介绍了iOS使用Swift创建通用的Alamofire请求的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

最近,我开始使用swift学习iOS应用开发,因此我是新手。我想在Swift&中实现rest api调用发现我们可以使用 URLRequest 来实现这一目标。因此,我编写了通用方法来调用所有类型的rest api(例如 get,put,post ),如下所示。

Recently I have started learning iOS app development using swift so I am new to it. I want to implement rest api call in swift & found that we can achieve this using URLRequest. So I have written generic method to call all type(like get, put, post) of rest api as below.

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 = "api.testserver/" 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 } }) }

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

I am decoding server response in below model

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 等。因此,我要寻找的是将此api( URLRequest )请求转换为通用的 Alamofire 请求并进行错误处理,例如 401、404 等。我已经安装了 Alamofire 吊舱。是否有人开发了具有解码和解码功能的通用 Alamofire 请求方法错误处理?

This code is not able to handle error response like 401, 404 etc from server. So what I am looking for, is to convert this api (URLRequest)request to generic Alamofire request with error handling like 401, 404 etc. I have install Alamofire pods. Is there anyone who has developed generic Alamofire request method with decoding & error handling?

预先感谢:)

推荐答案

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

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

Here is my version(Using 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! = "jsonplaceholder.typicode/" 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方法,并可以选择以Application / JSON格式或常规格式发送数据。有了此类,您将获得很大的灵活性,它会自动将响应转换为您的Swift对象。

The above class uses latest Alamofire version (as of now Feb 2020), This class covers almost every HTTP Method with option to send data in Application/JSON format or normal. With this class you get a lot of flexibility and it automatically converts response to your Swift Object.

请看它具有的此类init方法:

Look at the init method of this class it has:

  • 数据:[String,Any] =在此,您将放置表单数据。

  • data: [String,Any] = In this you will put your form data.

    标头:[String:String] =在此,您可以发送要与请求一起发送的自定义标头

    headers: [String:String] = In this you can send custom headers that you want to send along with the request

    url =这里您可以指定完整的url,如果您已经在Class中定义了baseurl,则可以将其保留为空白。当您要使用第三方提供的REST服务时,它非常方便。注意:如果要填充网址,则下一个参数服务应该为nil

    url = Here you can specify full url, you can leave it blank if you already have defined baseurl in Class. it comes handy when you want to consume a REST service provided by a third party. Note: if you are filling the url then you should the next parameter service should be nil

    service:services =这是NetworkClass本身定义的枚举。这些充当端点。查看init方法,如果url为nil但服务不是nil,则它将附加在基本url的末尾以构成完整的URL,将提供示例。

    service: services = It's an enum defined in the NetworkClass itself. these serves as endPoints. Look in the init method, if the url is nil but the service is not nil then it will append at the end of base url to make a full URL, example will be provided.

    方法:HTTPMethod =在这里,您可以指定请求应使用的HTTP方法。

    method: HTTPMethod = here you can specify which HTTP Method the request should use.

    isJSONRequest =默认情况下设置为true。

    isJSONRequest = set to true by default. if you want to send normal request set it to false.

    在init方法中,您还可以指定公共数据或您希望随每个请求发送的标头,例如您的应用程序版本号,iOS版本等

    In the init method you can also specify common data or headers that you want to send with every request e.g. your application version number, iOS Version etc

    现在看一下execute方法:这是一个通用函数,如果响应成功,它将返回您选择的swift对象。如果无法将响应转换为swift对象,它将以字符串形式打印响应。如果响应代码不在200-299范围内,则将失败,并为您提供完整的调试说明,以获取详细信息。

    Now Look at the execute method: it's a generic function which will return swift object of your choice if the response is success. It will print the response in string in case it fails to convert response to your swift object. if the response code doesn't fall under range 200-299 then it will be a failure and give you full debug description for detailed information.

    用法:

    说我们有以下结构:

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

    请注意在NetworkClass https://jsonplaceholder.typicode中定义的基本URL。 com /

    Note the base url defined in NetworkClass jsonplaceholder.typicode/

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

    Example 1: Sending HTTP Post with content type 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请求

  • HTTP 400 Request

    NetworkCall(data:[ email: peter @ klaven],URL: reqres.in/api/login ,方法:.post,isJSONRequest:false).executeQuery( ){(结果:结果)在转换结果{ case .success(let post): print(post) case .failure(let error) :打印(错误)} }

    NetworkCall(data: ["email":"peter@klaven"], url: "reqres.in/api/login", method: .post, isJSONRequest: false).executeQuery(){ (result: Result) in switch result{ case .success(let post): print(post) case .failure(let error): print(error) } }

    输出: Service: reqres.in/api/login data: ["email": "peter@klaven"] Error Domain=[Request]: POST 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="report-uri.cloudflare/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="report-uri.cloudflare/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}

  • 带有自定义标头

  • with custom headers

    NetworkCall(data:[ username: sahil.manchanda2 @ gmail],标题:[ custom-header-key: custom-header-value],网址: httpbin/post ,方法:.post).executeQuery(){(结果:结果)在切换结果{情况下.success(让数据):打印(数据)情况。失败(出现错误):打印(错误)} }

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

    输出:

    Service: httpbin/post data: ["username": "sahil.manchanda2@gmail"] { "args": {}, "data": "{\"username\":\"sahil.manchanda2@gmail\"}", "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", "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" }, "origin": "182.77.56.154", "url": "httpbin/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。

    In the last example you can see typeMismatch at the end, I tried to pass [String:Any] in the executeQuery but since the Any doesn't confirm to encodable I had to use String.

  • 更多推荐

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

    本文发布于:2023-11-26 03:07:40,感谢您对本站的认可!
    本文链接:https://www.elefans.com/category/jswz/34/1632389.html
    版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
    本文标签:iOS   Swift   Alamofire

    发布评论

    评论列表 (有 0 条评论)
    草根站长

    >www.elefans.com

    编程频道|电子爱好者 - 技术资讯及电子产品介绍!