Swift: как сохранить объект Decodable.Protocol в переменную?
В моем приложении несколько контроллеров имеют очень похожую структуру кода, различия минимальны, поэтому для оптимизации я решил создать основу для этих контроллеров, и унаследовать каждый конкретный контроллер от этой основы.
У меня есть функция для отправки сетевых запросов и обработки ответа, я передаю структуру ответа в качестве параметра этой функции, чтобы функция возвращала мне готовую структуру ответа. Каждая такая структураDecodable
.
Пример такой конструкции:
struct APIAnswerUserActivity: Decodable {
let status: String
let code: Int?
let data: [UserActivity]?
let total: Int?
}
Функция для сетевых запросов, объект (структура) Decodable.Protocol
тип принимается как jsonType
параметр:
public func networkRequest<T: Decodable> (
url: String,
timeout: Double = 30,
method: URLMethods = .GET,
data: [String : String]? = nil,
files: [URL]? = nil,
jsonType: T.Type,
success: @escaping (T) -> Void,
failure: @escaping (APIError) -> Void
) -> URLSessionDataTask { ... }
В основном контроллере есть несколько параметров, которые я переопределяю путем переопределения в дочерних контроллерах, один из этих параметров должен быть объектом типа Decodable
чтобы общая функция корректно получала данные. Структуры JSON ответа очень похожи, но все же немного отличаются, общую структуру для них создать нельзя, потому что данные все же немного отличаются.
Если в главном контроллере сделать так:
public var decodableType: Decodable.Type {
return APIAnswerUserActivity.self
}
Это будет работать, и можно переопределить типы, но сетевая функция не принимает это, ей требуется Decodable.Protocol
объект. Если типdecodable.Protocol
указывается для переменной decodableType
, то добавить APIAnswerUserActivity.self
, который спокойно принимается при вызове функции networkRequest.
Как быть в такой ситуации? Надеюсь, что мне удалось правильно и четко изложить суть своей проблемы. Благодарность!
1 ответ
@Владислав Артемьев, я все еще не уверен, что полностью понимаю проблему, потому что вы не поделились кодом, который принимает Decodable
класс. Но, похоже, проблема заключается в том, как пройтиDecodable
класс.
Я надеюсь, что следующее может помочь прояснить, как вы можете наложить правильное ограничение на общий и как вы должны объявить переменную. Вы можете вставить его в детскую площадку и поэкспериментировать.
import Foundation
struct FakeToDo: Decodable {
var userId: Int
var id: Int
var title: String
var completed: Bool
}
enum URLMethods {
case GET
case POST
}
func networkRequest<T: Decodable> (
url: String,
timeout: Double = 30,
method: URLMethods = .GET,
data: [String : String]? = nil,
files: [URL]? = nil,
jsonType: T.Type,
success: @escaping (T) -> Void,
failure: @escaping (Error) -> Void
) -> URLSessionDataTask {
let task = URLSession.shared.dataTask(with: URL(string: url)!, completionHandler: { (data, response, error) in
if error != nil {
failure(error!)
return
}
guard let data = data else { return }
let decoder = JSONDecoder()
guard let value = try? decoder.decode(T.self, from: data) else { return }
// get back on the main queue for UI
DispatchQueue.main.async {
success(value)
}
})
return task
}
class Example<T> where T: Decodable {
let type: T.Type
init(_ type: T.Type) {
self.type = type
}
public var decodableType: T.Type {
return type
}
}
let decodableType = Example(FakeToDo.self).decodableType
let url = "https://jsonplaceholder.typicode.com/todos/1"
let task = networkRequest(url: url, jsonType: decodableType,
success: { value in print(value) },
failure: { error in print(error) })
task.resume()