Swift: Как сохранить объект Decodable.Protocol в переменную? - PullRequest
1 голос
/ 12 января 2020

В моем приложении несколько контроллеров имеют очень похожую структуру кода, различия минимальны, поэтому для оптимизации я решил создать основу для этих контроллеров и наследовать каждый указанный c контроллер от базы.

У меня есть функция для отправки сетевых запросов и обработки ответа, я передаю структуру ответа в качестве параметра этой функции, чтобы функция возвращала мне готовую структуру ответа. Каждая такая структура имеет вид 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. Если для переменной decodableType указан тип decodable.Protocol, то больше невозможно добавить APIAnswerUserActivity.self, что спокойно принимается при вызове функции networkRequest.

Как быть в этой ситуации ? Надеюсь, мне удалось правильно и четко изложить суть моей проблемы. Спасибо!

1 Ответ

0 голосов
/ 13 января 2020

@ Владислав Артемьев, я все еще не уверен, что полностью понимаю проблему, потому что вы не поделились кодом, который принимает класс Decodable. Но, похоже, проблема в том, как пройти класс Decodable.

Я надеюсь, что следующее может помочь прояснить, как вы можете наложить правильное ограничение на шаблон c и как вы должны объявить переменную. Вы можете вставить его на игровую площадку и поэкспериментировать.

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()

...