How to avoid building apps that crash by parsing JSON the safe way in Swift

When you’re just getting started with Swift, it’s easy to fall into the trap of doing things the easy way, especially when working with JSON. But if you parse JSON the easy way, your app will crash, and you’ll be scratching your head wondering why. For your users and for your own sanity, don’t build apps that crash – be sure that when you parse JSON in Swift, you do it the safe way, not the easy way.

Let’s start with some simple JSON we’d like to parse:

{
"username": "josh"
}

Now let’s assume we have this JSON data in a NSData object called data, similar to what we’d get from NSURLSession (or even a third-party networking library) after making a request.

The easy, WRONG way to parse JSON in Swift

So to parse the JSON above and print the username to the console, you could do something similar to what you’d do in Objective-C. In Swift:

var jsonError: NSError?
let json = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &jsonError) as NSDictionary

if let unwrappedError = jsonError {
    println("json error: \(unwrappedError)")
} else {
    let username = json.valueForKeyPath("username") as String
    println("username: \(username)")
}

And this works… but only on the happy path. It has a few problems – it’s just too dependent on getting properly formatted JSON. First of all, if the response is a JSON array instead of a dictionary, your app will crash at runtime. And second – assuming you do get the dictionary you’re hoping for – if username doesn’t exist in the JSON response, or if its value isn’t a String, the app will crash. I’m guessing this isn’t exactly the user experience you’re aiming for…

The safe way to parse JSON in Swift

Wouldn’t it be nice if we could parse JSON in a way that doesn’t crash the app when we get unexpected data? Sure, we can hope to always get the data we want… but hasn’t an API let you down before? Yeah, me too. So let’s build a hedge around our code to protect it from crashing we parse our JSON.

To do that, we can use optional binding to safely unwrap the values, avoiding crashes if the username key doesn’t exist, or if it can’t be parsed into a String. Check it out:

var jsonError: NSError?
if let json = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &jsonError) as? [String: AnyObject] {
    if let unwrappedError = jsonError {
        println("json error: \(unwrappedError)")
    } else {
        if let username = json["username"] as? String {
            println("username: \(username)")
        }
    }
}

Now we’ve just added some extra question marks, changed a type, and added another optional binding statement. Let’s break it down and discuss what’s going on, starting with the first change:

if let json = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &jsonError) as? [String: AnyObject]

Here, we’re using the as? syntax to make sure we can cast the result of JSONObjectWithData to a Swift dictionary where the keys are Strings and the values are AnyObjects. If it can’t cast – perhaps because we got an array in our JSON – it’ll just skip right over the if block. No crash, no unhappy users. Hooray!

The next change we made was in getting the value for username out of the dictionary. Let’s look at that:

if let username = json["username"] as? String

This time, instead of assuming we’d get a String and forcing a cast, we’re hoping we get a String and failing silently – without crashing – if we don’t get the String we expected. We’re using optional binding to unwrap the value of the username key in our dictionary and attempting to cast it to a String with the safe cast (as?) syntax. In this case, when the cast fails, the result is nil, and the if block is skipped. No crash, happy users. Hooray!

You can tinker with the full source code from this article by downloading the Playground on GitHub.

If you landed here from the 5-Part Guide to Getting Started with Swift, you’ll get the next lesson in your inbox soon, so stay tuned! If you haven’t signed up for the Guide yet, don’t miss the rest of it: enter your name and email in the box below to get the rest of the Guide and continue learning Swift.