【问题标题】:Codable for API request可用于 API 请求的编码
【发布时间】:2019-07-01 06:25:52
【问题描述】:

我如何通过可编码来发出相同的 API 请求? 在我的应用中,这个函数在每个调用 API 的视图中重复。

func getOrders() {

        DispatchQueue.main.async {

            let spinningHUD = MBProgressHUD.showAdded(to: self.view, animated: true)
            spinningHUD.isUserInteractionEnabled = false

            let returnAccessToken: String? = UserDefaults.standard.object(forKey: "accessToken") as? String

            let access  = returnAccessToken!
            let headers = [
                "postman-token": "dded3e97-77a5-5632-93b7-dec77d26ba99",
                "Authorization": "JWT \(access)"
            ]

            let request = NSMutableURLRequest(url: NSURL(string: "https://somelink.com")! as URL,
                                              cachePolicy: .useProtocolCachePolicy,
                                              timeoutInterval: 10.0)

            request.httpMethod          = "GET"
            request.allHTTPHeaderFields = headers

            let session  = URLSession.shared
            let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
                if (error != nil) {
                    print(error!)

                } else {
                    if let dataNew = data, let responseString = String(data: dataNew, encoding: .utf8) {
                        print("----- Orders -----")
                        print(responseString)
                        print("----------")

                        let dict = self.convertToDictionary(text: responseString)
                        print(dict?["results"] as Any)
                        guard let results = dict?["results"] as? NSArray else { return }
                        self.responseArray = (results) as! [HomeVCDataSource.JSONDictionary]

                        DispatchQueue.main.async {
                            spinningHUD.hide(animated: true)
                            self.tableView.reloadData()
                        }

                    }

                }

            })

            dataTask.resume()
        }
    }

【问题讨论】:

  • 使用 Codable 添加您要解析的 JSON 响应。
  • 你需要在你的模型中实现func encode(to encoder: Encoder) throwsconvenience init(from decoder: Decoder) throws,然后你可以做类似var arrayOfComments : [CommentModel] = try JSONDecoder().decode([CommentModel].self, from: response.result.value!)的事情
  • 信息:您可以使用后台线程进行网络请求。更新 UI 的主线程
  • @ReinierMelian 请您详细说明一下吗?
  • 将您的 JSON 响应粘贴到此站点上,您将获得 Codable 数据 json4swift.com

标签: ios swift codable


【解决方案1】:

我建议做以下事情

  1. 如下创建基础服务

import UIKit
import Foundation

enum MethodType: String {
    case get     = "GET"
    case post    = "POST"
    case put     = "PUT"
    case patch   = "PATCH"
    case delete  = "DELETE"
}

class BaseService {

    var session: URLSession!

    // MARK: Rebuilt Methods
    func FireGenericRequest<ResponseModel: Codable>(url: String, methodType: MethodType, headers: [String: String]?, completion: @escaping ((ResponseModel?) -> Void)) {
        UIApplication.shared.isNetworkActivityIndicatorVisible = true

        // Request Preparation
        guard let serviceUrl = URL(string: url) else {
            print("Error Building URL Object")
            return
        }
        var request = URLRequest(url: serviceUrl)
        request.httpMethod = methodType.rawValue

        // Header Preparation
        if let header = headers {
            for (key, value) in header {
                request.setValue(value, forHTTPHeaderField: key)
            }
        }

        // Firing the request
        session = URLSession(configuration: URLSessionConfiguration.default)
        session.dataTask(with: request) { (data, response, error) in
            DispatchQueue.main.async {
                UIApplication.shared.isNetworkActivityIndicatorVisible = false
            }
            if let data = data {
                do {
                    guard let object = try? JSONDecoder().decode(ResponseModel.self , from: data) else {
                        print("Error Decoding Response Model Object")
                        return
                    }
                    DispatchQueue.main.async {
                        completion(object)
                    }
                }
            }
        }.resume()
    }

    private func buildGenericParameterFrom<RequestModel: Codable>(model: RequestModel?) -> [String : AnyObject]? {
        var object: [String : AnyObject] = [String : AnyObject]()
        do {
            if let dataFromObject = try? JSONEncoder().encode(model) {
                object = try JSONSerialization.jsonObject(with: dataFromObject, options: []) as! [String : AnyObject]
            }
        } catch (let error) {
            print("\nError Encoding Parameter Model Object \n \(error.localizedDescription)\n")
        }
        return object
    }

}


上面的类你可以在不同的场景中重用它,向它添加请求对象并传递你想要的任何类,只要你符合 Coddle 协议

  1. 创建符合 Coddle 协议的模型

class ExampleModel: Codable {
    var commentId : String?
    var content : String?

    //if your JSON keys are different than your property name
    enum CodingKeys: String, CodingKey {
        case commentId = "CommentId" 
        case content = "Content"
    }

}

  1. 使用端点常量子类化到 BaseService 的特定模型创建服务,如下所示

class ExampleModelService: BaseService<ExampleModel/* or [ExampleModel]*/> {

    func GetExampleModelList(completion: ((ExampleModel?)/* or [ExampleModel]*/ -> Void)?) {
        super.FireRequestWithURLSession(url: /* url here */, methodType: /* method type here */, headers: /* headers here */) { (responseModel) in
            completion?(responseModel)
        }
    }

}

  • 用法

class MyLocationsController: UIViewController {

    // MARK: Properties
    // better to have in base class for the controller
    var exampleModelService: ExampleModelService = ExampleModelService()

    // MARK: Life Cycle Methods
    override func viewDidLoad() {
        super.viewDidLoad()
        exampleModelService.GetExampleModelList(completion: { [weak self] (response) in
            // model available here
        })
    }
}

【讨论】:

    【解决方案2】:

    基本上,您需要在模型类中遵守Codable 协议,为此您需要实现两种方法,一种用于编码您的模型,另一种用于从JSON 解码您的模型

    func encode(to encoder: Encoder) throws
    
    required convenience init(from decoder: Decoder) throws
    

    之后,您将能够使用 Apple 提供的 JSONDecoder 类来解码您的 JSON,并返回一个数组(如果是这样的话)或您的模型类的一个对象。

    class ExampleModel: Codable {
        var commentId : String?
        var content : String?
    
        //if your JSON keys are different than your property name
        enum CodingKeys: String, CodingKey {
            case commentId = "CommentId" 
            case content = "Content"
        }
    
    }
    

    然后使用JSONDecoder 你可以像这样得到你的模型数组

    do {
        var arrayOfOrders : [ExampleModel] = try JSONDecoder().decode([ExampleModel].self, from: dataNew)                           
        }
        catch {
        }
    

    【讨论】:

    • 基本上你不需要实现协议方法。在默认实现中,方法是合成的。并且符合Codable的对象不需要是类,也不需要继承自NSObject。为什么属性声明为可选?
    • @vadian 你是对的,但如果你想拥有与 JSON 定义不同的属性名称,你需要定义CodingKeys,并实现方法协议或者我错了?关于 NSObject 你完全好的,我会删除的,谢谢
    • 添加CodingKeys不需要实现协议方法。
    【解决方案3】:

    首先,我可以建议您使用此应用程序 -quicktype- 将 json 文件转换为您想要的类或结构(可编码)。 enter link description here.

    之后,您可以创建一个通用函数来获取任何类型的可编码类并将其作为响应返回。

    func taskHandler<T:Codable>(type: T.Type, useCache: Bool, urlRequest: URLRequest, completion: @escaping (Result<T, Error>) -> Void) {
        let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
            if let error = error {
                print("error : \(error)")
            }
            if let data = data {
                do {
                    let dataDecoded = try JSONDecoder().decode(T.self, from: data)
                    completion(.success(dataDecoded))
                    // if says use cache, let's store response data to cache
                    if useCache {
                        if let response = response as? HTTPURLResponse {
                            self.storeDataToCache(urlResponse: response, urlRequest: urlRequest, data: data)
                        }
                    }
                } catch let error {
                    completion(.failure(error))
                }
            } else {
                completion(.failure(SomeError))
            }
        }
        task.resume()
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-08
      • 1970-01-01
      • 2013-01-25
      • 1970-01-01
      • 2021-09-28
      • 1970-01-01
      相关资源
      最近更新 更多