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

Другие вопросы по тегам