Protocol-oriented programming is the heart of Swift. Define capabilities with protocols, share default behavior with protocol extensions, and write reusable, type-safe code with generics.
A protocol declares a set of requirements — methods and properties — that a type can adopt, describing a capability rather than a concrete type. A struct, class, or enum can conform to many protocols. Protocols are how Swift achieves polymorphism without inheritance, and they underpin the standard library (Equatable, Comparable, Codable) and SwiftUI (View).
protocol Describable {
var name: String { get } // required property
func describe() -> String // required method
}
// any type can conform — here a struct and later others
struct Product: Describable {
let name: String
func describe() -> String { "Product: \(name)" }
}
func printInfo(_ item: Describable) { // works for ANY Describable
print(item.describe())
}
printInfo(Product(name: "Book")) // Product: BookA protocol extension provides default implementations of a protocol’s methods, so conforming types get behavior for free and only override what differs. This "protocol-oriented programming" lets you share functionality across unrelated types without a base class — the Swift alternative to inheritance for code reuse, and a defining feature of the language.
protocol Greetable {
var name: String { get }
}
// default behavior for ALL conformers, no base class needed
extension Greetable {
func greet() -> String { "Hello, \(name)!" }
}
struct Person: Greetable { let name: String }
struct Robot: Greetable {
let name: String
func greet() -> String { "BEEP \(name)" } // override the default
}
print(Person(name: "Ada").greet()) // Hello, Ada! (default)
print(Robot(name: "R2").greet()) // BEEP R2 (overridden)Generics let a function or type work over any type while staying type-safe. A generic function declares a placeholder type in angle brackets and can constrain it to conform to a protocol, so the body can use that protocol’s requirements. This is how you write reusable containers and algorithms once instead of duplicating them per type.
// works for any T, with the constraint that T is Comparable
func maxOf<T: Comparable>(_ a: T, _ b: T) -> T {
a > b ? a : b
}
print(maxOf(3, 7)) // 7
print(maxOf("apple", "pear")) // pear
// a generic type — a type-safe container for any Element
struct Stack<Element> {
private var items: [Element] = []
mutating func push(_ item: Element) { items.append(item) }
mutating func pop() -> Element? { items.popLast() }
}
var s = Stack<Int>()
s.push(1); s.push(2)
print(s.pop() ?? -1) // 2Codable — actually Encodable & Decodable — is a standout example of protocol power: conform a type to it (often automatically) and Swift generates JSON encoding and decoding for you. This is how iOS apps turn API responses into typed models with almost no boilerplate, and it shows why protocols are central to everyday Swift.
struct User: Codable { // conformance is automatic for simple types
let id: Int
let name: String
}
// decode JSON -> a typed model
let json = "{\"id\": 1, \"name\": \"Ada\"}".data(using: .utf8)!
let user = try JSONDecoder().decode(User.self, from: json)
print(user.name) // Ada
// encode a model -> JSON
let data = try JSONEncoder().encode(user)
// Codable powers nearly all iOS networking (see the Architecture course).