Protocol Extensions
Part of the Object Oriented Programming section of Coddy's Swift journey. Lesson 29 of 57.
A protocol extension gives a protocol's methods a default implementation. Every conforming type gets it for free, and a type can still provide its own version:
protocol Greeter {
var name: String { get }
func greet() -> String
}
extension Greeter {
func greet() -> String { "Hello, \(name)!" }
}
struct English: Greeter { let name: String }
struct Pirate: Greeter {
let name: String
func greet() -> String { "Ahoy, \(name)!" }
}
print(English(name: "Ada").greet())
print(Pirate(name: "Ada").greet())Output:
Hello, Ada!
Ahoy, Ada!An extension can also add new methods that are built only from the requirements. A conforming type writes the small required part and receives the rest:
protocol Scored {
var points: [Int] { get }
}
extension Scored {
var total: Int { points.reduce(0, +) }
var best: Int { points.max() ?? 0 }
func summary() -> String { "total \(total), best \(best)" }
}
struct Player: Scored {
let points: [Int]
}
print(Player(points: [3, 9, 4]).summary())Output:
total 16, best 9This shares behaviour between structs, classes and enums without inheritance. The standard library works the same way: every Collection gets isEmpty, first and map from protocol extensions.
extension Collection {
// isEmpty, first, map, filter, ... are all defined once here
// and every array, set, dictionary and string gets them
}A constrained extension adds methods only for some conforming types, written with where:
protocol Container {
associatedtype Item
var items: [Item] { get }
}
extension Container where Item == Int {
var sum: Int { items.reduce(0, +) }
}
struct Box: Container {
var items: [Int]
}
print(Box(items: [2, 5]).sum)Output:
7Challenge
EasyDeclare a protocol Priced that requires var price: Int { get } (in cents) and var formatted: String { get }. In a protocol extension, give formatted a default that returns $4.50, and add a method isAffordable(budget:) that returns whether the price fits the budget. Make the structs Coffee (price 450) and Sandwich (a stored price) conform. Coffee must replace the default formatted with coffee $4.50.
The supplied code reads a budget and then lines coffee or sandwich,650. It prints formatted and yes or no for each item.
Your code goes in Priced.swift, Coffee.swift and Sandwich.swift. main.swift holds the supplied input/output code and cannot be edited.
Try it yourself
// Supplied input/output code: keep it as it is
var input: [String] = []
while let line = readLine() { input.append(line) }
let budget = Int(input[0])!
for line in input.dropFirst() {
let item: any Priced = line == "coffee" ? Coffee() : Sandwich(price: Int(line.split(separator: ",")[1])!)
print("\(item.formatted) \(item.isAffordable(budget: budget) ? "yes" : "no")")
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Classes and Objects
Working With FilesDefining ClassesMethods and selfInitializersClasses Are ReferencesRecap - Library Card4Encapsulation
Access ControlRead-Only From OutsideGuarding StateFailable InitializersRecap - Bank Account7Polymorphism and Generics
PolymorphismType CastingGeneric FunctionsGeneric TypesRecap - Shape Calculator5Inheritance
SubclassesOverriding MethodsInitializers and superfinal and HierarchiesRecap - Employee Hierarchy11Project: Library Management
Books and MembersBorrowing Books3Properties in Depth
Computed PropertiesProperty ObserversStatic MembersEnums with MethodsRecap - Temperature Log6Protocols
Defining ProtocolsConforming TypesProtocol ExtensionsProtocols as TypesRecap - Payment SystemPractice on your own: Swift playground