Проблема при преобразовании универсального типа, который является массивом - PullRequest
0 голосов
/ 17 октября 2019

Я создал общий репозиторий, который возвращает запрашиваемые ресурсы. Это работает нормально, пока я не попробую выполнить групповой запрос, поместив несколько вызовов извлечения в DispatchGroup. Репозиторий является универсальным классом, который указывает тип возвращаемого значения.

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

Проблема возникает, когда универсальный тип имеет тип [Something] (то есть T == [Something])

    public typealias RepoCompletion<T: Codable> = (Result<T?, Error>) -> Void

    public func fetch<T: Entity>(_ resources: [FetchResource<T>], completion: @escaping RepoCompletion<T>) {
        DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
            let group = DispatchGroup()
            let valueSyncQueue = DispatchQueue(label: "Repo.ValueSync")
            var values = [String: T]() 

            for resource in resources {
                group.enter()
                self.localDataSource.fetch(resource) { result in
                    switch result {
                    case .success(let value):
                        if let value = value {
                            valueSyncQueue.sync {
                                values[resource.description] = value
                            }
                        }

                    case .failure(_): break
                    }

                    group.leave()
                }
            }

            group.wait(timeout: .now() + 10)
            let flattenedValues = resources.compactMap({ values[$0.description] }).flatMap({ $0 })
            completion(.success(flattenedValues))
        }
    }

Приведенный выше код не компилируется. Ошибка говорит Member 'success' in 'Result<_?, Error>' produces result of type 'Result<Success, Failure>', but context expects 'Result<_?, Error>'. Я думаю, что проблема возникает из-за того, что универсальный тип является [], а компилятору нужна помощь.

Проверьте типы при отладке:

(lldb) po type(of: resources.compactMap({ values[$0.description] }))
Swift.Array<Swift.Array<Schedule>>

(lldb) po type(of: resources.compactMap({ values[$0.description] }).flatMap({ $0 }))
Swift.Array<Schedule>

(lldb) po type(of: flattenedValues)
Swift.Array<Swift.Array<Schedule>>

Я не понимаю, почему выражение *У 1014 * есть разные типы для левой и правой сторон после оценки: S

Скажем, T == [Расписания]. Я ожидаю, что values будет [Строка: [Расписания]]. Затем, когда .compactMap, я ожидаю, что результат [[Расписания]] и, наконец, .flatMap вернет [Расписания] (т.е. T). Но я что-то упускаю или не понимаю.

Если я попытаюсь помочь компилятору и принудительно привести приравненные значения к значению! T, он аварийно завершает работу, возвращая

Невозможно привести значение типа 'Swift.Array' (0x1058073b0) к 'Schedule' (0x1044ac928). 2019-10-17 09: 48: 14.585362 + 0200 Chicisimo [86448: 2426001] Не удалось преобразовать значение типа «Swift.Array» (0x1058073b0) в «Расписание» (0x1044ac928).

Что я не совсем понимаю, потому что T относится к типу [Schedule], а не к типу Schedule.

Любая помощь будет принята с благодарностью! Спасибо!

1 Ответ

0 голосов
/ 17 октября 2019

Вы получили эту ошибку, потому что вы пытаетесь создать Result с массивом T, когда ваше завершение ожидает единственное значение T.

values: [String: T] создает списокT в вашем цикле for:

for resource in resources {
  // here value is a T
  values[resource.description] = value
}

Переменная сглаженных значений является результатом компактной карты вашего values словаря

let flattenedValues = resources.compactMap({ values[$0.description] }).flatMap({ $0 })

Это означает, что на данный момент,тип flattenedValues равен [T].

Завершение ((Result<T?, Error>) -> Void) предполагает Result с одним значением типа T, но вы пытаетесь разрешить его с помощьюмассив ([T]). Вот почему вы получаете эту ошибку.

Решение:

Из того, что я понимаю, вы хотите получить свои сущности из нескольких ресурсов (FetchResource), которыеозначает, что вы, вероятно, хотите всегда возвращать массив сущностей ([T]).

Если это так, вы можете просто изменить RepoCompletion на массив: [T]. Это будет выглядеть так:

// change RepoCompletion<T> to RepoCompletion<[T]>
public func fetch<T: Entity>(_ resources: [FetchResource<T>], completion: @escaping RepoCompletion<[T]>) {
  // ...
}
...