Anoop Rawat
Anoop Rawat

Reputation: 357

How to create array of generic associated type protocol in swift?

Better to ask with example. So its table sections and rows

First created a protocol which needs to be implemented by all rows which has an associated type, must be given by caller to tell while defining row.

protocol RowElementProtocol {
    associatedtype ElementType
    var cellIdentifier: String {get set}
    var cellType: ElementType {get set}
}

So creating a generic row struct here

struct GenericRow <T>: RowElementProtocol {

    var cellIdentifier = "cell"
    var cellType: T

    /// Some Other Properties

    init(cellIdentifier: String = "cell", cellType: T) {

        self.cellIdentifier = cellIdentifier
        self.cellType = cellType
    }
}

Creating a different row struct here

struct DifferentRow<T>: RowElementProtocol {

    var cellIdentifier = "cell"
    var cellType: T

    /// Some Different Properties other then generic
}

Now creating a section which can have any kind of rows

struct Section<T: RowElementProtocol> {

    var rows = 1
    var rowElements: [T]
}

Everything is fine here, Problem arises when I want to initialise an array of sections

let sections = [Section<T: RowElementProtocol>]()

Compiler is not allowing me to initialise. Its showing ">' is not a postfix unary operator".

Upvotes: 4

Views: 1500

Answers (2)

Sweeper
Sweeper

Reputation: 270945

Let's have a look at the consequences of the compiler allowing you to create such an array.

You could do something like this:

var sections = [Section<T: RowElementProtocol>]()
var sec1 = Section<GenericRow<String>>()
var sec2 = Section<DifferentRow<Int>>()
sections.append(sec1)
sections.append(sec2)
let foo = sections[0] // how can the compiler know what type this is?

See the problem now? "Well, foo can just be of type Section<T: RowElementProtocol>" you might say. Okay, let's suppose that's true:

foo.rowElements[0].cellType // what the heck is the type of this?

The compiler has no idea. It only knows that foo.rowElements[0] is "some type that conforms to RowElementProtocol but cellType could be anything.

"Okay, why not just use Any for that type?" you might ask. The compiler could do this, but that makes your generics pointless, doesn't it?

If you want to make your generics pointless, you can do what's called "type erasure", by creating the types AnyRowElement and AnySection:

struct AnyRowElement : RowElementProtocol {
    var cellIdentifier: String

    var cellType: Any

    typealias ElementType = Any

    init<T: RowElementProtocol>(_ x: T) {
        self.cellIdentifier = x.cellIdentifier
        self.cellType = x.cellType
    }
}

struct AnySection {
    var rows: Int
    var rowElements: [AnyRowElement]

    init<T: RowElementProtocol>(_ x: Section<T>) {
        self.rows = x.rows
        self.rowElements = x.rowElements.map(AnyRowElement.init)
    }
}

let sections: [AnySection] = [AnySection(sec1), AnySection(sec2)]

Upvotes: 4

Joakim Danielson
Joakim Danielson

Reputation: 51882

You need to tell the compiler what the implemented type of T is

let sections = [Section<GenericRow<String>>]()

or like this

typealias SectionType = Section<GenericRow<String>>
let sections = [SectionType]()

Upvotes: 0

Related Questions