Protocol Composition in Swift

swift Sep 04, 2023
Protocol Composition in Swift

In Swift, protocol composition is a powerful feature that allows you to combine multiple protocols into a single name. This can be very useful when you want to define a type that needs to adhere to multiple protocols simultaneously. Instead of writing multiple protocols again and again, you can just give a single name by combining them.

In this article, you will learn about:

  • Protocol Composition without typealias
  • Protocol Composition with typealias
  • Using Composition in Functions
  • Predefined Protocol Composition
  • Where to go next?

Let's deep dive into some practical use cases of protocol composition with examples.

Protocol Composition without Typealias :

Imagine you're building a weather app, and you want to display weather information for various locations. You need to represent both the weather data and the location data, and you also want to display the weather information. You'll first define all the protocols that represent the required functionality:

// A protocol to store weather information.
protocol WeatherData {
    var temperature: Double { get }
    var condition: String { get }
}

// A protocol to store location information.
protocol LocationData {
    var cityName: String { get }
    var latitude: Double { get }
    var longitude: Double { get }
}

// A protocol to display information.
protocol Displayable {
    func printInfo()
}

In the code snippet, we declared three different protocols WeatherData, LocationData, and Displayable.

  1. The WeatherData protocol can be used to hold the weather information.
  2. The LocationData protocol can be used to hold the location information.
  3. The Displayable protocol to display the weather information.

 

Further, create a struct called City that conforms to all the above protocols. But how these three protocols can be relate to City type?

Well, the WeatherData protocol can be used to define the weather information for a city. The LocationData protocol can be used to define the location information of a city. Similarly, the Displayable protocol can be used to print the city information.

Here is the struct City that conforms to all protocols:

struct City: WeatherData, LocationData, Displayable {
    
    let cityName: String
    let latitude: Double
    let longitude: Double
    let temperature: Double
    let condition: String
    
    func printInfo() {
        print("City: \(cityName), Condition: \(condition), Temperature: \(temperature)°F")
    }
}

In the preceding example, the City conforms to the protocols and implements the required method and properties. Now, whenever you need to conform all these protocols to a type, it requires to conform all protocols separately.

You can create an instance of City and utilize its combined functionality:

let newYork = City(cityName: "New York", latitude: 40.7128, longitude: -74.0060, temperature: 75.0, condition: "Sunny")

newYork.printInfo() // Output: City: New York, Condition: Sunny, Temperature: 75.0°F

In this example, the City struct conforms to multiple protocols:

WeatherData, LocationData, and Displayable. This composition allows you to create instances that represent weather data for specific locations while also providing the ability to print the information.

Protocol Composition with Typealias :

In the previous example, we defined three protocols: WeatherData, LocationData, and Displayable. We then created a struct WeatherLocation that conformed to all three protocols. Instead of directly conforming to all three protocols, we can simplify the conformance using typealias and protocol composition.

Now, this is how we can create a new typealias for all the protocols like below:

// Protocol Composition: CityData
typealias CityData = WeatherData & LocationData & Displayable

struct City: CityData {
    
    let cityName: String
    let latitude: Double
    let longitude: Double
    let temperature: Double
    let condition: String
    
    func printInfo() {
        print("City: \(cityName), Condition: \(condition), Temperature: \(temperature)°F")
    }
}

In the example with typealias, the typealias CityData clearly communicates the intent of the composition, making the City struct's conformance more concise and easier to understand.

Using typealias for protocol composition can lead to cleaner, more readable, and more maintainable code, especially in cases where you need to work with multiple protocols. It enhances code organization and reduces the visual noise associated with directly listing multiple protocol conformances.

Using Protocol Composition in Functions :

Using protocol composition in functions is a powerful technique in Swift that allows you to define functions which accept parameters conforming to multiple protocols. This enhances the flexibility and reusability of your code, enabling you to work with a wider range of types that provide specific functionalities.

Let's say you have two protocols, TextMessage and ImageMessage, and you want to create a function displayMessage() that works with types conforming to both protocols.

To call the function displayMessage(), you have to provide the value of type Message struct that conform to both protocols (TextMessage & ImageMessage):

protocol TextMessage {
    var text: String { get }
}

protocol ImageMessage {
    var imageURL: String { get }
}

struct Message: TextMessage & ImageMessage {
    var text: String
    var imageURL: String
}

func displayMessage(_ message: TextMessage & ImageMessage) {
    print("Message text: \(message.text) and image URL: \(message.imageURL)")
}

let newMessage = Message(text: "Hello, Swift Anytime", imageURL: "sampleurl.com/image/image1")
displayMessage(newMessage) // Prints: Message text: Hello, Swift Anytime and image URL: sampleurl.com/image/image1

Here, the Message instance satisfies the requirements of both TextMessage and ImageMessage, making them suitable arguments for the displayMessage() function.

By using protocol composition in functions, you create functions that can work with a broad range of types.

Overusing composition can lead to overly complex and less readable code. Make sure that protocol composition enhances your code's structure and reusability rather than complicating it unnecessarily.

Predefined Protocol Composition :

In the Swift library, there are many protocol compositions that are predefined to conform to multiple protocols. For example,

In Swift, Codable is a form of protocol composition. The Codable protocol is actually a typealias for a composition of two protocols: Encodable and Decodable. These two protocols work together to provide a unified way to encode and decode data, making it easy to serialize and deserialize objects to and from various formats such as JSON.

Here's how the Codable protocol is defined:

public typealias Codable = Encodable & Decodable
  • Encodable defines the protocol for types that can be encoded (serialized) into an external representation, such as JSON or binary data.
  • Decodable defines the protocol for types that can be decoded (deserialized) from an external representation back into a Swift object.

When you conform to the Codable protocol, you're effectively conforming to both Encodable and Decodable, which allows you to perform encoding and decoding operations on instances of that type without needing to explicitly implement the encoding and decoding methods.

Here's an example of using the Codable protocol:

struct Person: Codable {
    var name: String
    var age: Int
}

In this example, Person conforms to the Codable protocol, allowing us to easily encode it to JSON and decode it back from JSON using the JSONEncoder and JSONDecoder classes.

This shows how Codable combines the functionalities of Encodable and Decodable in a composition, making it easier to work with data serialization and deserialization in Swift.

Where to go next?

Congratulations, you are now one step ahead in the journey to master Protocol Oriented Programming concepts in Swift. In order to continue your learnings, we recommend you check out Protocol Extensions in Swift and Guide to Protocol Oriented Programming.

Signup now to get notified about our
FREE iOS Workshops!