6 Common Mistakes with Protocols in Swift and How to Avoid Them

Experienced iOS and React Native Developer with 10+ Years of Expertise - Available for New Opportunities. Connect Now!
Search for a command to run...

Experienced iOS and React Native Developer with 10+ Years of Expertise - Available for New Opportunities. Connect Now!
No comments yet. Be the first to comment.
This time, I'm exploring the fascinating world of AI, stepping away from my usual focus on iOS development. There's so much more to share about AI, and I can't wait to dive into it with you! If you'd like to follow along and receive regular updates, ...

The app is Time Tango, a Pomodoro timer app. I chose this concept after conducting keyword research for popular terms that face minimal competition on the app store. The app is free to use and received approval in its first review. The strategy focus...

In my last article, I talked about how data flows in SwiftUI. Now, Apple has released something new in iOS 17 - the Observation Framework. This changes how SwiftUI works with data. In this article, we will look at how to use the new Observable Macro ...

One of the critical elements of SwiftUI is its approach to managing data flow within applications. This comprehensive guide will provide you with a strong understanding of data flow in SwiftUI, how it operates, and why it's vital for your SwiftUI app...

Arrays are one of the most common data structures used in programming. In Swift, arrays are used to store ordered lists of values of the same type. They are incredibly versatile and powerful, but using them efficiently is key to writing high-performi...

Protocols, an integral part of Swift, hold a significant place in Swift development. They help us define a blueprint of methods, properties, and other requirements for particular tasks or functionalities.
As wonderful as protocols are, it's easy to make mistakes while using them, particularly when you are just starting with Swift. This post will outline some common mistakes developers make with protocols in Swift and how to avoid them. We will illustrate this with practical code examples.
One common mistake is confusing protocols with concrete types. Protocols define a blueprint, they cannot be instantiated directly. Let's see an example:
protocol Vehicle {
var numberOfWheels: Int { get }
}
struct Car: Vehicle {
var numberOfWheels: Int
}
let myVehicle: Vehicle = Vehicle() // WRONG
In the above example, we're trying to create an instance of a protocol which isn't valid. Instead, we should instantiate a struct or class that conforms to that protocol:
let myVehicle: Vehicle = Car(numberOfWheels: 4) // RIGHT
When a type promises to conform to a protocol, it must implement all the required properties and methods of that protocol. Forgetting to do so will lead to a compile-time error:
protocol Drawable {
func draw()
}
struct Circle: Drawable { // ERROR: Type 'Circle' does not conform to protocol 'Drawable'
}
The Circle struct says it conforms to Drawable, but doesn't implement the draw() method. The correct implementation would be:
struct Circle: Drawable {
func draw() {
print("Drawing a circle.")
}
}
Protocols can inherit from other protocols, and this can sometimes lead to confusion. A common mistake is forgetting that a type conforming to a protocol that inherits from another protocol must satisfy the requirements of all protocols in its inheritance chain.
protocol Vehicle {
var numberOfWheels: Int { get }
}
protocol MotorVehicle: Vehicle {
var engineType: String { get }
}
struct Car: MotorVehicle { // ERROR: Type 'Car' does not conform to protocol 'Vehicle'
var engineType: String
}
In this example, Car conforms to MotorVehicle but does not provide the numberOfWheels property required by the Vehicle protocol. To fix this, we must provide all the required properties:
struct Car: MotorVehicle {
var numberOfWheels: Int
var engineType: String
}
Swift protocols cannot store state. They can only declare properties, which must be implemented by the conforming types. This often confuses developers who come from class-based or object-oriented backgrounds.
protocol Drawable {
var color: String { get set } // Error: Property in protocol must have explicit { get } or { get set }
}
To avoid this, always remember that protocols are used to define a blueprint for methods and computed properties, not to store the state.
Self in ProtocolsWhen defining a protocol, Self is used to represent the current type. This can lead to problems if you try to use it in a way that doesn't make sense:
protocol Copyable {
func copy() -> Self
}
struct Note: Copyable {
var text: String
func copy() -> Note { // ERROR: Method 'copy()' in protocol 'Copyable' requires the return type to be 'Self'
return Note(text: self.text)
}
}
In the above example, we've defined a Copyable protocol with a copy() method that should return an instance of the conforming type. However, when we attempt to implement this in the Note struct, we specify Note as the return type instead of Self. This is incorrect because Self can refer to any type that implements the protocol (including any subclasses, in the case of classes), not just Note. The correct implementation would be:
struct Note: Copyable {
var text: String
func copy() -> Self {
return Self(text: self.text)
}
}
Another common mistake when using protocols is ignoring associated types. Protocols in Swift can contain associated type placeholders, which are replaced with a real type when the protocol is adopted. Here's an example of where this goes wrong:
protocol Container {
associatedtype Item
mutating func add(_ item: Item)
var count: Int { get }
}
struct Stack: Container { // ERROR: Type 'Stack' does not conform to protocol 'Container'
var items = [Int]()
mutating func add(_ item: Int) {
items.append(item)
}
var count: Int {
return items.count
}
}
In the above example, Stack doesn't define what Item is, which leads to a compilation error. The correct way to implement the protocol would be:
struct Stack: Container {
typealias Item = Int
var items = [Item]()
mutating func add(_ item: Item) {
items.append(item)
}
var count: Int {
return items.count
}
}
By understanding these common mistakes and how to avoid them, you can start to harness the full power of protocols in your Swift applications. Remember, practice makes perfect. The more you use protocols, the more familiar you will become with their rules and nuances.
I hope you enjoyed this article, and if you have any questions, comments, or feedback, then feel free to comment here or reach out via Twitter.
Thanks for reading!