This is generally where I do test and try codes for better understanding of what it means and how to hearness the Swift compiler potentials.
One of the most twisted topics in Swift is definitely associatedtype
. I am currently trying to literally break down this concept to a layman understanding and by so doing I could also understand it myself.
associatedtype
is a protocol generic placeholder for an unknown Concrete Type
that requires concretization on adoption at the consumer side.
associatedtype
was introduced to solve the problem of rich and multi type abstraction which are not available in object-oriented subtyping.- Designed to address the known naive
generic protocol
especially where complexity scales badly with more generic type introduction.
associatedtype
come into play when subtyping alone cannot capture the rich type relationship between types.- They help specify the precise and exact type of an object with a protocol subtyping.
- They provide the relationship that you cannot fit into an object related type hierarchy.
- Assuming you have a protocol as follows:
/// Rows `Interface`
protocol Row {
/// PAT Placeholder for unknown Concrete Type `Model`
associatedtype Model
/// Recieves a parameter of Concrete Type `Model`
func configure(with model: Model)
}
/// Concrete Type `Product`
struct Product { }
/// Concrete Type `Item`
struct Item { }
- You can apply a
constrained type-erasure
as shown:
/// Wrapper `AnyRow`
struct AnyRow<I>: Row {
private let configureClosure: (I) -> Void
/// Initialiser guaratees that `Model`
/// should be a `Type` of `I`
init<T: Row>(_ row: T) where T.Model == I {
/// Matches the row `configure` func
/// to the private the `configureClosure`
configureClosure = row.configure
}
/// Conforming to `Row` protocol
func configure(with model: I) {
configureClosure(model)
}
}
- You can apply a
unconstrained type-erasure
as shown:`
/// Generic Wrapper `AnyCellRow` to match Heterogeneous Types + Dynamic Dispatch
struct AnyCellRow: Row {
private let configureClosure: (Any) -> Void
init<T: Row>(_ row: T) {
configureClosure = { object in
/// Asserting that `object` received is `type` of `T.Model`
guard let model = object as? T.Model else { return }
/// call the `T.configure` function on success
row.configure(with: model)
}
}
/// Conforming to `Row` protocol
func configure(with model: Any) {
configureClosure(model)
}
}
- You can apply the
shadow pattern
as follows:ù
//MARK: - `Shadowed` Protocol Based Type Erasure
/// `shadow` protocol
protocol TableRow {
/// - Recieves a parameter of Concrete Type `Any`
func configure(with model: Any)
}
/// `Row` To be shadowed.
protocol Row: TableRow {
associatedtype Model
/// - Recieves a parameter of Concrete Type `Model`
func configure(with model: Model)
}
/// `extension` to conform to `TableRow`
extension Row {
/// TableRow - conformation
func configure(with model: Any) {
/// Just throw a fatalError
/// because we don't need it.
fatalError()
}
}