4

I'm new to Swift and have been messing around with some code. I've defined a Layout class similar to the WWDC talk on protocol oriented programming. It looks like this:

protocol Layout {

    associatedtype Content: AnyObject

    var contents: [Content] { get }

    func layout()
}

Basically, I'd like a protocol layout which knows how to lay out some type of content (NSViews, UIViews, etc).

I'd like to extend this protocol like so:

protocol StackableLayout : Layout {
    var children: [Layout] { get set }
}

Where a StackableLayout is a layout that can handle an array of things of type layout. However this doesn't compile because of the associated type on Layout.

This compiles:

protocol StackableLayout : Layout {
    associatedtype Child: Layout
    var children: [Child] { get set }
}

But enforces that everything I'm stacking be of the same type:

struct YStackLayout<Child: Layout> : StackableLayout {

    var children: [Child]

So I can't have a YStackLayout of arbitrary Layout items. How can I accomplish this?

I came across these posts which look similar but I can't figure out how to apply them: How to define a protocol with an array of a protocol with an associated type How do I add different types conforming to a protocol with an associated type to a collection?

EDIT: It seems that type-erasure is what I need here. A linked example:

struct AnyAnimal<Food>: Animal {
    private let _feed: (Food) -> Void
    init<Base: Animal where Food == Base.Food>(_ base: Base) {
        _feed = base.feed
    }
    func feed(food: Food) { _feed(food) }
}
Community
  • 1
  • 1
JPC
  • 8,096
  • 22
  • 77
  • 110
  • I don't think this will work, otherwise `yStackLayout.children[0].contents` and `yStackLayout.children[1].contents` will have different types determined at runtime – kennytm Aug 25 '16 at 19:27
  • 2
    You'd need [type erasures](http://robnapier.net/erasure) for this. – Cristik Aug 25 '16 at 19:28
  • This looks like what I need. Question though...why is this the way they do it? struct AnyAnimal: Animal { private let _feed: (Food) -> Void init(_ base: Base) { _feed = base.feed } func feed(food: Food) { _feed(food) } } Why not just grab base and call the function on it in its feed method? – JPC Aug 25 '16 at 19:42

0 Answers0