Возвращение типа generi c из dataTask для URLRequest - PullRequest
0 голосов
/ 26 февраля 2020

Я пытаюсь создать обобщенную функцию c, которая возвращает массив заданного типа после завершения dataTask URLSession.

У меня есть этот Decodable:

struct Todo: Hashable, Codable, Identifiable {
    var id: Int
    var title: String
    var completed: Bool
}

И эта функция:

func loadFrom<T: Decodable>(url: String, memberType: T, completionHandler: (T?) -> Void) {
    guard let url = URL(string: url) else {
        completionHandler(nil)
    }

    URLSession.shared.dataTask(with: url) {data, response, error in
        guard let data = data else {
            fatalError("No data returned")
        }

        do {
            let decoder = JSONDecoder()
            let results = try decoder.decode(T.self, from: data)

            completionHandler(results)
        }catch {
            fatalError("Couldn't parse data")
        }
    }.resume()
}

loadFrom(url: "https://jsonplaceholder.typicode.com/todos?completed=true", memberType: Todo) {response in
    ...
}

ошибка: Swift Scratchpad.playground: 61: 88: ошибка: аргумент тип 'Todo.Type' не соответствует ожидаемому типу 'Decodable'

Я встречал похожие проблемы, которые указывают на неспособность компилятора синтезировать методы, которые протокол Decodable требует для соответствия, но работает аналогичный метод, который назначен типу Decodable, а не указан в качестве параметра:

func loadFile<T: Decodable>(file: String) -> T {
...
}

var todos: [Todo] = loadFile(file: "todos.json")
print(todos[0].title) => "The todo title"

Я предполагаю, что причиной того, что мой loadFrom не указал тип возвращаемого значения, является причина но я не понимаю почему. Можно ли предоставить достаточно контекста для этого кода для компиляции?

Ответы [ 2 ]

1 голос
/ 26 февраля 2020

В методе loadFrom(url:memberType:completionHandler:),

1. Используйте T.Type вместо T в качестве типа данных для memberType

2. Добавьте @escaping к completionHandler

func loadFrom<T: Decodable>(url: String, memberType: T.Type, completionHandler: @escaping ((T?)->())) 

Вызовите метод как,

loadFrom(url: "https://jsonplaceholder.typicode.com/todos?completed=true", memberType: Todo.self) {response in
     ...
}

Также добавьте return в операторе guard,

guard let url = URL(string: url) else {
    completionHandler(nil)
    return //here...
}
0 голосов
/ 26 февраля 2020

Вы должны объявить

func loadFrom<T: Decodable>(url: String, memberType: T.Type, completionHandler: @escaping (T?) -> Void) {

и назвать его

loadFrom(url: "https://jsonplaceholder.typicode.com/todos?completed=true", memberType: Todo.self) {response in
    ...
}

И вместо случайных фатальных ошибок добавить лучший тип результата в иметь возможность возвращать также ошибки и ловить неверный URL перед вызовом метода

func loadFrom<T: Decodable>(url: URL, memberType: T.Type, completionHandler: @escaping (Result<T,Error>) -> Void) {
    URLSession.shared.dataTask(with: url) {data, _, error in
        if let error = error {
            completionHandler(.failure(error))
        } else {
           completionHandler( Result{ try JSONDecoder().decode(T.self, from: data!)})
        }
    }.resume()
} 
...