#swift
#быстрая
Вопрос:
У меня есть эта общая функция, которая запускает контроллер представления на основе переданных общих данных
func processListData<T: FormDataSource>(title: String, data: [T]) {
let vc = ListViewController(data: data)
vc.coordinator = self
push(vc, title: title.localized())
}
На сайте вызывающего абонента, если бы я использовал этот код
func handleTableSelection(cellData: (String, [FormDataSource])){
let (title,dataSource) = cellData
coordinator?.processListData(title: title, data: dataSource)
}
отображается следующая ошибка
Значение типа протокола ‘FormDataSource’ не может соответствовать ‘FormDataSource’; только типы struct / enum / class могут соответствовать протоколам
итак, мне пришлось привести переменную FormDataSource, чтобы иметь возможность вызывать processListData следующим образом
func handleTableSelection(cellData: (String, [FormDataSource])){
let (title,dataSource) = cellData
if let assumptions = dataSource as? [Assumption] {
coordinator?.processListData(title: title, data: assumptions)
} else if let stakeholders = dataSource as? [StakeHolder] {
coordinator?.processListData(title: title, data: stakeholders)
}
//.... tons of else if
Есть ли какой-нибудь лучший способ сохранить его универсальным и передать источник данных без приведения всех возможных моделей?
Комментарии:
1. Вы пытались ограничить
FormDataSource
протокол только классом? Что-то вродеprotocol FormDataSource: class { }
.2. Можете ли вы сделать
handleTableSelection
универсальный?func handleTableSelection<U: FormDataSource>(cellData: (String, [U])) {
3. @ben да, я пробовал это, но он показывает ту же ошибку
4. @vadian выполнение этого переместит проблему в таблицу вызывающего (TableView:didSelectRowAt)
Ответ №1:
Если processListData
имеется общая переменная T
, то handleTableSelection
она также должна иметь ту же форму, которую вы можете удалить <T: FormDataSource>
в A, например:
func processListData(title: String, data: [FormDataSource]) {
let vc = ListViewController(data: data)
vc.coordinator = self
push(vc, title: title.localized())
}
Комментарии:
1. @DotFreelancer FormDataSource — это протокол, а не тип. Если вы создадите массив элементов, соответствующих протоколу, вы не сможете получить доступ к свойствам соответствующего ему типа, который не требуется протоколом.
2. @LeoDabus Спасибо, что упомянули об этом. В моем случае это нормально; я использую только свойство, объявленное в протоколе FormDataSource.
Ответ №2:
Вводя общие термины, вы должны предоставить компилятору возможность статически выводить типы из использования.
Насколько я мог воспроизвести ваш код, вот возможное направление для решения этого случая без приведения во время выполнения (это также потребует изменений в другом не предоставленном коде, но, похоже, идея должна быть ясна)
enum DataSourceStorage {
case assumption([Assumption])
case stakeholder([StakeHolder])
func process(with coordinator: Coordinator?, title: String) {
switch self {
case .assumption(let assumptions):
coordinator?.processListData(title: title, data: assumptions)
case .stakeholder(let stakeholders):
coordinator?.processListData(title: title, data: stakeholders)
}
}
}
и теперь это можно обрабатывать напрямую
func handleTableSelection(cellData: (String, DataSourceStorage)){
let (title,dataSource) = cellData
dataSource.process(with: coordinator, title: title)
}
поскольку речь идет о статической типизации, поэтому в соответствующих местах рабочие процессы должны быть явно разделены, чтобы вызываться как
handleTableSelection(cellData: (title, .assumption(data))