Swift enum
is very powerful and versatile. It's a type in its own right, with
support for methods and initializers, and the associate values feature allows
us to use it in some nifty way.
One example of the power of enum
is the Optional
type, which we've seen in
detail in
previous
posts.
Optional
is defined as an enum
with two cases. The case None
represents
the absence of value, while Some
the presence of value. Some
wraps the
value which presence is representing in its associated value.
A less known but equally powerful type which we can implement as an enum is Either
.
Either
The Either
type represents duality, a value that can either be of a type or
another.
This is how we can write Either
:
enum Either<A, B>{
case Left(A)
case Right(B)
}
Either
allows us to write elegant, self explanatory, and type safe code by
switching on it:
let x: Either<SomeType, AnotherType> = ...
switch x {
case .Left(let someTypeValue):
// do something with someTypeValue
case .Right(let anotherTypeValue):
// do something with anotherTypeValue
}
Defined like that Either
might not seem very practical, but here's a great
usage for either: injecting extra cells in a table or collection view.
Injecting cells in a table view using Either
Sometimes we need to display lists in table or collection views with extra cells dedicated to call to action, or advertisement banners, which we'll let the user remove through in-app purchase (a dev still needs to pay the bills right?).
We could do this by displaying a banner instead of the actual item for a given index path, and shifting the index used to fetch the next item we want to display from the array of data based on the number of banner cells that we've already displayed.
Or we could use Either
.
Rather than an array of cell models we could feed the data source an array of Either<CellModel, BannerModel>
.
let data: [Either<CellModel, BannerModel>] = ...
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
In the tableView(_:cellForRowAt:)
method we could then get the item for the
current index path and simply switch and behave accordingly to its type.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item = data[indexPath.row]
let identifier: String = {
switch item {
case .Left: return pizzaCellIdentifier
case .Right: return adCellIdentifier
}
}()
let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath)
switch item {
case .Left(let pizza):
configure(cell, with: pizza)
case .Right(let ad):
configure(cell, with: ad)
}
return cell
}
This approach seems more solid to me. It doesn't rely on any math, making the
code easier to digest, and I find the switch-case
construct a very elegant
and self explanatory way to describe behaviour.
You can see the whole project from which the example code is taken on GitHub.
I came in touch with Either by reading Maybe Haskell by Pat Brisbin. If I hadn't stepped outside of my comfort zone and decided to learn something quite different from the tools I use for my "day job" I might have never learnt about this type and I'd still be shifting array indexes.
I'm saying this to encourage you to experiment an play around with different technologies, and see if some of the ideas you'll encounter can be brought back in your daily development.
Leave the codebase better than you found it.