【问题标题】:What type to use for generic decodable class通用可解码类使用什么类型
【发布时间】:2019-02-20 16:23:44
【问题描述】:

我有一些 Swift 基础知识,现在我正在尝试学习 iOS 开发。我目前正在一个小型应用程序中工作,该应用程序将询问我制作的 API 上的资源,该 API 返回由以下内容制成的 json:

struct A : Codable {
    let name: String
    let age: Int
}

struct B : Codable {
    let something: String
}

API 和应用程序都定义了这些结构。由于我总是在查询同一个 API,因此我想包装向 API 询问一些资源并对其进行解码的部分,以便在回调中使用该结构的实例。这是这个方法:

static func getContent(urlRequest: URLRequest, decodable: Decodable, completion: @escaping (Codable?, ErrorEnum?)->Void) {
    let config = URLSessionConfiguration.default
    let session = URLSession(configuration: config)

    let task = session.dataTask(with: urlRequest) {
        data, response, error in

        guard let data = data else {
            completion(nil, .noData) // Handling errors in an enum
            return 
        }
        let decoder = JSONDecoder()
        if let full = try? decoder.decode(decodable, from: data) {
            completion(full, nil)
        }
    }

    task.resume()
}

我的问题与可解码参数有关。这显示错误并阻止我编译应用程序。在 StackOverflow 上找到一些资源后,我尝试将参数更改为

static func getContent(urlRequest: URLRequest, decodable: Decodable.Type, completion: @escaping (Codable?, ErrorEnum?)->Void)

我也尝试保持这样的参数,而是在解码参数内部进行更改

if let full = try? decoder.decode(decodable, from: data) {
    completion(full, nil)
}

但似乎没有什么能让编译器满意...查看 Swift 源代码中的 decode 方法并没有太大帮助,因为它需要 T.Type where T is Decodable

我的愿望是能够按如下方式使用它:

static func getA() {
    guard let url = URL(string: "http://localhost/a") else { return }

    let urlRequest = URLRequest(url: url)
    getContent(urlRequest: urlRequest, decodable: A.self) {
        a, error in

        guard a = a else { return }
        print(a.name!)
    }
}

你知道我怎么能做到这一点吗?我也真的不知道如何调用这类参数或在谷歌上搜索什么可以引导我找到答案(缺乏词汇)。

谢谢!

【问题讨论】:

    标签: swift codable decodable


    【解决方案1】:

    试试这个,只需添加Codablegeneric .Type 并使用其类型作为参数来传递foo.self

    static func getContent<T: Codable>(urlRequest: URLRequest, decodable: T.Type, completion: @escaping (T?, ErrorEnum?)->Void) {
    let config = URLSessionConfiguration.default
    let session = URLSession(configuration: config)
    
    let task = session.dataTask(with: urlRequest) {
        data, response, error in
    
        guard let data = data else {
            completion(nil, .noData) // Handling errors in an enum
            return
        }
        let decoder = JSONDecoder()
        if let full = try? decoder.decode(decodable, from: data) {
            completion(full, nil)
        }
    }
    task.resume()
    }
    

    【讨论】:

    • decodable参数是多余的,可以写成decoder.decode(T.self,...
    • 你能解释一下你的意思吗?
    • 它的参数名称
    • 删除 , decodable: T.Type 并将 if let full = try? decoder.decode(decodable, from: data) { 替换为 if let full = try? decoder.decode(T.self, from: data) {。如果解码失败,您也应该调用 completion 并报错
    • 它是参数,因为它应该与返回对象的类型相同,我在这里看不到重点,但我稍后会研究它,感谢您的建议
    【解决方案2】:

    你可以用这个:

    func genericRequest<T: Decodable>(_ request: URLRequest, completion: @escaping APIGenericRequestCompletion<T>) {
    
            Alamofire.request(request).responseData { (response) in
    
    
                guard let data = response.data else {
                    completion(nil)
                    return
                }
    
    
                do {
                    let decodedObject = try JSONDecoder().decode(T.self, from: data)
    
                    completion(decodedObject)
                } catch {
                    completion(nil)
                }
    
            }
        }
    

    APIGenericRequestCompletion 是:

    typealias APIGenericRequestCompletion<T: Decodable> = (_ result: T?) -> Void
    

    然后你将它用作:

    genericRequest(request) { (decodableObjectResponse) in
                // your code here
    }
    

    【讨论】:

    • 非常感谢您的回答 :) 您知道为什么它适用于 T.Type 其中 T 是 Codable 而不是 Codable.Type 吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-22
    • 2011-03-06
    • 1970-01-01
    • 2016-04-05
    • 2017-04-19
    相关资源
    最近更新 更多