You are on page 1of 75

Introduction

When I first started developing in Swift, I felt like I was in a constant battle with the compiler, trying to get it to understand what I wanted to do. This is the struggle many developers have with Swift, especially when parsing JSON, which often leads to the optionals pyramid of doom that’s hard to write, hard to understand, and easy to get wrong.

As you learn how to parse JSON in Swift, you’ll also learn principles that will make the rest of your Swift code code more readable, more stable, and give you confidence that you’ve done it correctly.

You’re going to learn new concepts that are hard to grasp, so it’s not your fault for not understanding something. If anything is unclear as you’re reading, ask me about it. Send me an email (josh@roadfiresoftware.com) with your question and I’ll do my best to clear things up.

If you follow the principles in this book, you’ll be able to parse JSON in Swift without worrying that your app will crash or that your data won’t be displayed. You’ll be confident that your app can handle any JSON that comes its way.

Josh Brown

Deserializing JSON into Swift types

Imagine you’re building an app that shows popular repositories on GitHub. With the Search Repositories API, you can get top repositories by number of stars, filtered by a given programming language. For our app, we’ll grab the most popular Swift repositories on GitHub using the following request:

This gives us a response with an array of items; for now, we’ll concentrate on a single (truncated) item from that array:

{

 

"id": 22458259, "name": "Alamofire"

}

How would we deserialize that into Swift types so we can use the id and name in our app? We’ll assume we have the JSON in a variable called data of type NSData?, which should be the type you get back after making a network request. Let’s take a look at the code, then get into the explanation.

do {

if let data = data, json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [String: AnyObject] { let id = json["id"] as? Int let name = json["name"] as? String print("found repo with id: \(id) name: \(name)")

} } catch { print("couldn't parse JSON")

}

Let’s start from the top - you may be wondering: why is everything in a do- catch block?

In Swift, errors are thrown and handled inside of do-catch blocks. In the

NSJSONSerialization class, the JSONObjectWithData:options method is

marked with the throws keyword, which tells us it may throw an error. Swift then forces us to handle this, either by catching the error, or by marking our method with throws, allowing the error to bubble up another level. We’ve chosen to handle the error in a catch block - it’s unlikely that our caller would care about the details of a JSON parsing error, so we’ll just handle it ourselves right here by printing it to the console.

In the next statement, we’re using optional binding to unwrap the data, which may be nil. If you haven’t worked with Swift for long, this whole concept may seem foreign and strange - and the fact that we’re creating a new constant (data) with the same name as the optional data may be confusing. This is a common pattern in Swift, and once you grasp the way optionals work in Swift, you’ll be able to look quickly and know exactly what this is doing. In short, we’re creating a new constant called data if and only if our original data is not nil. Assuming it’s not, we can now use our new constant data in the following line with the guarantee that data is not nil. (If you feel like you need to learn more about this, read about Optional Binding in The Swift Programming Language.)

Let’s take a closer look at the next line:

json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [String: AnyObject]

There’s a lot going on here, so let’s pick it apart one concept at a time. First, we’re creating a constant through optional binding again, this time called json. How do we know it’s a constant, you ask? From the let in the previous line.

Next, we need to call JSONObjectWithData:options: with try since it may throw an error. If you’re familiar with Objective-C, you’ll notice that we don’t pass in an NSError pointer here - Swift’s do- try-catch error handling replaces the NSError pointer. So now instead of passing in an error pointer and checking it later, you only need to catch the error and handle it in the catch block.

In the call to JSONObjectWithData:options:, we first pass in the data that holds the JSON. For the options parameter, we send in an empty array, indicating that we don’t want to specify any options here. (If you want to see the options you can specify, you can check out the documentation for NSJSONReadingOptions, but you typically won’t need additional options.)

Let’s take a look at the last part of the statement:

as? [String: AnyObject]

Here, we’re attempting to convert our JSON into a dictionary with String keys and AnyObject values. If this part fails, it returns nil. In our case, this would mean skipping over the code inside the if block since this is all part of an optional binding statement.

Assuming we have non-nil data, and that we can transform it into the particular type of dictionary we want, the following lines are executed.

let id = json["id"] as? Int let name = json["name"] as? String

In these lines, we’re pulling the “id” and “name” keys from the dictionary and casting them to the type we expect. In the first line, we attempt to cast the “id” value to an Int. If the cast succeeds, we’ll have the value of the “id” in our id constant, and if it fails, our id will be nil. Either way, the type of our id is Int?, known as an optional Int. The next line does the same thing with a dierent key and type, attempting to cast the “name” from our JSON to a String.

And finally, we have our results and print them to the console:

print("found repo with id: \(id) name: \(name)")

Which gives us the following lovely output:

found repo with id: Optional(22458259) name:

Optional("Alamofire")

…which is probably not quite what we wanted. To fix this, should we just force cast our values with as! instead of playing it safe with as??

The dangers of forced type casting

When you’re casting types in Swift - and especially when parsing JSON - it’s easy to use a forced cast to guarantee you get a non-optional type instead of an optional type. Remember our code from before:

let id = json["id"] as? Int let name = json["name"] as? String print("found repo with id: \(id) name: \(name)")

And the output it produces:

found repo with id: Optional(22458259) name:

Optional("Alamofire")

Not quite what we’re looking for, is it? We could solve the problem with the following HORRIBLY CRASH-PRONE approach:

// CRASHY CODE AHEAD - BEWARE let id = json["id"] as! Int let name = json["name"] as! String // END CRASHY CODE print("found repo with id: \(id) name: \(name)")

Oh, but it works, you say! The output is exactly what we want now! Well, yes, technically, it does work…as long as “id” is in our dictionary AND is non- nil AND can be cast to an Int AND “name” is in our dictionary AND is non-nil AND can be cast to a String. If any of those fail, however, our app will crash, since we’re force casting with the as! keyword.

So what?, you might think. I know I’m going to have an “id” that’s an Int and a “name” that’s a String, based on the JSON output "om the API. And I’m sure you’re right - but what if something changes? What if some broken code is deployed to the server that returns a String for the “id”? Or what if the API changes unexpectedly and the “name” property becomes “title” instead? Will your app fail gracefully? You can never be 100% sure what you’ll get from your API, so it’s best to be prepared for the worst. Let’s take the following approach instead:

if let id = json["id"] as? Int, name = json["name"] as? String { print("found repo with id: \(id) name: \(name)")

}

Here again we’re using optional binding to conditionally create constants for the values we need. If we can find the “id” AND we can cast it to an Int AND we can find the “name” AND we can cast it to a String, then our print statement will be executed. And this time, without the optionals:

found repo with id: 22458259 name: Alamofire

Did you notice what happened there? We’re now meeting all of the same conditions we hoped to meet by force casting - getting both properties and casting to their respective types - but now if a cast fails or if we make a typo in our dictionary key, the app no longer crashes. It’ll just skip over the if block and happily continue executing the rest of the code. If we want to handle the failure, we can do that by adding an else block:

else { print("couldn't parse the id and name properties") }

…which does what you’d expect - it prints to the console in any of the

failure cases. If there’s there’s no “id” or it can’t be cast to an Int or there’s

no “name” or it can’t be cast to a String, we’ll get the following output:

couldn't parse the id and name properties

Our code is now safe from server- side changes; we can now be sure it won’t

crash if we get unexpected JSON keys or values. On top of that, we’re

unwrapping the optionals so we can work with the values inside - which is

what we really care about.

Avoid force

Avoid

force unwrapping

unwrapping ((!)) optionals

optionals whenever

whenever possible.

possible. This means

avoiding the forced cast operator, as!, whenever you can. Instead, try

combining optional binding (if let) with the conditional form of the type

cast operator (as?) to accomplish the same behavior in a much safer, less

error-prone way. Later we’ll look at using guard statements as well, which

can sometimes be a cleaner alternative to optional binding with if let.

Now that we have the basics down for transforming JSON into Swift types,

where should we put all this code?

Where should all this JSON code go?

Let’s take a look at all the JSON code we have so far.

do {

if let data = data, json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [String: AnyObject] { if let id = json["id"] as? Int, name = json["name"] as? String { print("found repo with id: \(id) name: \

(name)")

} else { print("couldn't parse the id and name

properties")

}

}

} catch { // handle the error

}

Now, please don’t tell me you’re planning to put all of that into your view

controller. If it’s there right now, that’s fine, but before you ship an app, that

code needs to live somewhere else. If you don’t move it out of the view

controller, you’ll end up with what the community calls Massive View

Controllers, which are a nightmare to maintain, hard to test, and violate the

single responsibility principle. Code in the view controller is also impossible

to reuse, so your networking and JSON parsing code should not live there.

Let’s move the JSON code into its own class for now. You may find later

that you want multiple classes to parse JSON, but we’ll start with one. Let’s

call it JSONParser and define a single method.

class JSONParser { func parseDictionary(data: NSData?) -> [String:

AnyObject]?

}

Now, let’s take a look at that method. First, it accepts NSData? as a

parameter. This likely matches what you get from your API request,

whether you’re using NSURLSession or some third-party library to fetch the

data. And second, it returns an optional dictionary of JSON. If we’re able

to parse the JSON out of the data, we should return the dictionary.

Otherwise, we’ll just return nil.

Let’s look at the implementation now.

func parseDictionary(data: NSData?) -> [String: AnyObject]? { do { if let data = data, json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [String: AnyObject] { return json

} } catch { print("couldn't parse JSON")

}

return nil

}

The main dierence with the code now from what we had before is that

we’re returning the dictionary representation of the JSON in the event that

parsing succeeds; otherwise, we’re returning nil. We’ll leave it up to the

caller to determine how to handle nil. But why should we return nil instead of

returning an empty dictionary or throwing an error?

Returning nil allows the caller to more easily deal with the result. If we

return nil when something goes wrong, the caller can use an optional

binding (if let) statement like we saw earlier to handle it. If instead we

returned an empty dictionary, the calling code wouldn’t know something

was awry until it inspected the dictionary - or tried to use the data in it -

and found it to be empty.

We could throw an error instead of returning nil if we wanted to

communicate to the caller exactly what went wrong. In some cases, this

may be useful, but in parseDictionary, I’m not sure it is. What would the

caller do if they knew exactly what error occurred while parsing the JSON?

Would they present this error message to the user? That’s doubtful - the

user wouldn’t care or be able to do anything about a “JSON parsing failed”

message. Therefore, returning nil from parseDictionary gives our caller

just enough information, clearly indicating that parsing failed.

There are two big benefits to creating this JSONParser class and the

parseDictionary method. First, we no longer need this mess of JSON code

in the view controller, or in the completion handler of the network call.

And second, we can write an automated test for this quite easily now. All we

need is some NSData, and we can assert that we get a dictionary back.

But how do I actua#y write a test? And why would I even want to? We’ll get to

those questions soon, but first we need to understand how error handling

works and what to do when things go wrong.

Handling errors with do-try-catch

We’ve seen the do- try-catch combo, but we didn’t get into it in any depth.

Let’s talk about how that works in Swift and when you might need - or want

- to use it.

In Swift, errors are handled inside of do-catch blocks. Whenever a method

you’re calling can throw an error, you’re required to either handle it or

ignore it and let it be thrown out of your method. Let’s look at calling

JSONObjectWithData:options:, which is marked in the documentation

with throws.

func parseDictionary(data: NSData?) -> [String: AnyObject]? { do { if let data = data, json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [String: AnyObject] { return json

} } catch { print("Couldn't parse JSON. Error: \(error)")

}

return nil

}

Here, we’re choosing to handle the error that can be thrown from

JSONObjectWithData:options: by catching it in the catch block. When

we catch this error, we can use its implicit name to reference it, which is

error.

The other option we have is to ignore the error completely and let it be

thrown out of our parseDictionary method. To do that, we could simply

add the throws keyword to our method signature and remove the do-catch

statement, like so:

func parseDictionary(data: NSData?) throws -> [String:

AnyObject]?

But that’s probably not a good idea; we want to catch and handle this error

here. Whoever calls parseDictionary probably doesn’t care whether an

error was thrown; they just want the dictionary. If something fails, they just

need to handle the nil without also worrying about catching an error.

You should fully understand your options for error handling - there are a

Now we can get back to our question from earlier: why would we want to

write automated tests for our JSONParser?

Why test?

In Where

Where should

should all

all this

this JSON

JSON code

code go?

go?, we discussed how one of the

big benefits to creating a separate JSON parser class was that we can write

automated tests for it easily. But why would you want to write tests? What

benefit could they possibly provide?

Unit tests provide at least five main benefits: they help you ensure

correctness during development, catch bugs in existing features, allow you

to refactor with the confidence that you didn’t break anything, run much

much faster than manual tests, and they don’t even require you to be

present while they run. Let’s take a look at each of these individually.

1. Unit tests ensure correctness during development

As you’re developing code, good unit tests can give you confidence that the

code you just wrote does what you think it does. And when your code isn’t

right, unit tests will quickly point that out. Catching bugs early like this is

the best time to catch them, since tracking down and fixing them is always

easiest right after you wrote the code. It’s still fresh in your mind; you can

quickly get back into it and fix whatever’s broken.

If you develop a small piece of code, then run a test that fails, you’ll be sure

to find the issue quickly. If you develop a small piece of code without tests,

ship it to production, and get a bug report from a user, it’ll take you much

longer to track down and fix that piece of code.

2. Unit tests catch bugs in existing features

When you have good unit tests that are run frequently, you can catch

regressions before they get to the QA team - or worse, to your users. How?,

you wonder. Well, once you’ve written a test that verifies some behavior,

that test continues to verify that behavior every time the test suite is run.

As long as you have good tests that are run frequently, whenever you break

something in your code, you’ll have a failing test as soon as the test suite

runs again.

Establish the habit of running your tests before every commit and set up a

continuous integration server to run the test suite whenever code is pushed

to your central repository. With these habits and processes, you can be sure

your tests will run frequently, protecting your code against regressions by

catching them as soon as possible.

3. Unit tests allow you to refactor with confidence

Over time, most code needs to be refactored. This is true even if you made

excellent decisions early on; requirements grow and change, and at some

point you’ll have code that’s doing something it wasn’t originally intended

to do. It becomes hard to maintain and hard to add new features.

Refactoring is part of life as a developer, and it can be downright scary if

you don’t have good unit tests in place. Without them, it’s rea#y hard to

know whether your refactoring broke anything in the process. Sure, you can

always test manually after you finish refactoring, but unit tests can tell you

right away when something breaks - without you having to launch and

navigate through the app. They can pinpoint the exact method that failed

to produce the expected output, making it faster and easier to fix the

problem. Plus…

4. Unit tests run faster than manual tests

A unit test can run in a fraction of a second. It’s impossible to run a manual

test in that time. Even a whole suite of unit tests can usually run in less than

a minute - which is probably about as long as it takes to run one or two

manual tests. And on top of that…

5. Unit tests run without you needing to be present

Once you’ve written unit tests, they can be executed again and again

without you being there. You can run them while you sip your coee or

have them run automatically on a continuous integration server every time

you push your code. Your time is valuable; computer time is cheap. If you

can write a test to verify behavior instead of having to test it manually over

and over - every time you make a change - the automated test is worth its

weight in gold.

But even with all these benefits, is it worth spending the time to write

tests? You still have to run manual tests, so if you write unit tests, aren’t you

wasting time you could be writing production code?

I’d argue that you actually save time by writing tests. By reducing the

number of bugs in your code, you’re spending less time doing manual

testing and less time tracking down problems. And you’re giving yourself an

extra level of confidence you just can’t get without them - even if you run

thousands of manual tests.

Unit testing is a big part of my process as a developer, and I fully believe it

makes the apps I develop more stable, less buggy, and easier to maintain.

Whenever I start a new app, whether it’s for a client or for myself, I always

write unit tests. And even if I join an existing project that doesn’t have unit

tests, I often add some to ensure that the code I’m writing does what I

want it to.

So, how can you write unit tests for your shiny new JSON parser?

Creating a JSON test file

First things first - we need some JSON before we write our first test. Since

we’re working with the GitHub API, we’ll grab some JSON from the

Search Repositories API. Below are truncated results from searching for

the most popular Swift repository, by number of stars. If you’d like, you can

{

 

"total_count": 78457, "incomplete_results": false, "items": [ {

"id": 22458259, "name": "Alamofire", "description": "Elegant HTTP Networking in Swift"

}

]

}

In the results above, the repository shown is missing most of its properties.

This is sucient for our test, but if you’d like, you can view the full output

and use that to create your test file. Let’s take our JSON and put it into a

file in our unit tests target.

Why we wouldn’t just use live data "om the API?, you may be wondering.

That’s certainly an option, but it’s nice for unit tests to run as quickly as

possible since we want to run them frequently. If we include a network

request into this test (and potentially others), our unit tests may take a very

long time to run - and tests that take too long to run often don’t get run.

On top of that, network failures can cause tests to fail even when nothing is

wrong with your code, so it’s best to isolate what you want to test. And

finally, using canned data gives us more control over the input so we can be

sure our code properly handles a variety of scenarios.

Remember, we’re still using actual data from the API even though it’s not

live. This is often good enough - API response schemas don’t change very

often, and when they do, we can update our test file with the new data.

If you want to write automated tests against a live API, feel free to do so.

I’ve done it before; I’d just recommend creating a new unit test target for

these live tests and calling them what they are - integration tests.

Now, on with creating the JSON file for our unit test!

1. Create a new file

Select File > New > File… (N), then choose the Empty template.

Click Next Next.
Click Next
Next.

2. Name the file and ensure the Tests target is selected

Give the file a name, such as search-response.json, and make sure the

Tests target is selected as shown in the following screenshot.

Click Create Create. 3. Paste the JSON into the file Drop the JSON from the APIfull response from the API. 24 " id="pdf-obj-23-2" src="pdf-obj-23-2.jpg">

Click Create

Create.

3. Paste the JSON into the file

Drop the JSON from the API into the file and save it. You can use the

JSON sample below, or you can use the full response from the API.

{

 

"total_count": 78457, "incomplete_results": false, "items": [ {

"id": 22458259, "name": "Alamofire", "description": "Elegant HTTP Networking in Swift"

}

]

}

That’s it! Your JSON is now ready to be loaded into the test. Now, how do

we write a test for this?

Creating a JSON Test Case

Now that we’ve create a JSON file with data to test, we can create our test

case, load the JSON file into it, call our parser, and make some assertions.

Let’s dive in:

1. Create a new Unit Test Case Class

Select File

File >> New

New >> File…

File… (N). In the dialog that appears, choose the

Unit Test Case Class template.

Click Next Next.
Click Next
Next.

2. Name the class

For the class name, enter JSONParserTests. This fits with the naming

convention I use when creating unit test classes: start with the name of the

class under test (in this case, JSONParser) followed by the sux “Tests”.

Make sure it’s a subclass of XCTestCase and that the language is Swift.

Click Next Next.
Click Next
Next.

4. Make sure the test target is selected

Do a quick sanity check to make sure the correct folder, group, and target

are selected. This class should be in the Tests

Tests folder, group, and target as

shown in the following screenshot.

4. Make sure the test target is selected Do a quick sanity check to make sure

5. Remove unnecessary methods from the test case

For this test, we don’t need any of the methods provided by the

XCTestCase file template, so you can remove them all. Yep, the setUp,

tearDown, testExample, and testPerformanceExample methods can all be

deleted. Go ahead and do that, and you should be left with an empty test

case class like the one shown below.

import XCTest

class JSONParserTests: XCTestCase {

}

6. Create a test method

In your now-empty JSONParserTests class, add a new test method that’ll

be executed when you run the tests in your project. Let’s call it

test_parseDictionary since we’ll be testing the

parseDictionary method:

func test_parseDictionary() { }

Note that in order for methods to be executed as tests, they need to be

prefixed with the word “test” entirely in lowercase. You can add helper

methods to your test classes, too - just don’t prefix them with the word

“test”, as this is how Xcode knows which methods to treat as tests that can

be executed and which to treat as regular methods.

7. Load the JSON file into the test

Inside our test_parseDictionary method, we need to load the JSON file

so we can get the data and parse it. To do that, add the following lines:

let bundle = NSBundle(forClass: self.dynamicType) let path = bundle.pathForResource("search-response", ofType:

"json") let data = NSData(contentsOfFile: path!)

Why can’t we use NSBundle.mainBundle() like we do in the app? And what’s that

self.dynamicType stu?

We can’t use NSBundle.mainBundle() here since we need to load the test

bundle, not the main bundle. The easy way to do that is to load the bundle

that contains the class we’re in; hence the reason for using

self.dynamicType. After loading the bundle, we ask for a path to our

search-response.json file, then create a NSData object with the data from

that file.

8. Import the app module

Next, we’d like to create an instance of our JSONParser so we can take this

data we have and run it through the parser to see what comes out. In order

to do that, however, we need to import the main app module, which

contains our JSONParser class.

At the top of your class, add the following line to import your app module:

@testable import ParsingJSON

Your import may be dierent - it needs to be your Product

Product Module

Module

Name, which you can find in the project’s Build

Name

Build Settings

Settings under

Packaging. My project is called ParsingJSON; therefore my Product

Packaging

Module Name is ParsingJSON, so I need to import ParsingJSON.

The @testable keyword makes all of your app’s public and internal classes

and methods visible by your tests so you don’t have to change their access

modifiers just to test them.

9. Create an instance of the JSONParser and call its parse method

We need an instance of our JSONParser, which will allow us to call our

parse method with the data from our JSON file. Let’s do that now:

let parser = JSONParser() let results = parser.parseDictionary(data)

This should give us our search results as a dictionary, filled with all the keys

and values from the JSON. Let’s check and see if we get what we expect.

10. Write an assertion

To verify that we have the data we expect, XCTest provides assertions. We

can use XCTAssertNotNil to assert that something is non-nil, or

XCTAssertEqual to assert that two things are equal. And there are a host of

others you can use, like XCTAssertTrue and XCTAssertFalse; these are the

ones I use most frequently.

Let’s make sure our results aren’t nil:

XCTAssertNotNil(results)

And that we have the data we need:

XCTAssertEqual(78457, results!["total_count"] as? Int)

And while I would never encourage you to use ! to force unwrap that

results dictionary, it’s probably safe to do in a unit test since (a) we’ve

already asserted that it’s not nil and (b) it’s OK for the test to crash since

that’ll tell us something is wrong and we need to fix it.

11. Run the test!

Now, to actually run the test, you can use Product

Product >> Test

Test (U) to run all

the tests in the suite. To run only the tests in a single test case class, you can

click the small diamond in the editor to the left of the class name, as shown

in the screenshot below. And finally, if you just want to run a single test

method, you can click the small diamond in the editor to the left of the test

method (also shown in the screenshot below).

Run your test now, and take a look at the results, which appear in the console,

Run your test now, and take a look at the results, which appear in the

console, the Test Navigator (5), and right inside the editor. Your test

should pass, and your Test Navigator and editor should look something

like this:

Run your test now, and take a look at the results, which appear in the console,

Your console should also indicate that your test passed and that there were

no failures.

You now have the tools you need to test that your JSON parsing code

works as expected - which means you can have a level of confidence in it

without waiting until the rest of the pieces are in place to test it manually.

But is one test enough? How can you be sure your app can handle any JSON

that comes its way? And what happens if a test fails? How do you debug and

find out what’s wrong?

We’ll start with what to do when a test fails, and then we’ll discuss how to

write tests that ensure correctness - without going overboard.

Dealing with test failures

When a test fails, how do you handle it? What steps should you take to

track down and fix the problem?

Start by making sure your test is correct. Ensure that you’re sending the

right input to the code under test and making the right assertions about its

output. Once you’ve confirmed that your test is correct, you can start

debugging the code under test.

Here are a few things you can look for to ensure your test is correct:

  • 1. Are you loading the correct JSON file? Check the filename and extension. Check the contents of the file and make sure it’s what you expect. Is there JSON in the file? Is it the right JSON?

  • 2. Is the test sending the proper input into the code under test? If the input should be nil, is it? If the input shouldn’t be nil, are you sure that it’s not?

  • 3. Are your assertions correct? Should you be expecting a nil return value? If not, are you sure the output is non-nil? Check for typos - make sure any dictionary keys are spelled correctly, and make sure the values you’re expecting in your assertions match what’s in your JSON.

Once you know that your test logic is sound, you can start looking at the

code under test since the problem must be there. Remember to check the

console for error or warning messages, which may lead you to the source of

the problem. If that doesn’t help, you can always use the debugger.

To debug, you can add a Test

Test Failure

Failure Breakpoint

Breakpoint by clicking the Add

button (+) at the bottom of the Breakpoint Navigator (7) as shown in the

screenshot below. This will stop the test when a failure happens, giving you

the opportunity to inspect the values of your variables in the debugger.

To debug, you can add a Test Test Failure Failure Breakpoint Breakpoint by clicking the Add

If the Test Failure Breakpoint fails to lead you to the source of the problem,

you can also set breakpoints manually in the code under test to inspect and

debug further. Doing this allows you to step over each line of code to see

what’s executing - and what’s not - and determine where the problem is.

And on top of that, you can inspect the values of variables and properties at

each step.

Once you have your breakpoints set, run the test again, and use your

debugging skills to track down the problem.

Now that we can debug test failures, how can we add more tests to ensure

our app can handle any JSON that comes its way - without going too far

and wasting our time?

Increasing confidence with test coverage

Wouldn’t it be great if we could be confident our code is correct?

Automated testing can get us there, but how do we know what tests to

write? And how do we know when we’ve written enough to have a

reasonable level of confidence in our code?

Each test you write should add additional confidence that your code is

correct. If you’re creating new tests for the same code with the same

assertions, you’re not adding any more confidence that your code is correct.

Knowing how much of your code is tested can help you to know when

you’ve done enough testing. Test coverage reports can tell you how much of

your code is tested in your entire app or a given class or method, and it can

even show you exactly which lines were executed when the test suite ran.

This is a powerful way to visualize which code is tested, and it can help you

decide whether you’ve tested enough to be confident in your code.

Enabling and viewing test coverage reports in Xcode

1. Select Edit Scheme on your main target

With the scheme for your main target selected, click your scheme in the

top bar, next to the Stop button. Then select Edit

Edit Scheme…

Scheme…

Enabling and viewing test coverage reports in Xcode 1. Select Edit Scheme on your main target

2. Select the Test action and enable Gather coverage data

In the Edit Scheme dialog, select the Test

Test action on the left. In the Info

tab, next to Code Coverage, enable Gather

Gather coverage

coverage data

data as the

following screenshot shows.

3. Run the test with coverage enabled Now that you’ve enabled code coverage, you need to

3. Run the test with coverage enabled

Now that you’ve enabled code coverage, you need to run your tests again

since coverage reports won’t show up for previous test runs. Use Product

Product >>

Test or UU to run all the tests and generate coverage data.

Test

4. Open the Report Navigator (8), select the Test run, and select the Coverage tab

You can select the Report

Report Navigator

Navigator in the Xcode Navigator by clicking

the last button on the right or just by pressing 88.

Next, select the latest Test

Test run at the top of the Report Navigator.

Finally, choose the Coverage

Coverage tab in the editor to view the coverage report.

Finally, choose the Coverage Coverage tab in the editor to view the coverage report. 5. View

5. View the coverage reports

From here, you can see how much of each class is tested, and you can sort

by coverage to see which ones have the most or least coverage. You can also

expand the coverage report for any class to see individual methods and how

much of the code in each method was tested. In the screenshot below, you

can see what the coverage reports look like.

The last line in the report shows us that only part of the parseDictionary method was

The last line in the report shows us that only part of the parseDictionary

method was tested. If we want to see which part of the method wasn’t

executed during our tests, we can jump into the method by hovering over

the report and clicking the arrow that appears. This will take us to the

standard editor and show us which lines were executed and which ones

weren’t, as shown below. (Alternatively, you can navigate to the JSONParser

class as you normally would and see the same results.)

The last line in the report shows us that only part of the parseDictionary method was

The numbers on the right show how many times each line was executed,

and the red bar indicates the large section of code that didn’t get executed

during our tests. Here, we can see that we’ve failed to test the error case

that causes the catch block to execute.

Test coverage reports are great for showing which code has been tested, but

beware the false confidence that can come with high coverage. Just because

code was executed during the test suite doesn’t mean it’s correct. To

combat this false confidence, we need to make sure our assertions

are strong.

Avoiding false confidence with strong assertions

As we saw in the last chapter, code coverage reports can show us which

code has been tested, but it can also lead to false confidence in our code.

How can it do this? And how can we avoid it?

Coverage without assertions creates false confidence

All you need to do to increase test coverage is to call a method from a test.

But while this gives you higher coverage, it won’t actually tell you whether

your code is correct. For example, you could write a test like this that

improves your coverage:

func testThatCreatesFalseConfidence() { let bundle = NSBundle(forClass: self.dynamicType) let path = bundle.pathForResource("search-response", ofType: "json") let data = NSData(contentsOfFile: path!)

let parser = JSONParser() parser.parseDictionary(data)

}

This test builds and runs without warning or error and even shows a green

indicator that the test passed. If you were to look at your coverage report,

you’d see that parseDictionary was called, which might make you think

your code works as expected. But we can’t be sure of that with this test.

Why not? What’s missing here?

The test doesn’t have any assertions. It calls parseDictionary, but it

doesn’t actually make an assertion about that method’s output, so we can’t

be sure the method does what it’s supposed to. We need to make an

assertion if we want to have any level of confidence in our code.

Weak assertions create false confidence

Let’s modify our test to include an assertion so we can be more confident in

our code:

func testWithWeakAssertionThatCreatesFalseConfidence() { let bundle = NSBundle(forClass: self.dynamicType) let path = bundle.pathForResource("search-response", ofType: "json") let data = NSData(contentsOfFile: path!)

let parser = JSONParser() let results = parser.parseDictionary(data) XCTAssertNotNil(results)

}

We’ve just added an XCTAssertNotNil to ensure that we were able to create

a results object from the JSON data. Does this give us confidence that

our code is correct?

It’s certainly better than what we had before; any assertion is better than no

assertion. But it still doesn’t tell us much. Does the results object have the

right data in it? Were we able to map all the JSON data we need to

the object?

We can’t tell with this test. While having an assertion is better than having

none, this is still not sucient. We need to make stronger assertions if

we’re going to convince ourselves our code actually works.

Strong assertions increase confidence

Let’s make a stronger assertion about our results:

func testParseResults() { let bundle = NSBundle(forClass: self.dynamicType) let path = bundle.pathForResource("search-response", ofType: "json") let data = NSData(contentsOfFile: path!)

let parser = JSONParser() let results = parser.parseDictionary(data) XCTAssertNotNil(results) XCTAssertEqual(78457, results!["total_count"] as? Int)

}

We’ve added another assertion here at the end to verify that the

total_count was mapped properly, and that its value is 78457. This is

finally convincing; we know our code is doing what it should when this test

passes. If we care about other properties from the JSON, we could add

additional assertions for them, such as:

XCTAssertFalse(results!["incomplete_results"] as! Bool)

let items = results!["items"] as! [[String: AnyObject]] XCTAssertEqual(10, items.count)

Now we’re also verifying that incomplete_results is a Bool and is false,

and that items is an array of dictionaries and its count is 10. All of this

continues to add to our confidence that our parseDictionary method is

working as it should.

Is this enough, then? Have we tested everything we need to?

Testing the error path

If you looked at our coverage reports now, you’d see that while the happy

path is covered, we haven’t done anything to test the error path. We need to

be sure to cover that path as well in our tests.

To do this, we can just create a new JSON file that has bad data in it, and

send that into our parseDictionary method. Let’s use the following JSON:

{

Yep, that’s just an open brace. It’s completely invalid, and it should exercise

our error handling code so we can verify that it works as it should. We’ll add

this to a JSON file called search-response-invalid.json

Then we’ll create a new test that uses this invalid JSON and verifies

the result:

func testParseInvalidResults() { let bundle = NSBundle(forClass: self.dynamicType) let path = bundle.pathForResource("search-response- invalid", ofType: "json") let data = NSData(contentsOfFile: path!)

let parser = JSONParser() let results = parser.parseDictionary(data) XCTAssertNil(results)

}

This time, we’re passing in bad data and expecting results to be nil.

When we run the tests, they should pass, and we should see that every line

of code is tested when we view the coverage report. That, combined with

the fact that we have strong assertions in our tests, should give us

confidence that our JSON parser can handle anything.

Finding the point of diminishing returns

Now that we’ve tested both the happy path and the error path, we should

be confident that our code works as it should. It can be tempting to try to

write more tests here - you may be thinking, what if we don’t get any JSON in

the response? What if a value isn’t what we expect?

While adding tests for these scenarios may seem like a good idea, don’t fall

into the trap of thinking they’ll give you any extra confidence. Trust in the

code coverage report and in the strength of your assertions. Once Once you’ve you’ve executed executed
code coverage report and in the strength of your assertions. Once
Once you’ve
you’ve
executed
executed all
all your
your code
code paths
paths and
and you
you know
know your
your assertions
assertions are
are
strong,
strong, there’s
there’s not
not much
much value
value inin continuing
continuing toto write
write tests.
tests. Don’t

waste your time writing tests and assertions that aren’t necessary.

On top of wasting your time writing tests, too many tests can make it

harder to update your tests later. Say you want to refactor some code -

perhaps a key in your JSON has changed, and you need to update your code

to reflect the change. If you have a dozen tests with assertions on that

JSON key, you’ll have a dozen tests to update. If, instead, you have just a

few tests that cover all the paths through your code, you’ll be in a much

better place to quickly and easily refactor.

Reducing clutter with proper typing

When you’re casting types as you parse your JSON, be as specific as you

can to reduce clutter. This is especially important when working with

arrays, which generally contain just one type. Let’s look at how we might

parse the following JSON (again from the GitHub Search

Repositories API):

{

 

"total_count": 78457, "incomplete_results": false, "items": [ {

"id": 22458259, "name": "Alamofire", "description": "Elegant HTTP Networking in Swift"

}

]

}

For now, let’s assume we have a parseRepository method with the

following signature:

func parseRepository(json: [String: AnyObject]) -> Repository?

This should take a JSON dictionary that represents a repository and

convert it into our Repository model object.

Now, if we want to get the repository out of the items array and transform

it into our model object, we might try something like this:

if let items = json["items"] as? [AnyObject], item = items[0] as? [String: AnyObject] { parseRepository(item)

}

We start by casting our items to an array containing elements of type

AnyObject. Then, since our parseRepository method requires a dictionary

([String: AnyObject]), we’re forced to cast item - the first element of our

array - to a [String: AnyObject]. This extra line is unnecessary clutter -

it’s an extra cast that we don’t need. And it’s all because we started by

casting items to [AnyObject]. (Note that casting to NSArray causes exactly

the same issue.)

Instead, we can just cast items to the type we expect, which is more

specific than an array of objects - it’s an array of dictionaries.

if let items = json["items"] as? [[String: AnyObject]] {

parseRepository(items[0])

}

This allows us to call parseRepository without an additional cast, since

accessing any element of the items array will yield a dictionary.

But now the cast to [[String: AnyObject]] is hard to read due to the all

the square brackets. Is it an array or a dictionary? It’s hard to tell at first

glance. Can we get rid of a pair of square brackets and make it clear that

we’re working with an array of dictionaries and not just a dictionary?

Increasing clarity and readability with typealias

Even after seeing this several times, it’s still hard for me to mentally parse:

if let items = json["items"] as? [[String: AnyObject]]

It’s easy to get lost in the square brackets (and this isn’t even Objective-C!).

Are we casting to a dictionary, or an array of dictionaries? Of course, after

looking at it closely, it’s clear that it’s an array of dictionaries. But I’d love it

if I could quickly glance and know immediately that it’s an array and not just

a dictionary.

One way to accomplish this is to use typealias to create a new name for an

existing type. In our case, we can create a typealias for the dictionary

like so:

typealias JSONDictionary = [String: AnyObject]

We’re calling it JSONDictionary because that’s what a [String:

AnyObject] dictionary represents for us - a dictionary from our JSON. So

now, anywhere we see a [String: AnyObject], we can use the typealias

instead. That means our code above can become:

if let items = json["items"] as? [JSONDictionary]

And now, it’s blindingly obvious that it’s an array of dictionaries. There’s

clearly a single set of square brackets around JSONDictionary, which we

know means that it’s an array of JSONDictionary objects. No need to count

the number of square brackets surrounding it - it’s easy to see that there’s

just one set. This is much simpler and more readable.

But what if I want to use [String: AnyObject] some places and

JSONDictionary in others? Can I use them interchangeably? Yes, in fact, you can.

They both mean exactly the same thing, so you can pass a JSONDictionary

to a method that’s expecting a [String: AnyObject] and vice-versa.

You’re smart enough that you’ve probably realized by now that typealias

doesn’t need to apply only to JSON dictionaries - you could use it for arrays,

too, if you wanted. And, of course, you can use it for any type you can think

of (including functions, tuples, and more), so if any of the types you’re using

are hard to read, try creating a typealias to make your code more clear.

Deconstructing the pyramid of doom

There’s a myth out there that parsing JSON in Swift requires you to build

up a bunch of nested conditionals in what’s known as the pyramid of doom

just to handle all the optionals. In the early days of Swift, nesting

conditionals was the only option, but I hope by now you’ve realized that

today that’s not true. There are tons of outdated tutorials that use the

pyramid of doom as an example of why you need a third-party library to

parse JSON in Swift, but as you’ve already seen, you can parse JSON in

Swift without nesting and without third-party libraries. Let’s say you found

some sample code in one of those outdated tutorials that looks like this:

if let data = data { if let json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? JSONDictionary { if let name = json["name"] as? String { if let description = json["description"] as?

String {

print("name: \(name), description: \

(description)")

}

}

}

}

Notice how many layers of if statements are nested here. Code like this is

hard to understand and hard to maintain. But you can easily transform it by

binding multiple optionals in a single if statement, making it easier to read

and maintain:

if let data = data, json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? JSONDictionary, name = json["name"] as? String, description = json["description"] as? String { print("name: \(name), description: \(description)")

}

This gives us the same behavior without nesting multiple layers of

conditionals, and it’s a simple fix: just remove the extra if statements and

combine all the expressions into one.

If you prefer, you can use guard statements instead of if let. Whatever

you do, avoid using the pyramid of doom in your code. It’s unnecessary in

modern Swift and makes your code harder to read and maintain.

Now that we can write code that’s easy to read and maintain, we can take it

a step further and make it safer and easier to work with by creating

model objects.

Why model objects?

We’ve deserialized our JSON from data into a dictionary, and we can get

the values we need straight from the dictionary. Why would we care to take

it a step further and transform our dictionary into a custom model object?

Model objects give us the extra type safety Swift does so well for us.

Consider the following code we might find in a view controller:

if let repo = parser.parseRepository(data) { label.text = repo["name"] as? String }

We’re doing two things we shouldn’t have to do in a view controller:

1. We’re accessing the name by its key, “name”, and need to make sure we

spell it correctly if we want text to be displayed on the label.

2. We’re casting the name value to a String, which should be

unnecessary.

Typos and type casting are things we shouldn’t need to worry about in a

view controller. Another class can handle these things and give our view

controller a better API to work with. We can solve both of these problems

by creating a custom model object (called Repository, perhaps) that has a

name property. Then we’ll just need to have our parseRepository return a

Repository object instead of a dictionary. Look at what happens in our

view controller when we do:

if let repo = parser.parseRepository(data) { label.text = repo.name }

It’s much cleaner now - there’s no need to access the "name" key in a

dictionary. We don’t have to worry about typos any more - the compiler will

now catch those for us. On top of that, we don’t need to cast the name to a

String, since it’s already defined as one in the Repository model.

When I’m developing apps, I generally don’t even reference model objects

in my view controller - instead, I prefer to use the MVVM pattern and have

the view controller talk to the view model, which calls the JSON parser and

holds the necessary model objects. But whether you use MVC or MVVM

or another pattern, having model objects gives you the benefit of type

safety in your app.

Let’s see how we can get this added type safety and cleaner code by creating

a custom model object. But first, we’ll define this new requirement in our

unit tests by updating our parseRepository test to expect a

Repository object.

Testing with model objects

As always, we’ll test that our code does what we expect. This time, we’re

going to write the test first, to serve as the specification for what we’d like

our code to do.

Let’s make some assertions about what we’d like our new parseRepository

method to do: accept a dictionary and return a Repository. Don’t be

concerned with the fact that we haven’t written parseRepository or

created a Repository yet - we’ll get to that. On with the test:

func test_parseRepository() { let bundle = NSBundle(forClass: self.dynamicType) let path = bundle.pathForResource("search-response", ofType: "json") let data = NSData(contentsOfFile: path!)

let parser = JSONParser() let results = parser.parseDictionary(data) let items = results!["items"] as! [[String: AnyObject]] let repoDict = items[0]

let repo = parser.parseRepository(repoDict) XCTAssertEqual("Alamofire", repo!.name)

}

In the first five executable lines, we’re loading our JSON and running it

through the parseDictionary method so we can get a dictionary. Then, in

the next two lines, we’re getting the items array from the JSON and pulling

out the first object - which is a repository dictionary (repoDict). We pass

that repoDict into our not-yet-written parseRepository and assert that

repo will have a name property whose value is the name of the object in our

JSON: "Alamofire".

Next, we’ll need to actually create our Repository type, and then we’ll need

to implement the parseRepository method that will take our dictionary

and transform it into a Repository.

Creating a model object

Now that we’ve specified what we want from our parseRepository

method, we can create our model object:

struct Repository { let id: Int let name: String let description: String let homepage: String?

}

Why use a struct?, you ask. In Swift, structs are value types, which gives us

the benefit of immutability. What’s a value type?, you wonder. The Swift

Programming Language defines it this way:

A value type is a type whose value is copied when it is assigned to a

variable or constant, or when it is passed to a function.

We can be sure that when we pass a struct into another method, that

method won’t be able to change any of the values on our instance of the

struct - something we can’t be sure of with classes. Additionally, since we’ve

chosen to define all of our properties as constants, we know that once a

Repository is created, it can never change. This makes it easier to read and

maintain our code, and it makes our code less likely to suer from errors

related to mutable state. Structs are preferred over classes in many cases

due to the fact that they’re value types, and model objects are great

candidates for structs. (More on the dierences and how to choose one

over the other in Classes and Structures in The Swift

Programming Language.)

In our Repository, we’ve defined most of our properties as non-optional,

but we made the homepage optional. Why? First of all, we’re saying that a

Repository can’t be created without values for the id, name, and

description - those values are absolutely necessary if we’re going to use

our Repository. If we don’t get one of those values from the JSON, we

don’t want a Repository object. Perhaps the name and description are

required to be displayed in the UI and the id is needed to make other

network requests.

But what about the homepage - why is that optional? We’re saying that it’s OK if

we don’t have a homepage for a Repository; we can get by just fine without

one. Our app can display and use the other properties as needed, and it can

display the homepage only if we get one from the JSON. Since the GitHub

API sometimes returns null for the homepage, marking this property as

optional allows us to create and display repositories even when there’s

no homepage.

Now, how do we implement the parseRepository method to make our test

compile and pass?

Transforming dictionaries into model objects

Now that we have a test and a model object, let’s take our dictionary and

turn it into a Repository object! We’ll need the following signature in our

JSONParser to make our test happy:

func parseRepository(dict: JSONDictionary) -> Repository?

The method accepts a JSONDictionary and optionally returns the

Repository object we want. If parsing fails for any reason, our method will

just return nil. Let’s look at the full implementation:

func parseRepository(dict: JSONDictionary) -> Repository? { guard let id = dict["id"] as? Int else { return nil

} guard let name = dict["name"] as? String else { return nil

} guard let description = dict["description"] as? String else {

return nil } let homepage = dict["homepage"] as? String

return Repository(id: id, name: name, description:

description, homepage: homepage) }

Now we’re using guards? What do those mean, and why are they here?

The guard statements here allow us to be sure we have everything we need

to create a Repository. Remember that id, name, and description are

non-optional properties, so it’s essential that we get values for them. On top

of that, we need to be able to cast those values to the proper types -

otherwise, we wouldn’t be able to set the values on our Repository

properties.

On the first line, we’re using the guard statement to fetch the "id" from

the dictionary and cast it to a String. If anything fails - if "id" is nil or

can’t be cast to an Int, the else block executes, returning nil from

our method.

Otherwise, flow continues through the method, and we can use our id

property, which is guaranteed to be a non-nil Int, outside of the guard

statement when we instantiate our Repository. The same happens as we

attempt to fetch and cast the "name" and "description" properties -

either we get the values and they’re of type String, or we return nil from

the method. Note that we could combine all these guard statements into a

single comma- separated guard statement, similar to how we created

comma- separated if let statements before. But for reasons we’ll discuss

later, we’re going to leave them in separate statements.

Finally, we come to our let homepage =

missing the guard. Why?

...

statement, which is curiously

Remember that the type of our homepage property is String? - an optional

String. We don’t need a homepage value to create a Repository, so creating

it with a simple let statement allows us to either get its value as a String

or nil.

But why did we use guard at a#? Couldn’t we do a# of this with a single (if let)

statement?

You’re absolutely right. We could certainly use an if let here, returning

the Repository only if we get an id, name, and description, returning nil

otherwise. But I prefer to use guard in cases where it’s important to get

more detail about what went wrong. Speaking of which… if we get a nil

value from parseRepository, how do we know where the method failed?

Was the dictionary missing the id, name, or description? Or did one of the

type casts fail?

Producing specific error messages to clarify where parsing failed

As you’re developing your JSON parser, it’s inevitable that you’ll make

mistakes. Maybe you’ll misspell a property name, or a type in your JSON

won’t be what you expected. Sometimes it can be hard to track down these

errors, but specific error messages can help. To create a good error message,

just state clearly and specifically what went wrong.

Let’s look at our parseRepository method again:

func parseRepository(dict: JSONDictionary) -> Repository? { guard let id = dict["id"] as? Int else { return nil

} guard let name = dict["name"] as? String else { return nil

} guard let description = dict["description"] as? String else {

return nil } let homepage = dict["homepage"] as? String

return Repository(id: id, name: name, description:

description, homepage: homepage) }

At this point, whenever we get a nil from this method, there’s nothing to

tell us why. What went wrong? Was the id actually a String instead of an

Int? Was the dictionary missing the name key? Did we get a null value for

the description in our JSON? Nothing here can indicate to us what caused

our method to return nil. Let’s start by fixing that in the id statement:

guard let id = dict["id"] as? Int else { print("Error: couldn't parse id property") return nil

}

Now if we can’t transform the id to an Int, we’ll know exactly what failed

and where to look. We can check our JSON to make sure that there’s an

"id" and that it’s an integer. And then we can check our code here to make

sure we didn’t make a typo in the "id" key in the dictionary.

The good news is that after we get past this statement, we can be even

more specific when we parse the name:

guard let name = dict["name"] as? String else { print("Error: couldn't parse name property for id: \(id)") return nil

}

Now if there’s a problem with the name, we’ll know it right away - and we’ll

even know which name was the problem since we’re printing the

corresponding id in our output. If this method is called multiple times with

dierent JSON dictionaries, this can help us to narrow down the problem

to the exact JSON repository that’s broken. Or, if it’s happening on a# of

our JSON repositories, the repeated error messages will indicate that the

problem is with a# of them - or with this guard statement in our parser.

Finally, we can make it even easier on ourselves with the

description statement:

guard let description = dict["description"] as? String else { print("Error: couldn't parse description property for id:

\(id), name: \(name)") return nil

}

Now if there’s a problem parsing the description, we’ll see both the id and

name printed in the console so like so:

Error: couldn't parse description property for id: 22458259, name: Alamofire

This should make it even easier to find the root cause of the issue. This

kind of granularity is impossible to get with a single if let statement.

Depending on what you’re doing, that may be OK - or you may want the

extra granularity you get with guard statements.

Handling arrays and nil

{

"total_count": 76266, "incomplete_results": false, "items": [ {

"id": 22458259, "name": "Alamofire",

// ...

}

How can we grab all the items from this response and return an array of

Repository objects?

Our method signature should look like this:

func parseSearchResponse(json: JSONDictionary) -> [Repository]?

We’ll pass in the entire JSON response as a dictionary, then optionally

return an array of Repository objects. If something is wrong with the

JSON as a whole, we’ll just return nil from this method. As we iterate

through the items array, whenever we can create a Repository object, we’ll

include that in the array of Repository objects we’re returning from the

method. If we’re not able to create a Repository object, we’ll still return

the array, but it won’t contain the data for that repository.

An initial attempt might look something like this:

func parseSearchResponse(json: JSONDictionary) -> [Repository]? { guard let items = json["items"] as? [JSONDictionary] else

{

 

print("Error: couldn't find items in JSON") return nil

}

var repos = [Repository]() for item in items { if let repo = parseRepository(item) { repos.append(repo)

}

}

return repos

}

First, we’re pulling the "items" out of the JSON and trying to cast it to an

array of JSON dictionaries. If this fails, we’ll print an error and return nil;

otherwise, we’ll continue.

Assuming we make it past the guard, our code creates an empty array called

repos that will contain our Repository objects. We can then loop over

each item in our items array, trying to create a Repository out of it with

our parseRepo method. If that succeeds, we’ll add the object to our array. If

it fails and returns nil, we’ll leave it out since we don’t want nil values in

our array.

This is a great first attempt; it gets the job done and works well. But we can

do this in fewer lines and more idiomatically. Since we’re creating an array

from an array, flatMap would be a great way to do this. We want to take our

array of JSON dictionaries (items) and transform it into an array of

Repository objects (repos). The flatMap method will take each object in

an array and transform it into something else, returning a new array

of things.

The flatMap method on Array takes a closure that transforms each

element in the array. It then iterates over each element, applying the

transformation, and returns a new array containing the transformed

elements that are non-nil.

Since this is exactly what we’re doing, we can replace our for loop with

flatMap and get the same results.

func parseSearchResponse(json: JSONDictionary) -> [Repository]? { guard let items = json["items"] as? [JSONDictionary] else

{

 

print("Error: couldn't find items in JSON") return nil

}

let repos = items.flatMap { item in return parseRepository(item) }

return repos

}

So now, we’re asking flatMap to take each item from the items array, call

parseRepo with it, and return that result from the closure. Each of these

results is then added to the new repos array, just like what we had before.

And on top of that, if our parseRepo method returns nil, flatMap will

automatically remove the nil for us. In the array that’s returned from

flatMap, all the values in the array are guaranteed to be non-nil. This is the

advantage of using flatMap over map in this case - map does transforms an

array, but it doesn’t remove nil values. With flatMap, we can be sure that

we’ll end up with an array of Repository objects without any nil

values inside.

But we can take this a step further and remove some of the unnecessary

decoration. The return statement isn’t needed since our closure is only one

line, and the item constant and the in keyword are also unnecessary. We

can compress all of that down to this:

let repos = items.flatMap { parseRepository($0) }

In this version, $0 is our item from earlier - it represents a single value from

the items array. And its type is JSONDictionary, since items is an array of

JSONDictionary objects. We pass $0, our JSONDictionary, into parseRepo,

and the result is returned in the flatMap closure. And then flatMap takes

the result of each parseRepo, removes any nil values, and returns the

resulting array which is then set to repos. This gives us [Repository] as

our type for repos, which is exactly what we need to return from

parseSearchResponse. So now that we’ve compressed all this code into a

single line, creating the repos constant is no longer necessary. Here’s the

full method:

func parseSearchResponse(json: JSONDictionary) -> [Repository]? { guard let items = json["items"] as? [JSONDictionary] else

{

 

print("Error: couldn't find items in JSON") return nil

} return items.flatMap { parseRepository($0) }

}

It’s simple, elegant, and beautiful.

Conclusion

Thank you for spending the time and eort reading this book. I hope the

investment you’ve made in learning how to parse JSON in Swift pays o

over and over again. I hope you’ve learned a ton about turning JSON data

into Swift types, transforming those into model objects, testing everything

to be sure your code is correct, and how to make your code concise,

readable, and elegant so you can continue to enjoy working with it. Go and

build something awesome!

I’d love to hear from you for any reason, so feel free to email me

(josh@roadfiresoftware.com) any time with questions, comments, or

feedback on anything in this book. Any feedback you send will go a long

way towards making this book better for the next reader (and they’ll

appreciate your eort). I’m especially excited to see an app you’ve built

using any of the principles in this book, so when you have one, send me a

link so I can check it out!

Thanks so much for reading, and have a great day!

Acknowledgements

I couldn’t have written this book myself. I appreciate all the help I had

from my amazing team of reviewers: Alex Robinson, Tim Ritchey, Chris

Patterson, Kevin Munc, and Christina Moulton. They made huge

contributions by sharing their thoughts, questions, and suggestions as they

read early versions, and they’re the reason the book is what it is today.

Thank you for making this book so much better than it was when you

saw it.

Amy Hoy and Alex Hillman have taught me a ton about building products

through 30×500 and The Forge. This book never could have happened

without their teaching, guidance, and support. Thank you for all you’ve

taught me and for making this book possible.

I’ve learned a ton about programming in Swift from awesome people (in no

particular order) like Chris Eidhof, Rob Napier, David Owens II, Andy

Matuschak, Justin Spahr-Summers, Natasha Murashev, Daniel Steinberg,

Florian Kugler, Wouter Swierstra, Soroush Khanlou, Drew Crawford, Erica

Sadun, Radek Pietruszewski, and probably a bunch of others I can’t recall

othe top of my head. Thank you for speaking and writing about Swift and

for teaching me so much.

And thanks to you, dear reader, for choosing to spend your time reading

this book.