Generic Protocols with Associated Type
Learn how to create generic protocol and override associated type
Introduction
You’ve started learning Protocols and you’ve got addicted to it. One day, you heard some words like associated type and type eraser. It seems rough and wild. I feel you. So, I wrote this tutorial for you. Let’s get started.
Goals
There are two objectives. First, learn how to create a generic protocol with associatedtype
. Second, use where
clauses for type constraints similar to that of generics.
Prerequisites
I consider this tutorial as somewhat intermediate-advanced. So, I expect you to be familiar with the topics below.
Intro to Generics, Intro to Protocol Oriented Programming, Closure, Typealias.
If you are not comfortable with the topics above, you might find Learn Swift with Bob useful.
p.s I’m going to be around a while. I plan to cover server-side Swift, RxSwift, Test driven, and all the good stuff. 😃
The Number One Rules
The Swift Programming Language is considered as type-safe. It means, the type must be defined before compiled.
Review
Before we dive into generic protocols, you should be familiar with the following code below.
struct GenericStruct<T> {
var property: T?
}
I could either explicitly state the type of T
or let Swift infer based on the value.
let explictStruct = GenericStruct<Bool>()
// T is Bool let implicitStruct = GenericStruct(property: "Bob")
// T is String
Keep in mind of the principle that every type must be defined.
Normal Protocol
First, to appreciate generic protocols, let’s look into your/my past. Let’s create a protocol that requires you to add property
whose type is String
.
Design Protocol
protocol NormalProtocol {
var property: String { get set }
}
Design Class and Conform
class NormalClass: NormalProtocol {
var property: String = "Bob"
}
Sounds good. However, NormalProtocol
forces NormalClass
to work with String
. But, what if you want property
to be Int
or Bool
?
It’s time to introduce Protocol Associated Types. 😎 (I should have a thug-life meme)
Introducing Protocol Associated Types
In generic protocols, to create something like <T>
in generics, you need to add associatedtype
.
protocol GenericProtocol {
associatedtype myType
var anyProperty: myType { get set }
}
Associated type = type alias + generics
Now, anything that conforms to GenericProtocol
must implement anyProperty
. However, the type is not defined. Therefore, the class or struct that conforms to the protocol must define it either implicitly or explicitly.
First, let’s create a class SomeClass
that conforms to GenericProtocol
. We must define myType
. Well, there are two ways to define as stated above.
Define Associated Type Implicitly
You may define myType
based on the value associated with anyProperty
.
class SomeClass: GenericProtocol {
var anyProperty: myType = "Bob"
}
Now, myType
has been defined as String
based on “Bob”. However, you can let Swift do more guessing as shown below.
class SomeClass: GenericProtocol {
var anyProperty = "Bob" // myType is "String"
}
Is everything okay with you? If so far so good, you can show me some love through ❤️.
Define Associated Type Explicitly
Well, you may also define the associated type, myType
by calling typealias
. What? Let’s take a look.
class SomeClass: GenericProtocol {
typealias myType = String
var anyProperty: myType = "Bob"
}
If you want to define the associatedtype
a.k.a myType
, you may use typealias
. Of course, it is not necessary since you may define myType
implicitly as we’ve seen.
So far, you’ve defined myType
as String
. Let’s create a struct that conforms to GenericProtocol
but myType
is Int
instead.
struct SomeStruct: GenericProtocol {
var anyProperty = 1996
}
You’ve implicitly stated that myType
is Int
based on the value of 1996
.
If you hear Protocol Associated Types (PATs), it just means generic protocols.
Protocol Extension and Type Constraints
As you already know, Protocol extension is amazing because it provides default implementations without having to define required methods and properties. Let’s review.
Design Extension
extension GenericProtocol {
static func introduce() {
print("I'm Bob")
}
}
Anything that adopts GenericProtocol
now contains this magic.
SomeClass.introduce() // I'm Bob
SomeStruct.introduce() // I'm Bob
But all of a sudden, you only want myType
as String
to have the introduce()
method. How do you go about?