Associated types are specified by the implementer of a protocol. Generics are specified by the caller when instantiating.
An example for this is the following:
The Sequence protocol has an associated type Element.
Array implements Sequence and forwards this associated type as a generic type parameter to the caller:
struct Array<Element>: Sequence {
typealias Element = Element // associatedtype requirement Element is fulfilled with generic type Element
}
Then, the caller can decide to use an Array<Int>, an Array<String>, etc
However, the type String also conforms to Sequence but only allows Character for Element:
struct String: Sequence {
typealias Element = Character
}
The implementer decides what the associated type should be.
(Types have been simplified for this example)
As you can see, associatedtype and generics are not the same thing. They are different things and should be treated as such.
Swift does not have generalized existentials yet, which would bridge the gap. You may find some discussions about them here on the forum.
With those, you could write something like
var dataManager: some CollectionViewDataManager where DataType == Int
(not currently valid Swift syntax)
So as of right now, you have to consider alternatives:
-
Make CollectionViewDataManager a class and subclass that. Concrete types can have generic parameters.
-
Use type erasure. The Swift standard library uses this approach with types such as AnyHashable or AnySequence.
Basically, you will have to write a wrapper type AnyCollectionViewDataManager<DataType>: CollectionViewDataManager.
struct AnyCollectionViewDataManager<DataType>: CollectionViewDataManager {
init<Wrapped: CollectionViewDataManager>(_ wrapped: Wrapped) where Wrapped.DataType == DataType {}
}
You can then create closures in the initializer, that perform method calls to the wrapped manager.
Lastly, you can make the enclosing type generic:
struct DataManagerHolder<DataSource: CollectionViewDataManager> where DataSource.DataType == Int {
var dataManager: DataSource
}