Swift Tip: An Example Refactoring
In last week's tip, we showed you a typical refactoring from our server-side Swift project, as we continue to port the Swift Talk backend from Ruby to Swift. This week, we follow with another useful refactoring.
To fetch all episodes for a Collection, we have a simple method in an extension. Likewise, we have a computed property that calculates the total duration of all the episodes in the collection:
extension Collection {
func episodes() -> [Episode] {
return Episode.all.filter { $0.collections.contains(id) }
}
var totalDuration: TimeInterval {
return episodes.map { $0.mediaDuration }.reduce(0, +)
}
}
These implementations aren't finished; they don't take the role of the user into account. For example, we want to hide the unreleased episodes from regular users, and only show them for administrators.
We refactored the episodes()
method like this:
extension Collection {
func episodes(for user: UserData?) -> [Episode] {
return Episode.scoped(for: user).filter { $0.collections.contains(id) }
}
}
We also need to change the totalDuration
from a computed property to a method:
extension Collection {
func totalDuration(for user: UserData?) -> TimeInterval {
return episodes(for: user).map { $0.mediaDuration }.reduce(0,+)
}
}
However, it feels slightly wrong to pass on the user directly. What about other computed properties that depend on episodes? After thinking about it, we removed totalDuration(for:)
from our extension, and rewrote it like this:
extension Sequence where Element == Episode {
var totalDuration: TimeInterval {
return map { $0.mediaDuration }.reduce(0, +)
}
}
This changes our call site: instead of collection.totalDuration(for: user)
, we have to write collection.episodes(for: user).totalDuration
.
The nice thing about this implementation is that we can now reuse the functionality in other places — for example, we can use it when we show the total duration of all episodes on Swift Talk. We can also easily add more computed properties to the constrained extension on Sequence
without having to pass on the user everywhere.
If you like these kind of refactorings, check out Swift Talk, our weekly live-coding video series. We do them often! 😊