Functional Swift for Dealing with Optional Values

Gordon Fontenot

Dealing with Optional values (specifically Implicitly Unwrapped Optionals) is unfortunately an everyday part of our life as forward-looking iOS developers. The entirety of the Cocoa API won’t be audited for optionals for a good long while, so these things are here to stay. This means we’re going to introduce some actual runtime errors into our application where we might not have encountered them before.

Let’s look at a fairly standard Objective-C object that we might be pulling into our app through a library:

@interface TBUser

@property (nonatomic, copy) NSString *fullName;
@property (nonatomic, copy) NSString *emailAddress;
@property (nonatomic, copy) NSString *twitterUsername;
@property (nonatomic) NSURL *websiteURL;

@end

Now, back in our Swift application, we start building a View Model to present this data to the view object:

struct UserViewModel {
    let user: TBUser
}

This view model is instantiated with an instance of TBUser (remember that using a Struct gives us the initializer for free). We can then start building out the computed properties for the view model to pass the data from the model:

extension UserViewModel {
    var displayName: String {
        return user.fullName
    }

    var email: String {
        return user.emailAddress
    }

    var twitterHandle: String {
        return "@\(user.twitterUsername)"
    }
}

All of this looks fairly straightforward. However, once we run the application and view a user, we get a crash:

fatal error: unexpectedly found nil while unwrapping an Optional value

Upon bridging to Swift from Objective-C, those properties are being turned into implicitly unwrapped optionals. And apparently, our library isn’t guaranteeing that we actually get values back for some of these properties.

“This seems simple enough” we say, and throw a quick guard around the implementation:

var twitterHandle: String {
    if let username = user.twitterUsername {
        return "@\(username)"
    }

    return ""
}

And there we go. Now, when we navigate to that same user, we don’t see that crash. We get the formatted username when there is one, and an empty string when it doesn’t exist.

Except… that if let is kind of gross. And then we start looking around and realize that this might be something we have to do a lot. It’s a fairly common pattern when dealing with optional values:

  1. Check to see if there is a value
  2. Do something with the unwrapped value
  3. Do something else if the optional is .None

Luckily, we can use a functional concept to simplify this for us.

Enter, fmap

(Quick note for Functional Programming nerds: we’re really just talking about fmap in the context of Optional here. I know this is a simplification. Please don’t email me.)

The function fmap (represented as an infix operator as <$> in Haskell and which we’ll define as <^> in Swift) can be implemented as so:

infix operator <^> { associativity left }

func <^><A, B>(f: A -> B, a: A?) -> B? {
    switch a {
    case .Some(let x): return f(x)
    case .None: return .None
    }
}

Put into words, fmap takes a function and an optional value. If the optional value is .Some, it passes the contained value into the function. If the optional value is .None it just returns .None.

So our first step here is to create that function that we want to pass to fmap.

func usernameFormat(name: String) -> String {
    return "@\(name)"
}

Note that now we’re dealing with a non-optional instance of String. So we have a guarantee from the compiler that when this function is called, we will have a name to format. Rad.

Now we can try to start to work that into the original implementation:

var twitterHandle: String {
    if let username = user.twitterUsername {
        return usernameFormat(username)
    }

    return ""
}

Now, we introduce fmap. Hold onto your butts:

var twitterHandle: String {
    let handle = usernameFormat <^> user.twitterUsername
    return handle ?? ""
}

That actually looks really nice to my eye. And we get to take advantage of the insanely awesome nil coalescing operator (??). This operator returns the value of the optional, or the default value if the optional is .None. This is the same as the fromMaybe function from Haskell.

So again, as a recap, putting into words what’s happening here:

We create the handle constant (which is of the type String? after type inference) with the return value from fmaping usernameFormat over the optional username. If the username is .None this will mean that handle is .None. However, if the username is .Some, the contained value will be passed into usernameFormat, which will set handle to the formatted string, wrapped in .Some.

Then, we return the contained value of handle, or an empty string if handle is .None.

Whew.

Let’s look at a more interesting example.

Multiple optional values

Now we want to display that website on the screen. We add the following function to our View Model:

var website: String {
    let url = user.websiteURL
    return "\(url.host)\(url.path)"
}

Navigating to a user in the UI reveals another unexpected bug. Instead of nicely formatted URLs like example.com/path, we have something else entirely:

Optional("example.com")/Optional("path")

That’s… not what we wanted at all. Even worse, some websites are coming back as nil/nil. So what’s going on here?

Turns out, not only is the user’s websiteURL property an implicitly unwrapped optional, but the host and path properties on NSURL are bridged into optionals as well. If we decided to use if let here, we’d end up with the following:

var website: String {
    if let url = user.websiteURL {
      if let host = url.host {
          if let path = url.path {
            return "\(host)/\(path)"
          }
      }
    }

    return ""
}

Again, to my eye those nested if let statements are ugly and hard to parse. Luckily for us, we can use the same pattern we used before with a small addition to handle this cleanly.

Enter, apply

The function apply (represented with the infix operator <*> in Haskell, and defined in Swift by us as the same) can be implemented as so:

infix operator <*> { associativity left }

func <*><A, B>(f: (A -> B)?, a: A?) -> B? {
    switch f {
    case .Some(let fx): return fx <^> a
    case .None: return .None
    }
}

It’s very similar to our implementation of fmap. In fact, it even uses fmap in the implementation. The main difference here is that the function itself is an optional. Yes, that’s right, functions can be optional values just like anything else. Wild, right?

So walking through our implementation of apply, it works much like fmap, except that we’re pattern matching on the function optional (f), not the normal optional value (a). If we have a function, we return the result of fmaping the function over a. Remember that this itself will then check to see if a exists. If f is .None, we return .None.

The cool thing about this is that it allows us to chain things together in a way that means when one thing fails (returns .None), everything else fails down the line.

So how can we use this? Much like the original solution, we will create a function to hold onto that formatting code, but this time we’ll implement the function in curried form, which allows for partial application:

private func websiteFormat(host: String)(path: String) -> String {
    return "\(host)\(path)"
}

Notice that each argument is in its own set of parens. That’s the important part here. With this syntax, when we give the websiteFormat function the host argument, it actually returns a new function that takes the path argument. When we hand this new function the path argument, it satisfies all of the required arguments, and returns the string we expect.

So now, we can take our fmap pattern from before and use apply to chain it together with the second argument:

var website: String {
    let url = user.websiteURL
    let websiteString = websiteFormat <^> url?.host <*> url?.path
    return websiteString ?? ""
}

We create a local variable url. It’s typed as NSURL!, but we’re going to treat it like a full blown optional.

We use fmap and apply to partially apply websiteFormat to the host, and then if that succeeds, we pass in the path. If that succeeds as well, we get .Some with a nicely formatted string. If it fails, we get .None.

We then use the ?? operator to return the value, or an empty string.

I think that looks significantly nicer than those earlier nested if let statements. And we don’t have to check for .None when accessing url because we’re using the optional chaining syntax. So if url is .None this whole thing results in .None and we return an empty string.

Wrap up

Even outside of the scope of amazing type-safe JSON parsing, these concepts from Functional Programming can make our life dealing with ubiquitous optional values just as safe (if not safer) than when we could message nil all we wanted.

What’s next

Read my colleague Tony’s posts on using these same concepts (and a few others) for parsing JSON values safely and cleanly.

Also, check out Argo, the JSON parsing library that was developed alongside those posts.