In my new gig, my first assignment has been to work on a proof-of-concept app that demonstrates a new architecture we’re considering. (If you care, a combination of VIPER and RxSwift.)
This new app is written entirely in Swift. I’m new to Swift.
Taking inspiration from Brent Simmons’s Swift Diary, there are a few things about Swift that are deserving of praise and I plan to call out as I’m learning the language.
Today’s case study: Enumerations.
Enumerations are not a new construct; they’ve been in most languages I’ve worked with. Generally speaking, they look something like this, in most C-derived languages:
enum LibraryBookState {
CheckedIn,
CheckedOut
}
Think of it as saying “For a library book, it can either be CheckedIn
or
CheckedOut
. Nothing else.”
As with life, things are rarely that simple.
In this contrived example, it’s useful to know that the library book is checked
out to someone, but to whom? We’d need another variable to track the patron
the book is checked out to. So, if we were designing a Book
class in Swift,
it may look like this:
class Book {
var status: LibraryBookState
var checkedOutTo: Patron?
var title: String
var author: String
}
That’s a little, well, weird, if you think about it. The patron the book is
checked out to is really part of the state of the book. A part of the state
that we’re capturing in the LibraryBookState
enumeration.
Wouldn’t it be nice if we could keep track of who the book is checked out to
within the LibraryBookState
enumeration?
In Swift, we can. All thanks to associated values.
We can write a Swift version of our enumeration like so:
enum LibraryBookState {
case CheckedIn
case CheckedOut(patron: Patron)
}
Note the change in CheckedOut
: now it looks like a function call. Though
syntactically a little curious, CheckedOut
is still just an enumeration
value. However, it has a super power: it carries with it an associated
value.
The only way to set a variable to the CheckedOut
enumeration value is
to also provide the associated Patron
along with it.
let casey = Patron(name: "Casey Liss")
var state = .CheckedOut(patron: casey)
Similarly, when we are using a switch
to evaluate the value of that
enumeration, we can get the value back out:
switch state {
case .CheckedIn:
// ...
case .CheckedOut(let p):
// p is the associated Patron
}
Associated values are a perfect solution to a problem I didn’t know I had.
To return to our Book
class, we can now simplify it:
class Book {
var status: LibraryBookState
var title: String
var author: String
}
We no longer need checkedOutTo
since we’re capturing that within
LibraryBookState
. The revised class is much cleaner, and more
directly represents the way we wish to model a book.
I’ve found associated values are used very frequently for the results of network requests. Many take a form like this:
enum FetchBookResult {
case .Success(book: Book)
case .Failure(error: ErrorType)
}
Using enumerations in this way makes it so much easier to return a value from a function, as you don’t have to do any awkward dancing with tuples or multiple callbacks or equivalent:
func fetchBook(id: Int) -> FetchBookResult {
// ...
}
Enumerations in Swift have many other tricks as well:
- Properties
- Methods
- Extensions
- Protocols (think
interface
s)
It’s stunning how powerful support for these paradigms makes enumerations.
I’m only three weeks in, but so far Swift is really impressing me. It’s bringing a joy to development that I haven’t felt in quite some time. I often feel like I’m fighting my own ignorance; I rarely feel as though I’m fighting the language.
There’s a lot in flux with Swift these days, which makes many people I deeply respect shy away from it. That may come to burn us, if we try to go all-in on Swift; especially once Swift 3 comes along. However, I’m very hopeful and excited for the future, as it’s clear Swift is a language that is somehow able to be both well-considered and improving rapidly.