【问题标题】:How to check and cast a type to Decodable in swift如何快速检查类型并将其转换为 Decodable
【发布时间】:2020-08-27 03:12:17
【问题描述】:

我想发出一个网络请求,其中我可以控制响应是 JSON 原始值 [String: AnyDecodable

示例如下:

func reqest<T>(endpoint: EndPoint, completion: @escaping (Result<T, Error>) -> Void) {
   session.dataTask(with: request) { (data, response, error) in

       if T.self is Decodable.Type {
           try? JSONDecoder().decode(T.self, from: data)
       } else {
           try? JSONSerialization.jsonObject(with: data, options: .mutableLeaves)
       }
   }.resume()
}

然后当我想要 JSON 值时,我只需调用 T 作为 [String: Any] 或使用任何模型确认 Decodable 协议。

问题是针对这一行的:

try? JSONDecoder().decode(T.self, from: data)

如何将T 转换为Decodable

我知道使用:

func reqest<T: Decodable >(endpoint: EndPoint, completion: @escaping (Result<T, Error>) -> Void)

[String: Any] 不是Decodable

或者任何更好的解决方案来实现我想要的?谢谢。

【问题讨论】:

  • [String: Any] 不符合 Decodable。我认为您无法为两者创建一个方法。
  • 这种运行时的类型检查非常unswifty。声明两个具有不同约束的方法。忽略任何错误都是非常糟糕的做法。而.mutableLeaves 在 Swift 中毫无意义。

标签: ios swift decode


【解决方案1】:

我建议对 reqest(endpoint:completion:) 函数使用重载来实现你想要的。

我想要的结构是这样的:

enum ResponseError: Error {
    case noData
    case typeMismatch
}

func reqest<T>(endpoint: EndPoint, completion: @escaping (Result<T, Error>) -> Void) {
    baseReqest(endpoint: endpoint) { result in
        switch result {
        case .success(let data):
            do {
                guard let json = try JSONSerialization.jsonObject(with: data) as? T else {
                    completion(.failure(ResponseError.typeMismatch))
                    return
                }
                completion(.success(json))
            } catch {
                completion(.failure(error))
            }
        case .failure(let error):
            completion(.failure(error))
        }
    }
}

func reqest<T: Decodable>(endpoint: EndPoint, completion: @escaping (Result<T, Error>) -> Void) {
    baseReqest(endpoint: endpoint) { result in
        switch result {
        case .success(let data):
            do {
                let response = try JSONDecoder().decode(T.self, from: data)
                completion(.success(response))
            } catch {
                completion(.failure(error))
            }
        case .failure(let error):
            completion(.failure(error))
        }
    }
}

private func baseReqest(endpoint: EndPoint, completion: @escaping (Result<Data, Error>) -> Void) {
    session.dataTask(with: request) { (data, response, error) in
        if let error = error {
            completion(.failure(error))
            return
        }
        guard let data = data else {
            completion(.failure(ResponseError.noData))
            return
        }
        completion(.success(data))
    }.resume()
}

这样您就可以在baseReqest(endpoint:completion:) 函数中使用通用响应处理代码,而仅将其他两个函数中的响应解析分开。

那么调用reqest(endpoint:completion:)函数就可以了

  • 使用[String: Any] 作为响应类型:
reqest(endpoint: endpoint) { (result: Result<[String: Any], Error>) in
    // Handle result
}
  • 使用[[String: Any]] 作为响应类型:
reqest(endpoint: endpoint) { (result: Result<[[String: Any]], Error>) in
    // Handle result
}
  • 并且还使用Decodable 对象作为响应类型:
struct Response: Decodable {}

reqest(endpoint: endpoint) { (result: Result<Response, Error>) in
    // Handle result
}

【讨论】:

  • 是的,看来还得再添加一种方法来实现。
【解决方案2】:

以 Swift 的方式进行。使用 struct Codable(它为您提供了 Decodable)。

例如:

 struct testStruct: Codable {
   public var testString:String!
   public var testAny:Any!
   init(
          testString:String!,
          testAny:Any!
       )
   {
       self.testString = testString
       self.testAny = testAny
   }

然后你用这个初始化它:

var testStructToUse:[testStruct] = []

从这里你可以用 append 方法填充它:

testStructToUse.append(testStruct(testString: "any String", testAny: "any value"))

并使用 JSONencoder 进行编码

let jsonData = try JSONEncoder().encode(testStruct)

【讨论】:

  • 上面的代码真的有效吗? Any 不应该是可编码的。
猜你喜欢
  • 2016-01-22
  • 1970-01-01
  • 1970-01-01
  • 2019-03-30
  • 1970-01-01
  • 2010-12-25
  • 2017-11-17
  • 2019-01-29
相关资源
最近更新 更多