< Why can variable properties in constant classes be changed? | When should we use protocol inheritance? > |
Updated for Xcode 14.2
Protocols let us define how structs, classes, and enums ought to work: what methods they should have, and what properties they should have. Swift will enforce these rules for us, so that when we say a type conforms to a protocol Swift will make sure it has all the methods and properties required by that protocol.
In practice, what protocols allow us to do is treat our data in more general terms. So, rather than saying “this buy()
method must accept a Book
object,” we can say “this method can accept anything that conforms to the Purchaseable
protocol. That might be a book, but it might also be a movie, a car, some coffee, and so on – it makes our simple method more flexible, while ensuring that Swift enforces the rules for us.
In code terms, our simple buy()
method that works only with books would look like this:
struct Book {
var name: String
}
func buy(_ book: Book) {
print("I'm buying \(book.name)")
}
To create a more flexible, protocol-based approach we would first create a protocol that declares the basic functionality we need. This might be many methods and properties, but here we’re just going to say that we need a name string:
protocol Purchaseable {
var name: String { get set }
}
Now we can go ahead and define as many structs as we need, with each one conforming to that protocol by having a name string:
struct Book: Purchaseable {
var name: String
var author: String
}
struct Movie: Purchaseable {
var name: String
var actors: [String]
}
struct Car: Purchaseable {
var name: String
var manufacturer: String
}
struct Coffee: Purchaseable {
var name: String
var strength: Int
}
You’ll notice each one of those types has a different property that wasn’t declared in the protocol, and that’s okay – protocols determine the minimum required functionality, but we can always add more.
Finally, we can rewrite the buy()
function so that it accepts any kind of Purchaseable
item:
func buy(_ item: Purchaseable) {
print("I'm buying \(item.name)")
}
Inside that method we can use the name
property of our item safely, because Swift will guarantee that each Purchaseable
item has a name
property. It doesn’t guarantee that any of the other properties we defined will exist, only the ones that are specifically declared in the protocol.
So, protocols let us create blueprints of how our types share functionality, then use those blueprints in our functions to let them work on a wider variety of data.
SPONSORED Thorough mobile testing hasn’t been efficient testing. With Waldo Sessions, it can be! Test early, test often, test directly in your browser and share the replay with your team.
Sponsor Hacking with Swift and reach the world's largest Swift community!
Link copied to your pasteboard.