Reputation: 11175
I have a protocol named TableViewItem
. This protocol enforces that conforming objects implement a type
property, which has the protocol TableViewCellIdentifiable
as its type. TableViewCellIdentifiable
is used to group three nested enums together, as shown below:
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"
}
}
Here is an example of how this is being used:
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
}
}
Unfortunately, on the line that I am declaring the type
property, I receive the following error:
Protocol 'TableViewCellIdentifiable' can only be used as a generic constraint because it has Self or associated type requirements
I have read through other questions/answers from others who have encountered this error but I can't quite understand why this particular implementation is problematic, and what the solution would be. I know that Equatable
is the source of the problem, however this is crucial to the functionality, as the enums serve two purposes:
To allow types to be compared - i.e:
self.tableViewItems.contains(where: { $0.type == item.type })
Any suggestions would be much appreciated, even if it means taking an alternative approach.
Upvotes: 0
Views: 214
Reputation: 456
As explained by Honey's answer, TableViewCellIdentifiable
doesn't provide enough type information for the compiler to work with. You could potentially adopt a different approach which changes the structure a bit (and is potentially overkill), but provides the functionality you're looking for:
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"
}
}
This can be used as follows:
internal let cellType: CellType = .portfolio(.portfolioAsset)
print(cellType.identifier!) // Prints "portfolioAssetTableViewCell"
Hope this helps.
Upvotes: 1
Reputation: 36287
In your head, should the following code compile?
var x : Equatable
It shouldn't. Why?
Because if you had:
var x : Equatable
var y : Equatable
Then the compiler can't ensure that x & y are of the same type. x
can be "John", because "John"/Strings are Equatable...all while y
can be 10
, because 10/integers are equatable.
and the compiler would suspect that a few lines below you might want to do
if x == y { print ("equal" }
which it can't process. So it just stops you from ever doing it in the beginning.
The following line of your code will trigger the same error because of the reason above.
internal let type: TableViewCellIdentifiable = TableViewCellType.EditPortfolio.editPortfolioName
Upvotes: 2