Swift Associated Types and Protocol Inheritance

Protocol inheritance with associated type

Once a protocol has an associated type, that protocol can't be used as a type by itself for instance declarations-- only for generic constraints and declaring conformance.

So in this case, Swift is saying "yeah, but what is the concrete type for StartRouterProtocol's associated type?"

In this case, it's asking you to either:

  1. Use a concrete type directly, i.e. let router: MyStartViewClass with this conformance declaration, elsewhere: class MyStartViewClass: StartRouterProtocol { ... })
  2. OR, push the need for a concrete type up one layer, as a generic constraint i.e.
class MyRouterController<T: StartRouterProtocol> {
let router: T
}

This is probably not what you were hoping for, but unfortunately associated types add complexity to how you use protocols, especially if you're familiar with generics & interfaces from other languages. (i.e. Java/C# interfaces)

You can work around some aspects of associated types by using a concept called "type erasure" -- but that can cause other problems and complexity.

Here's some further reading that may help: https://medium.com/monstar-lab-bangladesh-engineering/swift-from-protocol-to-associatedtype-then-type-erasure-a4093f6a2d08

Swift protocol associated types and inheritance contraints

Create a DataSource protocol with an associated type for your layout type:

protocol ColumnDataSourceProtocol: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
associatedtype Layout: UICollectionViewFlowLayout
}

Make your DataSource base class conform to this protocol. You may need to add a typealias to specify the associatedtype if the compiler cannot infer it:

class ColumnDataSource<FlowLayoutType: UICollectionViewFlowLayout>: NSObject, ColumnDataSourceProtocol {

typealias Layout = FlowLayoutType

// the rest stays the same
}

Adapt ColumnFlowLayoutable to associate the data source type instead of the layout type. Constraining it to the ColumnDataSourceProtocol lets you access its associated Layout type:

protocol ColumnFlowLayoutable {

associatedtype DataSource: ColumnDataSourceProtocol

var columnDataSource: DataSource { get }
var customFlowLayout: DataSource.Layout { get }
}

Now you can subclass ColumnDataSource:

class DataSource: ColumnDataSource<CustomFlowLayout> { /* ... */ }

returning swift protocol associated type in multiple methods

The solution you came up with by playing around is exactly what you need

As mentioned elsewhere, the main issue with your first protocol is that you're enforcing createSomeView() createAnotherView() both return the same type. While ViewA() and ViewB() are both candidates for V, since they conform to View they are still different types, and therefore cannot BOTH be V in a given object.

By defining both V1 and V2, you allow for each function to return a different type, or the same type, it's all acceptable. By making both V1 and V2 require View conformance, you allow for the some View syntax



Related Topics



Leave a reply



Submit