Swift Protocols
A protocol is a contract. It lists the properties and methods that any conforming type must provide — but it does not implement them. Think of a protocol like a job description: it says what skills are required, but not how the employee performs the job. Any struct, class, or enum can sign that contract by implementing what is required.
Defining a Protocol
protocol Greetable {
var name: String { get }
func greet() -> String
}The { get } after a property means the conforming type must provide at least a getter. Writing { get set } means it must also be writable.
Conforming to a Protocol
struct Person: Greetable {
var name: String
func greet() -> String {
return "Hello, I'm \(name)!"
}
}
class Robot: Greetable {
var name: String
init(name: String) {
self.name = name
}
func greet() -> String {
return "BEEP. I am \(name)."
}
}
let p = Person(name: "Alice")
let r = Robot(name: "R2D2")
print(p.greet()) // Output: Hello, I'm Alice!
print(r.greet()) // Output: BEEP. I am R2D2.Diagram: Protocol as a Contract
protocol Greetable
+---------------------+
| var name: String |
| func greet() |
+---------------------+
| |
struct Person class Robot
(must provide (must provide
name + greet) name + greet)
Multiple Protocol Conformance
A type can conform to more than one protocol simultaneously.
protocol Printable {
func printInfo()
}
protocol Saveable {
func save()
}
struct Document: Printable, Saveable {
var title: String
func printInfo() {
print("Document: \(title)")
}
func save() {
print("Saving \(title) to disk...")
}
}
let doc = Document(title: "Swift Guide")
doc.printInfo() // Output: Document: Swift Guide
doc.save() // Output: Saving Swift Guide to disk...Protocol as a Type
You can use a protocol name as a type. Any conforming type can fill that variable.
let greeters: [Greetable] = [Person(name: "Alice"), Robot(name: "R2D2")]
for greeter in greeters {
print(greeter.greet())
}
// Output:
// Hello, I'm Alice!
// BEEP. I am R2D2.Protocol with Default Implementation
Protocol extensions add default behavior that all conforming types receive automatically. A type can still override the default if it needs different behavior.
protocol Describable {
var description: String { get }
func describe()
}
extension Describable {
func describe() {
print(description) // Default implementation
}
}
struct Product: Describable {
var description: String
}
let phone = Product(description: "Smartphone — 128GB storage")
phone.describe()
// Output: Smartphone — 128GB storageProtocol Inheritance
Protocols can inherit from other protocols, building a hierarchy of requirements.
protocol Named {
var name: String { get }
}
protocol Aged: Named {
var age: Int { get }
}
struct Employee: Aged {
var name: String
var age: Int
}
let emp = Employee(name: "Bob", age: 35)
print("\(emp.name), age \(emp.age)")
// Output: Bob, age 35Equatable and Comparable Protocols
Swift's standard library is built on protocols. Two widely used ones are Equatable (enables ==) and Comparable (enables <, >).
struct Point: Equatable {
var x: Int
var y: Int
}
let p1 = Point(x: 1, y: 2)
let p2 = Point(x: 1, y: 2)
let p3 = Point(x: 5, y: 8)
print(p1 == p2) // Output: true
print(p1 == p3) // Output: falseCodable Protocol
Codable is a combination of Encodable and Decodable. Conforming types can be converted to and from JSON automatically — a common need in apps that talk to web APIs.
struct User: Codable {
var name: String
var age: Int
}
let user = User(name: "Alice", age: 30)
let encoded = try! JSONEncoder().encode(user)
let json = String(data: encoded, encoding: .utf8)!
print(json)
// Output: {"name":"Alice","age":30}
let decoded = try! JSONDecoder().decode(User.self, from: encoded)
print(decoded.name) // Output: AliceSummary
Protocols define requirements without implementations. Any struct, class, or enum can conform by supplying what the protocol asks for. Protocol extensions add shared default implementations. Use protocols to write flexible, reusable code that works with any conforming type — not just a specific class or struct.
