У меня есть протокол с именем TableViewItem. Этот протокол заставляет соответствующие объекты реализовывать свойство type, в качестве типа которого используется протокол TableViewCellIdentifiable. TableViewCellIdentifiable используется для группировки трех вложенных перечислений, как показано ниже:

internal protocol TableViewCellIdentifiable: Equatable { }

internal enum TableViewCellType {
    internal enum PortfolioSelection: String, TableViewCellIdentifiable {
        case portfolio = "portfolioTableViewCell"
        case enterPortfolioDetails = "enterPortfolioDetailsTableViewCell"
        case addPortfolio = "actionTableViewCell"
    }

    internal enum EditPortfolio: String, TableViewCellIdentifiable {
        case editPortfolioName = "editPortfolioNameTableViewCell"
        case deletePortfolio = "deletePortfolioTableViewCell"
    }

    internal enum Portfolio: String, TableViewCellIdentifiable {   
        case portfolioAsset = "portfolioAssetTableViewCell"
        case addAsset = "actionTableViewCell"
    }
}

Вот пример того, как это используется:

internal final class EditPortfolioNameTableViewItem: TableViewItem {

    // MARK: - Internal Properties

    internal let type: TableViewCellIdentifiable = TableViewCellType.EditPortfolio.editPortfolioName
    internal let viewModel: TableViewCellModel

    // MARK: - Initialization

    internal init(viewModel: EditPortfolioNameTableViewCellModel) {
        self.viewModel = viewModel
    }
}

К сожалению, в строке, где я объявляю свойство type, я получаю следующую ошибку:

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

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

  1. Предоставить идентификаторы повторного использования для ячеек табличного представления (необработанные значения).
  2. Чтобы позволить сравнивать типы - т.е.

    self.tableViewItems.contains(where: { $0.type == item.type })
    

Любые предложения будут высоко оценены, даже если это означает альтернативный подход.

0
user3746428 6 Мар 2019 в 01:20

2 ответа

Лучший ответ

Как объясняется в ответе Хани, TableViewCellIdentifiable не предоставляет достаточно информации о типе для работы компилятора. Вы могли бы потенциально принять другой подход, который немного изменяет структуру (и потенциально может оказаться слишком сложным), но обеспечивает требуемую функциональность:

internal protocol ValueAssociated { }

internal extension ValueAssociated {

    fileprivate var association: (label: String, value: Any?)? {
        get {
            let mirror = Mirror(reflecting: self)
            if let association = mirror.children.first, let label = association.label {
                return (label, association.value)
            }
            return nil
        }
    }
}

internal protocol CellIdentifiable {

    var rawValue: String { get }
}

internal enum CellType: Equatable, ValueAssociated {

    case portfolio(PortfolioIdentifier)
    case portfolioSelection(PortfolioSelectionIdentifier)
    case editPortfolio(EditPortfolioIdentifier)

    internal var identifier: String? {
        return (self.association?.value as? CellIdentifiable)?.rawValue
    }

    internal enum PortfolioIdentifier: String, Equatable, CellIdentifiable {

        case portfolioAsset = "portfolioAssetTableViewCell"
        case addAsset = "actionTableViewCell"
    }

    internal enum PortfolioSelectionIdentifier: String, Equatable, CellIdentifiable {

        case portfolio = "portfolioTableViewCell"
        case enterPortfolioDetails = "enterPortfolioDetailsTableViewCell"
        case addPortfolio = "actionTableViewCell"
    }

    internal enum EditPortfolioIdentifier: String, Equatable, CellIdentifiable {

        case editPortfolioName = "editPortfolioNameTableViewCell"
        case deletePortfolio = "deletePortfolioTableViewCell"
    }
}

Это можно использовать следующим образом:

internal let cellType: CellType = .portfolio(.portfolioAsset)
print(cellType.identifier!) // Prints "portfolioAssetTableViewCell"

Надеюсь это поможет.

1
A. Walker 6 Мар 2019 в 21:42

В вашей голове, должен ли следующий код компилироваться?

var x : Equatable

Это не должно Зачем?

Потому что если бы у вас было:

var x : Equatable 
var y : Equatable

Тогда компилятор не может гарантировать, что x и y имеют один и тот же тип. x может быть "Джон", потому что "Джон" / Строки равны ... все, в то время как y может быть 10, потому что 10 / целые числа равноправны.

И компилятор будет подозревать, что несколько строк ниже вы можете сделать

if x == y { print ("equal" } 

Который он не может обработать. Так что это просто мешает вам когда-либо делать это в начале.


Следующая строка вашего кода вызовет ту же ошибку по вышеуказанной причине.

internal let type: TableViewCellIdentifiable = TableViewCellType.EditPortfolio.editPortfolioName
2
Honey 5 Мар 2019 в 22:32