Swift 5: Useful Protocols to Write Like a Pro
Hi iOS Developers,
I’m guessing we have all experienced the pain of spending a lot of time implementing a function, only to realize later that Swift has a built-in function that does the same thing. In this article, I will talk about a few useful protocols that can save you much time and take your code to the next level.
1. CaseIterable
The CaseIterable protocol allows you to retrieve all the values of the type. Let’s take a look at an example:
Without CaseIterable:
enum City {
case new_york
case bei_jing
case vancouver
....
}let cities: [City] = [.new_york, .bei_jing, .vancouver, ....]
In this example, when we want to retrieve all the cities from the City enum, we would have to type them out manually. Imagine that if this enum has hundreds of thousands of cities, it would be a nightmare to do that.
With CaseIterable:
enum City: CaseIterable {
case new_york
case bei_jing
case vancouver
....
}let cities = City.allCases
With the allCases
property provided by the CaseIterable protocol, we can retrieve an array of all the cases of City. This can save a significant amount of time.
2. CustomStringConvertible
Not everyone likes the default description for each object. Sometimes we want to give an object a more user-friendly description just for reading and recognition purposes.
Without CustomStringConvertible:
struct Weather {
let city: String
let temperature: Double
}let weather = Weather(city: "New York", temperature: 97.5)print("The temperature in \(weather.city) is \(weather.temperature)°F.")print("The temperature in \(weather.city) is \(weather.temperature)°F.")print("The temperature in \(weather.city) is \(weather.temperature)°F.")
If we want to print a readable message, we have to copy and paste the message every time we need to print it. Also, if we want to alter the contents of the message at some point, we have to find every single place where that message exists and modify it as needed. This wasteful process could be very time-consuming.
With CustomStringConvertible:
struct Weather: CustomStringConvertible {
let city: String
let temperature: Double var description: String {
return "The temperature in \(city) is \(temperature)°F."
}
}let weather = Weather(city: "New York", temperature: 97.6)print(weather) // The temperature in New York is 97.6°F.
print(weather) // The temperature in New York is 97.6°F.
print(weather) // The temperature in New York is 97.6°F.
By inheriting the CustomStringConvertible protocol, we need to provide a value to the description property. Every time we want to use the object as a String, the program will refer to the description property. Now we can much more easily print and/or modify our message using this handy protocol :)
3. IteratorProtocol
This protocol provides a .next() function that allows access to the next value. Let’s say we have a list of names, and we want to iterate the list one-by-one without the need to store the current index.
Without IteratorProtocol:
let names = ["Bob", "Amy", "Mike"]
var currentIndex = 0print("\(names[currentIndex]) did the laundry.")
currentIndex += 1print("\(names[currentIndex]) did the dishes.")
currentIndex += 1print("\(names[currentIndex]) did nothing.")// Outputs
Bob did the laundry.
Amy did the dishes.
Mike did nothing.
With IteratorProtocol:
let names = ["Bob", "Amy", "Mike"]
var nameIterator = names.makeIterator()print("\(nameIterator.next() ?? "") did the laundry.")print("\(nameIterator.next() ?? "") did the dishes.")print("\(nameIterator.next() ?? "") did nothing.")// Outputs
Bob did the laundry.
Amy did the dishes.
Mike did nothing.
The makeIterator()
function returns an object that inherits the IteratorProtocol protocol. We can use the next()
function to get the next value without the need to track the current index. Remember, the next()
function returns an optional value.
Conclusion
There are tons of useful built-in protocols in Swift, and I can’t list them all in one article. You know what a waste of your time it is if you write code from scratch and later find that Swift already provides built-in supports. Using these protocols solves that problem and also makes your code much cleaner and more efficient.
