Skip to main content

Swift Protocol Inheritance

In Swift, protocols aren't just standalone entities - they can build upon each other through a concept called protocol inheritance. This powerful feature allows you to create hierarchies of protocols, leading to more organized and modular code.

Introduction to Protocol Inheritance

Protocol inheritance is the ability for one protocol to inherit requirements from another protocol. When a protocol inherits from another protocol, any type that adopts the child protocol must satisfy all requirements of both the child and parent protocols.

This concept is similar to class inheritance but applies to interfaces rather than implementations.

Basic Protocol Inheritance Syntax

The syntax for protocol inheritance uses the same colon notation as class inheritance:

swift
protocol ParentProtocol {
// requirements
}

protocol ChildProtocol: ParentProtocol {
// additional requirements
}

Any type that conforms to ChildProtocol must fulfill both the requirements of ChildProtocol and ParentProtocol.

Simple Example of Protocol Inheritance

Let's start with a basic example to understand protocol inheritance:

swift
protocol Identifiable {
var id: String { get }
func identify()
}

protocol DetailedIdentifiable: Identifiable {
var name: String { get }
var description: String { get }
}

In this example, DetailedIdentifiable inherits from Identifiable. Now, let's implement these protocols:

swift
struct User: DetailedIdentifiable {
var id: String
var name: String
var description: String

func identify() {
print("User ID: \(id)")
}
}

let user = User(id: "U123", name: "John Doe", description: "Administrator")
user.identify() // Output: User ID: U123

Notice that User must implement all properties and methods from both protocols: id and identify() from Identifiable, and name and description from DetailedIdentifiable.

Multiple Protocol Inheritance

Unlike classes in Swift, protocols can inherit from multiple protocols. This is a powerful feature that enables composition:

swift
protocol Named {
var name: String { get }
}

protocol Aged {
var age: Int { get }
}

protocol Person: Named, Aged {
var occupation: String { get }
}

Implementing a type that conforms to a protocol with multiple inheritance:

swift
struct Employee: Person {
var name: String
var age: Int
var occupation: String
}

let employee = Employee(name: "Jane Smith", age: 28, occupation: "Software Developer")
print("\(employee.name), \(employee.age), \(employee.occupation)")
// Output: Jane Smith, 28, Software Developer

Protocol Inheritance with Default Implementations

Protocol inheritance becomes even more powerful when combined with protocol extensions, which provide default implementations:

swift
protocol Vehicle {
var speed: Double { get }
func accelerate()
}

extension Vehicle {
func accelerate() {
print("Accelerating to \(speed) mph")
}
}

protocol ElectricVehicle: Vehicle {
var batteryLevel: Int { get }
func charge()
}

extension ElectricVehicle {
func charge() {
print("Charging battery to 100%")
}
}

Implementing a type that uses default implementations:

swift
struct Tesla: ElectricVehicle {
var speed: Double
var batteryLevel: Int
}

let modelS = Tesla(speed: 120, batteryLevel: 80)
modelS.accelerate() // Output: Accelerating to 120.0 mph
modelS.charge() // Output: Charging battery to 100%

Real-World Example: Media Player Application

Let's explore a more realistic example of protocol inheritance in a media player application:

swift
protocol Playable {
var title: String { get }
var duration: Double { get }
func play()
func pause()
}

protocol AudioPlayable: Playable {
var volume: Float { get set }
func adjustEqualizer(bass: Float, mid: Float, treble: Float)
}

protocol VideoPlayable: Playable {
var resolution: String { get }
var aspectRatio: String { get }
func adjustBrightness(level: Float)
}

Now we can create implementations for audio and video content:

swift
class Song: AudioPlayable {
var title: String
var duration: Double
var volume: Float = 0.5
var artist: String

init(title: String, duration: Double, artist: String) {
self.title = title
self.duration = duration
self.artist = artist
}

func play() {
print("Playing song: \(title) by \(artist)")
}

func pause() {
print("Song paused")
}

func adjustEqualizer(bass: Float, mid: Float, treble: Float) {
print("Setting equalizer - Bass: \(bass), Mid: \(mid), Treble: \(treble)")
}
}

class Movie: VideoPlayable {
var title: String
var duration: Double
var resolution: String
var aspectRatio: String
var director: String

init(title: String, duration: Double, resolution: String, aspectRatio: String, director: String) {
self.title = title
self.duration = duration
self.resolution = resolution
self.aspectRatio = aspectRatio
self.director = director
}

func play() {
print("Playing movie: \(title) directed by \(director)")
}

func pause() {
print("Movie paused")
}

func adjustBrightness(level: Float) {
print("Setting brightness to \(level)")
}
}

Now let's create a media player that can handle both types:

swift
func playContent(_ content: Playable) {
content.play()

// We can also check and use the specific type
if let audioContent = content as? AudioPlayable {
audioContent.adjustEqualizer(bass: 0.8, mid: 0.5, treble: 0.6)
}

if let videoContent = content as? VideoPlayable {
videoContent.adjustBrightness(level: 0.7)
}
}

let song = Song(title: "Bohemian Rhapsody", duration: 354, artist: "Queen")
let movie = Movie(title: "The Matrix", duration: 7380, resolution: "4K",
aspectRatio: "16:9", director: "Wachowski Sisters")

playContent(song)
// Output:
// Playing song: Bohemian Rhapsody by Queen
// Setting equalizer - Bass: 0.8, Mid: 0.5, Treble: 0.6

playContent(movie)
// Output:
// Playing movie: The Matrix directed by Wachowski Sisters
// Setting brightness to 0.7

Protocol Composition

Sometimes you want a type to conform to multiple protocols without creating a new protocol. Swift offers protocol composition using the & operator:

swift
protocol Payable {
var salary: Double { get }
}

protocol Employable {
var position: String { get }
}

func printEmployeeInfo(employee: Payable & Employable) {
print("Position: \(employee.position), Salary: $\(employee.salary)")
}

struct Worker: Payable, Employable {
var salary: Double
var position: String
}

let developer = Worker(salary: 90000, position: "iOS Developer")
printEmployeeInfo(employee: developer)
// Output: Position: iOS Developer, Salary: $90000.0

Best Practices for Protocol Inheritance

When working with protocol inheritance, consider these guidelines:

  1. Keep protocols focused: Each protocol should represent a single, well-defined capability.

  2. Favor composition over deep hierarchies: Instead of creating deep inheritance chains, consider using protocol composition.

  3. Use protocol extensions wisely: Provide default implementations when they make sense for most conforming types.

  4. Consider the "is-a" relationship: A protocol should inherit from another only if it truly represents a more specialized version.

  5. Be mindful of requirements: Each requirement you add creates an obligation for conforming types.

Summary

Protocol inheritance in Swift allows you to:

  • Create hierarchical relationships between protocols
  • Inherit requirements from multiple protocols
  • Build modular interfaces that types can adopt
  • Combine with protocol extensions for powerful default implementations

This feature enables you to design flexible, modular code that follows interface segregation principles while maintaining clear type relationships.

Exercises

  1. Create a Drawable protocol with basic drawing functionality, then create AnimatedDrawable that inherits from it with animation-specific requirements.

  2. Design a set of protocols for a banking app: Account as a base protocol, with CheckingAccount and SavingsAccount inheriting from it. Implement a Bank struct that manages different account types.

  3. Create a protocol hierarchy for a shopping cart system with Item, DiscountableItem, and TaxableItem protocols. Implement products that conform to different combinations of these protocols.

Additional Resources



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)