Протокол Swift со связанным типом: как использовать в абстрактном методе? - PullRequest
0 голосов
/ 21 октября 2018

У меня есть два протокола, один для ViewModel и один для ConfigurableView, который принимает тип ViewModel в качестве связанного типа .:

public protocol ViewModel {}

public protocol ConfigurableView {

  associatedtype ViewModelType: ViewModel

  func configure(with viewModel: ViewModelType)

}

В моем методе, который настраивает абстрактное представление с абстрактной моделью:

let viewModel = getMyViewModel() // returns ViewModel

if let configurableView = cell as? ConfigurableView {
    configurableView.configure(with: viewModel)
}

Я получаю "Протокол 'ConfigurableView' может использоваться только в качестве общего ограничения, поскольку он имеет требования к Self или связанные с типами".

Как сообщить компилятору, что я хочунастроить представление с любым связанным типом, который имеет этот экземпляр, если это экземпляр ConfigurableView?

Ответы [ 2 ]

0 голосов
/ 22 октября 2018

На самом деле я нашел то, что я считаю достойным решением, которое не требовало бы слишком больших усилий для моей архитектуры.Спасибо @lib за то, что поставили меня на правильный путь.Хитрость заключалась в том, чтобы иметь вышеописанный протокол, который не требовал ассоциированного типа с расширением, которое преобразует общий видовой модели в связанный тип конкретного.Я полагаю, это стирание типа?Но это не похоже ни на один из примеров, которые я прочитал.

public protocol ViewModel {}

/*
 This parent protocol exists so callers can call configure on
 a ConfigurableView they don't know the specific type of.
*/
public protocol AnyConfigurableView {

  func configure(with anyViewModel: ViewModel)

}

public protocol ConfigurableView: AnyConfigurableView {

  associatedtype ViewModelType: ViewModel

  func configure(with viewModel: ViewModelType)

}

/*
 This extension does the trick of converting from the generic
 form of ConfigurableView to the specific form.
 */
public extension ConfigurableView {

  func configure(with anyViewModel: ViewModel) {

    guard let viewModel = anyViewModel as? ViewModelType else {
      return
    }

    configure(with: viewModel)

  }

}

Использование:

let viewModel = getViewModel()
(someView as? AnyConfigurableView)?.configure(with: viewModel)
0 голосов
/ 21 октября 2018

Вы не можете использовать общие протоколы иначе, чем ограничения типа.Без определения универсального типа компилятор не может сравнивать соответствие типов.Если я вас правильно понял, то вам нужно определить универсальный CellConfigurator класс.Одно из возможных решений ниже:

1.Абстракции ячеек и конфигуратора

protocol ConfigurableCell {
    associatedtype DataType
    func configure(viewModel: DataType?)
}

protocol CollectionViewCellConfigurator {
    static var reuseId: String { get }
    func configure(cell: UICollectionViewCell)
    var item: UniqueIdentifiable? { get }
}

final class CellConfigurator<CellType: ConfigurableCell, DataType>: CollectionViewCellConfigurator where CellType.DataType == DataType, CellType: UICollectionViewCell {

    /// Cell Reuse identifier
    static var reuseId: String { return CellType.reuseId }

    /// Configures cell and populates it with `viewModel`
    ///
    /// - Parameter cell: Cell to configure
    func configure(cell: UICollectionViewCell) {
        (cell as! CellType).configure(viewModel: item as? DataType)
    }

    /// Initializer
    ///
    /// - Parameter item: Data item (usually ViewModel of the cell)
    init(item: DataType?) {
        self.item = item
    }
}

2.Использование

Ваш источник данных теперь будет работать с CellConfigurators в виде CellConfigurator<CellType /*UI(CollectionView/TableView)Cell subclass*/, CellData /*Data you need to populate to the cell*/>(item: cellData)

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let configItem = yourDataSource.rows[indexPath.row]
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: type(of: configItem).reuseId, for: indexPath)
        configItem.configure(cell: cell)
        return cell
    }

Надеюсь, это поможет.Удачи

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