gpt4 book ai didi

iOS 使用 swift 创建通用 Alamofire 请求

转载 作者:行者123 更新时间:2023-12-02 00:24:27 26 4
gpt4 key购买 nike

最近我开始学习使用 swift 开发 iOS 应用程序,所以我对它很陌生。我想在 swift 中实现 Rest api 调用,发现我们可以使用 URLRequest 来实现这一点。所以我编写了通用方法来调用其余API的所有类型(如get、put、post),如下所示。

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 等。所以我正在寻找的是将此 api (URLRequest) 请求转换为通用 Alamofire 请求,并进行诸如 401 之类的错误处理, 404 等。我已经安装了 Alamofire pod。有没有人开发过带有解码和错误处理的通用Alamofire请求方法?

提前致谢:)

最佳答案

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 格式或普通格式发送数据。通过这个类,您可以获得很大的灵 active ,它会自动将响应转换为您的 Swift 对象。

看看这个类有init方法:

  1. data: [String,Any] = 您将在其中放置表单数据。

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

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

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

  5. method:HTTPMethod = 此处您可以指定请求应使用哪种 HTTP 方法。

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

在 init 方法中,您还可以指定要随每个请求发送的通用数据或 header ,例如您的应用程序版本号、iOS 版本等

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

用法:

假设我们有以下结构:

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

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

示例 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")
  • HTTP 400 请求

    NetworkCall(数据: ["email":"peter@klaven"], url: "https://reqres.in/api/login ", 方法: .post, isJSONRequest: false).executeQuery(){ (结果:结果)中 切换结果{ 案例.成功(让帖子): 打印(发布) 案例.失败(让错误): 打印(错误) }}

  • 输出:

    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}
  • 带有自定义 header

    NetworkCall(数据:[“用户名”:“sahil.manchanda2@gmail.com”], header :[“自定义 header 键”:“自定义 header 值”],网址:“https://httpbin.org/post” ,方法:.post).executeQuery(){(结果:结果)中 切换结果{ 案例.成功(让数据): 打印(数据) 案例.失败(让错误): 打印(错误) } }

  • 输出:

    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。

    关于iOS 使用 swift 创建通用 Alamofire 请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60334798/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com