Дженерики Swift - невозможно преобразовать значение в тип - PullRequest
1 голос
/ 20 июня 2020

У меня проблема с дженериками в swift.

Я создаю простой http-клиент и хочу расширить его, добавив кеширование. Я создал решение для кеширования, используя этот отличный пост здесь .

Я хочу иметь возможность передавать экземпляр кеша моему клиенту, когда он инициализирован, поэтому он должен иметь возможность хранить разные данные типы, соответствующие Codable.

Вот что у меня есть (реализация кеширования описана в статье):

data:

struct CountryResult: Codable {
    let meta: Meta //confroms to Codable
    let results: [Country] //conforms to Codable
}

cache :

final class Cache<Key: Hashable, Value> {
// cache implementation
}

// MARK: - Codable

extension Cache.Entry: Codable where Key: Codable, Value: Codable {}
extension Cache: Codable where Key: Codable, Value: Codable {
    convenience init(from decoder: Decoder) throws {
        self.init()

        let container = try decoder.singleValueContainer()
        let entries = try container.decode([Entry].self)
        entries.forEach(insert)
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try container.encode(keyTracker.keys.compactMap(entry))
    }
}

http-клиент:

struct HttpService {

    let service: BaseUrl
    let cache: Cache<URL, Codable>

    init(service: String = "https://some.service/api", cache: Cache<URL, Codable> = Cache()) {
        self.service = service
        self.cache = cache
    }

    // performs http get
    func get<T: Codable>(endpoint: String, completion: @escaping (T?, Error?) -> Void) {
        guard let url = buildUrl(endpoint) else {
            print("Invalid url")
            return
        }

        if let cachedData = cache[url] as? T {
            print("Cache hit for: \(url)")
            completion(cachedData, nil)
            return
        } else {
            print("Cache miss for: \(url)")
        }

        //data not in cache, go to network
        //....
    }

Теперь я хочу иметь возможность создавать экземпляр этого http-клиента в разных контроллерах представления, которые будут запрашивать разные типы данных. Я также хочу сохранить каждый кеш V C отдельно, поэтому при инициализации V C я пытаюсь определить, был ли кеш сохранен на диске, и загрузить его так:

let urls = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)
let cacheUrl = urls[0].appendingPathComponent("/v1/test.cache")
if let data = try? Data(contentsOf: cacheUrl) {
    let cache  = try? JSONDecoder().decode(Cache<URL, CountryResult>.self, from: data) ?? Cache<URL, CountryResult>()
    let httpClient = HttpService(service: "https://some.service/api", cache: cache) //ERROR
}

Но я получаю следующая ошибка, которую я не совсем понимаю: Cannot convert value of type 'Cache<URL, CountryResult>?' to expected argument type 'Cache<URL, Codable>' (aka 'Cache<URL, Decodable & Encodable>')

Почему его нельзя преобразовать, если CountryResult соответствует Codable aka Decodable & Encodable

1 Ответ

1 голос
/ 20 июня 2020

Если вы измените приведенный ниже код

let cache: Cache<URL, Codable>

как

let cache: Cache<URL,T: Codable>

в структуре HttpService, надеюсь, он будет работать.

Согласно Swift do c, В Generics разрешено иметь параметр типа, соответствующий классу, протоколу или составу протокола, но не только протоколу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...