I have a protocol, which has a property that is another protocol. I have a few structs that implement that protocol and I'm happy with them. However, I would like to have an array of the protocol, but specify that the inner protocol is always a specific type.
It is probably easier if I show some example code:
protocol MyCategory {
var displayTitle: String {get}
}
enum Category1: MyCategory {
case person
case place
case thing
var displayTitle: String {
switch self {
...
}
}
}
enum Category2: MyCategory {
case animal
case vegetable
case mineral
var displayTitle: String {
switch self {
...
}
}
}
protocol FieldModel {
var category: MyCategory { get }
var name: String { get }
}
struct IntFieldModel: FieldModel {
let category: MyCategory
let name: String
let value: Int
}
struct StringFieldModel: FieldModel {
let category: MyCategory
let name: String
let value: String
}
struct Category1ConsolidatedResults {
let fields: [FieldModel]
}
I would like to be able to tell Category1ConsolidatedResults
that every item in fields
will be of type FieldModel
but also that every category
in that every FieldModel
will be of type Category1
.
I tried to do something with generics and associatedtype
but I couldn't figure out how to pass the type into the FieldModel
protocol in Category1ConsolidatedResults
. Instead I got the error "Use of protocol 'FieldModel' as a type must be written 'any FieldModel'".
Is there a way to pass a type to a generic protocol?
You should indeed add an associated type to FieldModel
, so that category information is encoded into the type system. In addition to that, you can make this the primary associated type of FieldModel
, so that in Category1ConsolidatedResults
, you can write [any FieldModel<Category1>]
.
protocol FieldModel<Category> {
associatedtype Category: MyCategory
var category: Category { get }
var name: String { get }
}
struct IntFieldModel<Category: MyCategory>: FieldModel {
let category: Category
let name: String
let value: Int
}
struct StringFieldModel<Category: MyCategory>: FieldModel {
let category: Category
let name: String
let value: String
}
struct Category1ConsolidatedResults {
let fields: [any FieldModel<Category1>]
}
struct Category1ConsolidatedResults {
let fields: [any FieldModel<Category1>]
}
Also consider writing a ConsolidatedResults
type, generic over the category type:
struct ConsolidatedResults<Category: MyCategory> {
let fields: [any FieldModel<Category>]
}