21
Web Services and UIWebView

For the next two chapters, you are going to take another break from Homepwner to work with web services and split view controllers.

In this chapter, you will lay the foundation for an application named Nerdfeed that reads in a list of the courses that Big Nerd Ranch offers. Each course will be listed in a table view, and selecting a course will open that course’s web page. Figure 21.1 shows Nerdfeed at the end of this chapter.

Figure 21.1  Nerdfeed

Nerdfeed

The work is divided into two parts. The first is connecting to and collecting data from a web service and using that data to create model objects. The second part is using the UIWebView class to display web content. Figure 21.2 shows an object diagram for Nerdfeed.

Figure 21.2  Nerdfeed object diagram

Nerdfeed object diagram

Web Services

Your web browser uses the HTTP protocol to communicate with a web server. In the simplest interaction, the browser sends a request to the server specifying a URL. The server responds by sending back the requested page (typically HTML and images), which the browser formats and displays.

In more complex interactions, browser requests include other parameters, like form data. The server processes these parameters and returns a customized, or dynamic, web page.

Web browsers are widely used and have been around for a long time. So the technologies surrounding HTTP are stable and well-developed: HTTP traffic passes neatly through most firewalls, web servers are very secure and have great performance, and web application development tools have become easy to use.

You can write a client application for iOS that leverages the HTTP infrastructure to talk to a web-enabled server. The server side of this application is a web service. Your client application and the web service can exchange requests and responses via HTTP.

Because the HTTP protocol does not care what data it transports, these exchanges can contain complex data. This data is typically in JSON (JavaScript Object Notation) or XML format. If you control the web server as well as the client, you can use any format you like; if not, you have to build your application to use whatever the server supports.

In this chapter, you will create a client application that will make a request to the courses web service hosted at http://bookapi.bignerdranch.com. The data that is returned will be JSON that describes the courses.

Starting the Nerdfeed application

Create a new Empty Application for the iPad Device Family. Name this application Nerdfeed, as shown in Figure 21.3. (If you do not have an iPad to deploy to, use the iPad simulator.)

Figure 21.3  Creating an iPad Empty Application

Creating an iPad Empty Application

Let’s knock out the basic UI before focusing on web services. Create a new NSObject subclass and name it BNRCoursesViewController. In BNRCoursesViewController.h, change the superclass to UITableViewController.

@​i​n​t​e​r​f​a​c​e​ ​B​N​R​C​o​u​r​s​e​s​V​i​e​w​C​o​n​t​r​o​l​l​e​r​ ​:​ ​N​S​O​b​j​e​c​t​
@​i​n​t​e​r​f​a​c​e​ ​B​N​R​C​o​u​r​s​e​s​V​i​e​w​C​o​n​t​r​o​l​l​e​r​ ​:​ ​U​I​T​a​b​l​e​V​i​e​w​C​o​n​t​r​o​l​l​e​r​

In BNRCoursesViewController.m, write stubs for the required data source methods so that you can build and run as you go through this exercise.

-​ ​(​N​S​I​n​t​e​g​e​r​)​t​a​b​l​e​V​i​e​w​:​(​U​I​T​a​b​l​e​V​i​e​w​ ​*​)​t​a​b​l​e​V​i​e​w​
 ​n​u​m​b​e​r​O​f​R​o​w​s​I​n​S​e​c​t​i​o​n​:​(​N​S​I​n​t​e​g​e​r​)​s​e​c​t​i​o​n​
{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​
}​

-​ ​(​U​I​T​a​b​l​e​V​i​e​w​C​e​l​l​ ​*​)​t​a​b​l​e​V​i​e​w​:​(​U​I​T​a​b​l​e​V​i​e​w​ ​*​)​t​a​b​l​e​V​i​e​w​
 ​ ​ ​ ​ ​ ​ ​ ​ ​c​e​l​l​F​o​r​R​o​w​A​t​I​n​d​e​x​P​a​t​h​:​(​N​S​I​n​d​e​x​P​a​t​h​ ​*​)​i​n​d​e​x​P​a​t​h​
{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​n​i​l​;​
}​

In BNRAppDelegate.m, create an instance of BNRCoursesViewController and set it as the root view controller of a navigation controller. Make that navigation controller the root view controller of the window.

#​i​m​p​o​r​t​ ​"​B​N​R​A​p​p​D​e​l​e​g​a​t​e​.​h​"​
#​i​m​p​o​r​t​ ​"​B​N​R​C​o​u​r​s​e​s​V​i​e​w​C​o​n​t​r​o​l​l​e​r​.​h​"​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​B​N​R​A​p​p​D​e​l​e​g​a​t​e​

-​ ​(​B​O​O​L​)​a​p​p​l​i​c​a​t​i​o​n​:​(​U​I​A​p​p​l​i​c​a​t​i​o​n​ ​*​)​a​p​p​l​i​c​a​t​i​o​n​
 ​ ​ ​ ​d​i​d​F​i​n​i​s​h​L​a​u​n​c​h​i​n​g​W​i​t​h​O​p​t​i​o​n​s​:​(​N​S​D​i​c​t​i​o​n​a​r​y​ ​*​)​l​a​u​n​c​h​O​p​t​i​o​n​s​
{​
 ​ ​ ​ ​s​e​l​f​.​w​i​n​d​o​w​ ​=​ ​[​[​U​I​W​i​n​d​o​w​ ​a​l​l​o​c​]​ ​i​n​i​t​W​i​t​h​F​r​a​m​e​:​[​[​U​I​S​c​r​e​e​n​ ​m​a​i​n​S​c​r​e​e​n​]​ ​b​o​u​n​d​s​]​]​;​


 ​ ​ ​ ​B​N​R​C​o​u​r​s​e​s​V​i​e​w​C​o​n​t​r​o​l​l​e​r​ ​*​c​v​c​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​[​[​B​N​R​C​o​u​r​s​e​s​V​i​e​w​C​o​n​t​r​o​l​l​e​r​ ​a​l​l​o​c​]​ ​i​n​i​t​W​i​t​h​S​t​y​l​e​:​U​I​T​a​b​l​e​V​i​e​w​S​t​y​l​e​P​l​a​i​n​]​;​

 ​ ​ ​ ​U​I​N​a​v​i​g​a​t​i​o​n​C​o​n​t​r​o​l​l​e​r​ ​*​m​a​s​t​e​r​N​a​v​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​[​[​U​I​N​a​v​i​g​a​t​i​o​n​C​o​n​t​r​o​l​l​e​r​ ​a​l​l​o​c​]​ ​i​n​i​t​W​i​t​h​R​o​o​t​V​i​e​w​C​o​n​t​r​o​l​l​e​r​:​c​v​c​]​;​

 ​ ​ ​ ​s​e​l​f​.​w​i​n​d​o​w​.​r​o​o​t​V​i​e​w​C​o​n​t​r​o​l​l​e​r​ ​=​ ​m​a​s​t​e​r​N​a​v​;​

 ​ ​ ​ ​s​e​l​f​.​w​i​n​d​o​w​.​b​a​c​k​g​r​o​u​n​d​C​o​l​o​r​ ​=​ ​[​U​I​C​o​l​o​r​ ​w​h​i​t​e​C​o​l​o​r​]​;​
 ​ ​ ​ ​[​s​e​l​f​.​w​i​n​d​o​w​ ​m​a​k​e​K​e​y​A​n​d​V​i​s​i​b​l​e​]​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​Y​E​S​;​
}​

Build and run the application. You should see an empty UITableView and a navigation bar.

NSURL, NSURLRequest, NSURLSession, and NSURLSessionTask

The Nerdfeed application will fetch data from a web server using four handy classes: NSURL, NSURLRequest, NSURLSessionTask, and NSURLSession (Figure 21.4).

Figure 21.4  Relationship of web service classes

Relationship of web service classes

Each of these classes has an important role in communicating with a web server:

  • An NSURL instance contains the location of a web application in URL format. For many web services, the URL will be composed of the base address, the web application you are communicating with, and any arguments that are being passed.

  • An NSURLRequest instance holds all the data necessary to communicate with a web server. This includes an NSURL object as well as a caching policy, a limit on how long you will give the web server to respond, and additional data passed through the HTTP protocol. (NSMutableURLRequest is the mutable subclass of NSURLRequest.)

  • An NSURLSessionTask instance encapsulates the lifetime of a single request. It tracks the state of the request and has methods to cancel, suspend, and resume the request. Tasks will always be subclasses of NSURLSessionTask, specifically NSURLSessionDataTask, NSURLSessionUploadTask, or NSURLSessionDownloadTask.

  • An NSURLSession instance acts as a factory for data transfer tasks. It is a configurable container that can set common properties on the tasks it creates. Some examples include header fields that all requests should have or whether requests work over cellular connections. NSURLSession also has a rich delegate model that can provide information on the status of a given task and handle authentication challenges, for example.

Formatting URLs and requests

The form of a web service request varies depending on who implements the web service; there are no set-in-stone rules when it comes to web services. You will need to find the documentation for the web service to know how to format a request. As long as a client application sends the server what it wants, you have a working exchange.

Big Nerd Ranch’s courses web service wants a URL that looks like this:

h​t​t​p​:​/​/​b​o​o​k​a​p​i​.​b​i​g​n​e​r​d​r​a​n​c​h​.​c​o​m​/​c​o​u​r​s​e​s​.​j​s​o​n​

You can see that the base URL is bookapi.bignerdranch.com and the web application is located at courses on that server’s filesystem, followed by the data format (json) you expect the response in.

Web service requests come in all sorts of formats, depending on what the creator of that web service is trying to accomplish. The courses web service, where each piece of information the web server needs to honor a request is broken up into arguments supplied as path components, is somewhat common. This particular web service call says, Return all of the courses in a json format.

Another common web service format looks like this:

h​t​t​p​:​/​/​b​a​s​e​U​R​L​.​c​o​m​/​s​e​r​v​i​c​e​N​a​m​e​?​a​r​g​u​m​e​n​t​X​=​v​a​l​u​e​X​&​a​r​g​u​m​e​n​t​Y​=​v​a​l​u​e​Y​

So, for example, you might imagine that you could specify the month and year for which you wanted a list of the courses having a URL like this:

h​t​t​p​:​/​/​b​o​o​k​a​p​i​.​b​i​g​n​e​r​d​r​a​n​c​h​.​c​o​m​/​c​o​u​r​s​e​s​.​j​s​o​n​?​y​e​a​r​=​2​0​1​4​&​m​o​n​t​h​=​1​1​

At times, you will need to make a string URL-safe. For example, space characters and quotes are not allowed in URLs; They must be replaced with escape-sequences. Here is how that is done.

N​S​S​t​r​i​n​g​ ​*​s​e​a​r​c​h​ ​=​ ​@​"​P​l​a​y​ ​s​o​m​e​ ​​"​A​b​b​a​​"​"​;​
N​S​S​t​r​i​n​g​ ​*​e​s​c​a​p​e​d​ ​=​
 ​ ​ ​ ​ ​ ​[​s​e​a​r​c​h​ ​s​t​r​i​n​g​B​y​A​d​d​i​n​g​P​e​r​c​e​n​t​E​s​c​a​p​e​s​U​s​i​n​g​E​n​c​o​d​i​n​g​:​N​S​U​T​F​8​S​t​r​i​n​g​E​n​c​o​d​i​n​g​]​;​

/​/​ ​e​s​c​a​p​e​d​ ​i​s​ ​n​o​w​ ​"​P​l​a​y​%​2​0​s​o​m​e​%​2​0​%​2​2​A​b​b​a​%​2​2​"​

If you need to un-escape a percent-escaped string, NSString has the method:

-​ ​(​N​S​S​t​r​i​n​g​ ​*​)​s​t​r​i​n​g​B​y​R​e​m​o​v​i​n​g​P​e​r​c​e​n​t​E​n​c​o​d​i​n​g​;​

When the request to the Big Nerd Ranch courses feed is processed, the server will return JSON data that contains the list of courses. The BNRCoursesViewController, which made the request, will then populate its table view with the titles of these courses.

Working with NSURLSession

NSURLSession refers both to a specific class as well as a name for a collection of classes that form an API for network requests. To differentiate the two definitions, the book will refer to the class as just NSURLSession, or an instance thereof, and the API as the NSURLSession API.

In BNRCoursesViewController.m add a property to the class extension to hold onto an instance of NSURLSession.

@​i​n​t​e​r​f​a​c​e​ ​B​N​R​C​o​u​r​s​e​s​V​i​e​w​C​o​n​t​r​o​l​l​e​r​ ​(​)​

@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​)​ ​N​S​U​R​L​S​e​s​s​i​o​n​ ​*​s​e​s​s​i​o​n​;​

@​e​n​d​

Then override initWithStyle: to create the NSURLSession object.

-​ ​(​i​n​s​t​a​n​c​e​t​y​p​e​)​i​n​i​t​W​i​t​h​S​t​y​l​e​:​(​U​I​T​a​b​l​e​V​i​e​w​S​t​y​l​e​)​s​t​y​l​e​
{​
 ​ ​ ​ ​s​e​l​f​ ​=​ ​[​s​u​p​e​r​ ​i​n​i​t​W​i​t​h​S​t​y​l​e​:​s​t​y​l​e​]​;​
 ​ ​ ​ ​i​f​ ​(​s​e​l​f​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​s​e​l​f​.​n​a​v​i​g​a​t​i​o​n​I​t​e​m​.​t​i​t​l​e​ ​=​ ​@​"​B​N​R​ ​C​o​u​r​s​e​s​"​;​

 ​ ​ ​ ​ ​ ​ ​ ​N​S​U​R​L​S​e​s​s​i​o​n​C​o​n​f​i​g​u​r​a​t​i​o​n​ ​*​c​o​n​f​i​g​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​N​S​U​R​L​S​e​s​s​i​o​n​C​o​n​f​i​g​u​r​a​t​i​o​n​ ​d​e​f​a​u​l​t​S​e​s​s​i​o​n​C​o​n​f​i​g​u​r​a​t​i​o​n​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​_​s​e​s​s​i​o​n​ ​=​ ​[​N​S​U​R​L​S​e​s​s​i​o​n​ ​s​e​s​s​i​o​n​W​i​t​h​C​o​n​f​i​g​u​r​a​t​i​o​n​:​c​o​n​f​i​g​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​d​e​l​e​g​a​t​e​:​n​i​l​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​d​e​l​e​g​a​t​e​Q​u​e​u​e​:​n​i​l​]​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​t​u​r​n​ ​s​e​l​f​;​
}​

The NSURLSession is created with a configuration, a delegate, and a delegate queue. The defaults for these arguments are what you want for this application. You get a default configuration and pass that in for the first argument. For the second and third arguments, you simply pass in nil to get the defaults.

In BNRCoursesViewController.m, implement the fetchFeed method to create an NSURLRequest that connects to bookapi.bignerdranch.com and asks for the list of courses. Then, use the NSURLSession to create an NSURLSessionDataTask that transfers this request to the server.

-​ ​(​v​o​i​d​)​f​e​t​c​h​F​e​e​d​
{​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​r​e​q​u​e​s​t​S​t​r​i​n​g​ ​=​ ​@​"​h​t​t​p​:​/​/​b​o​o​k​a​p​i​.​b​i​g​n​e​r​d​r​a​n​c​h​.​c​o​m​/​c​o​u​r​s​e​s​.​j​s​o​n​"​;​
 ​ ​ ​ ​N​S​U​R​L​ ​*​u​r​l​ ​=​ ​[​N​S​U​R​L​ ​U​R​L​W​i​t​h​S​t​r​i​n​g​:​r​e​q​u​e​s​t​S​t​r​i​n​g​]​;​
 ​ ​ ​ ​N​S​U​R​L​R​e​q​u​e​s​t​ ​*​r​e​q​ ​=​ ​[​N​S​U​R​L​R​e​q​u​e​s​t​ ​r​e​q​u​e​s​t​W​i​t​h​U​R​L​:​u​r​l​]​;​

 ​ ​ ​ ​N​S​U​R​L​S​e​s​s​i​o​n​D​a​t​a​T​a​s​k​ ​*​d​a​t​a​T​a​s​k​ ​=​
 ​ ​ ​ ​ ​ ​[​s​e​l​f​.​s​e​s​s​i​o​n​ ​d​a​t​a​T​a​s​k​W​i​t​h​R​e​q​u​e​s​t​:​r​e​q​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​m​p​l​e​t​i​o​n​H​a​n​d​l​e​r​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​^​(​N​S​D​a​t​a​ ​*​d​a​t​a​,​ ​N​S​U​R​L​R​e​s​p​o​n​s​e​ ​*​r​e​s​p​o​n​s​e​,​ ​N​S​E​r​r​o​r​ ​*​e​r​r​o​r​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​j​s​o​n​ ​=​ ​[​[​N​S​S​t​r​i​n​g​ ​a​l​l​o​c​]​ ​i​n​i​t​W​i​t​h​D​a​t​a​:​d​a​t​a​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​n​c​o​d​i​n​g​:​N​S​U​T​F​8​S​t​r​i​n​g​E​n​c​o​d​i​n​g​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​(​@​"​%​@​"​,​ ​j​s​o​n​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​]​;​
 ​ ​ ​[​d​a​t​a​T​a​s​k​ ​r​e​s​u​m​e​]​;​
}​

Creating the NSURLRequest is fairly straightforward: create an NSURL instance and instantiate a request object with it.

The purpose of the NSURLSession is a bit hazier. NSURLSession’s job is to create tasks of a similar nature. For example, if your application had a set of requests that all required the same header fields, you could configure the NSURLSession with these additional header fields. Similarly, if a set of requests should not connect over cellular networks, then an NSURLSession could be configured to not allow cellular access. These shared behaviors and attributes are then configured on the tasks that the session creates.

A project may have multiple instances of NSURLSession, but since Nerdfeed only initiates a single, simple request, it will use the sharedSession. The sharedSession is set up with a default configuration.

The session object can now be used to create tasks. By giving the session a request and a completion block to call when the request finishes, it will return an instance of NSURLSessionTask. Since Nerdfeed is requesting data from a web service, the type of task will be an NSURLSessionDataTask. Tasks are always created in the suspended state, so calling resume on the task will start the web service request. For now, the completion block will just print out the JSON data returned from the request.

Kick off the exchange whenever the BNRCoursesViewController is created. In BNRCoursesViewController.m, update initWithStyle:.

-​ ​(​i​n​s​t​a​n​c​e​t​y​p​e​)​i​n​i​t​W​i​t​h​S​t​y​l​e​:​(​U​I​T​a​b​l​e​V​i​e​w​S​t​y​l​e​)​s​t​y​l​e​
{​
 ​ ​ ​ ​s​e​l​f​ ​=​ ​[​s​u​p​e​r​ ​i​n​i​t​W​i​t​h​S​t​y​l​e​:​s​t​y​l​e​]​;​
 ​ ​ ​ ​i​f​ ​(​s​e​l​f​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​s​e​l​f​.​n​a​v​i​g​a​t​i​o​n​I​t​e​m​.​t​i​t​l​e​ ​=​ ​@​"​B​N​R​ ​C​o​u​r​s​e​s​"​;​

 ​ ​ ​ ​ ​ ​ ​ ​N​S​U​R​L​S​e​s​s​i​o​n​C​o​n​f​i​g​u​r​a​t​i​o​n​ ​*​c​o​n​f​i​g​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​N​S​U​R​L​S​e​s​s​i​o​n​C​o​n​f​i​g​u​r​a​t​i​o​n​ ​d​e​f​a​u​l​t​S​e​s​s​i​o​n​C​o​n​f​i​g​u​r​a​t​i​o​n​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​_​s​e​s​s​i​o​n​ ​=​ ​[​N​S​U​R​L​S​e​s​s​i​o​n​ ​s​e​s​s​i​o​n​W​i​t​h​C​o​n​f​i​g​u​r​a​t​i​o​n​:​c​o​n​f​i​g​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​d​e​l​e​g​a​t​e​:​n​i​l​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​d​e​l​e​g​a​t​e​Q​u​e​u​e​:​n​i​l​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​ ​f​e​t​c​h​F​e​e​d​]​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​t​u​r​n​ ​s​e​l​f​;​
}​

Build and run the application. A string representation of the JSON data coming back from the web service will print to the console. (If you do not see anything print to the console, make sure you typed the URL correctly.)

JSON data

JSON data, especially when it is condensed like it is in your console, may seem daunting. However, it is actually a very simple syntax. JSON can contain the most basic objects we use to represent model objects: arrays, dictionaries, strings, and numbers. A dictionary contains one or more key-value pairs, where the key is a string, and the value can be another dictionary, string, number, or array. An array can consist of strings, numbers, dictionaries, and other arrays. Thus, a JSON document is a nested set of these types of values.

Here is an example of some really simple JSON:

{​
 ​ ​ ​ ​"​n​a​m​e​"​ ​:​ ​"​C​h​r​i​s​t​i​a​n​"​,​
 ​ ​ ​ ​"​f​r​i​e​n​d​s​"​ ​:​ ​[​"​A​a​r​o​n​"​,​ ​"​M​i​k​e​y​"​]​,​
 ​ ​ ​ ​"​j​o​b​"​ ​:​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​"​c​o​m​p​a​n​y​"​ ​:​ ​"​B​i​g​ ​N​e​r​d​ ​R​a​n​c​h​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​"​t​i​t​l​e​"​ ​:​ ​"​S​e​n​i​o​r​ ​N​e​r​d​"​
 ​ ​ ​ ​}​
}​

A JSON dictionary is delimited with curly braces ({ and }). Within curly braces are the key-value pairs that belong to that dictionary. The beginning of this JSON document is an open curly brace, which means the top-level object of this document is a JSON dictionary. This dictionary contains three key-value pairs (name, friends, and job).

A string is represented by using text within quotations. Strings are used as the keys within a dictionary and can be used as values, too. Thus, the value of the name key in the top-level dictionary is the string Christian.

Arrays are represented with square brackets ([ and ]). An array can contain any other JSON information. In this case, the friends key holds an array of strings (Aaron and Mikey).

A dictionary can contain other dictionaries; the final key in the top-level dictionary, job, is associated with a dictionary that has two key-value pairs (company and title).

Nerdfeed will parse out the useful information from the JSON data and store this in its courses property.

Figure 21.5  Model object graph

Model object graph

Parsing JSON data

Apple has a built-in class for parsing JSON data, NSJSONSerialization. You can hand this class a bunch of JSON data and it will create instances of NSDictionary for every JSON object, NSArray for every JSON array, NSString for every JSON string, and NSNumber for every JSON number.

In BNRCoursesViewController.m, modify the NSURLSessionDataTask completion handler to use the NSJSONSerialization class to convert the raw JSON data into the basic foundation objects.

^​(​N​S​D​a​t​a​ ​*​d​a​t​a​,​ ​N​S​U​R​L​R​e​s​p​o​n​s​e​ ​*​r​e​s​p​o​n​s​e​,​ ​N​S​E​r​r​o​r​ ​*​e​r​r​o​r​)​ ​{​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​j​s​o​n​ ​=​ ​[​[​N​S​S​t​r​i​n​g​ ​a​l​l​o​c​]​ ​i​n​i​t​W​i​t​h​D​a​t​a​:​d​a​t​a​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​n​c​o​d​i​n​g​:​N​S​U​T​F​8​S​t​r​i​n​g​E​n​c​o​d​i​n​g​]​;​
 ​ ​ ​ ​N​S​L​o​g​(​@​"​%​@​"​,​ ​j​s​o​n​)​;​
 ​ ​ ​ ​
 ​ ​ ​ ​N​S​D​i​c​t​i​o​n​a​r​y​ ​*​j​s​o​n​O​b​j​e​c​t​ ​=​ ​[​N​S​J​S​O​N​S​e​r​i​a​l​i​z​a​t​i​o​n​ ​J​S​O​N​O​b​j​e​c​t​W​i​t​h​D​a​t​a​:​d​a​t​a​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​o​p​t​i​o​n​s​:​0​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​o​r​:​n​i​l​]​;​
 ​ ​ ​ ​N​S​L​o​g​(​@​"​%​@​"​,​ ​j​s​o​n​O​b​j​e​c​t​)​;​
}​

Build and run, then check the console. You will see the JSON data, except now it will be formatted slightly differently because NSLog does a good job formatting dictionaries and arrays. The jsonObject is an instance of NSDictionary and it has an NSString key with an associated value of type NSArray.

When the NSURLSessionDataTask finishes, you will use NSJSONSerialization to convert the JSON data into an NSDictionary. Figure 21.6 shows how the data will be structured.

Figure 21.6  JSON objects

JSON objects

In BNRCoursesViewController.m, add a new property to the class extension to hang on to that array, which is an array of NSDictionary objects that describe each course.

@​i​n​t​e​r​f​a​c​e​ ​B​N​R​C​o​u​r​s​e​s​V​i​e​w​C​o​n​t​r​o​l​l​e​r​ ​(​)​

@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​)​ ​N​S​U​R​L​S​e​s​s​i​o​n​ ​*​s​e​s​s​i​o​n​;​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​c​o​p​y​)​ ​N​S​A​r​r​a​y​ ​*​c​o​u​r​s​e​s​;​

@​e​n​d​

Then, in the same file, change the implementation of the NSURLSessionDataTask completion handler:

^​(​N​S​D​a​t​a​ ​*​d​a​t​a​,​ ​N​S​U​R​L​R​e​s​p​o​n​s​e​ ​*​r​e​s​p​o​n​s​e​,​ ​N​S​E​r​r​o​r​ ​*​e​r​r​o​r​)​ ​{​
 ​ ​ ​ ​N​S​D​i​c​t​i​o​n​a​r​y​ ​*​j​s​o​n​O​b​j​e​c​t​ ​=​ ​[​N​S​J​S​O​N​S​e​r​i​a​l​i​z​a​t​i​o​n​ ​J​S​O​N​O​b​j​e​c​t​W​i​t​h​D​a​t​a​:​d​a​t​a​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​o​p​t​i​o​n​s​:​0​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​o​r​:​n​i​l​]​;​
 ​ ​ ​ ​N​S​L​o​g​(​@​"​%​@​"​,​ ​j​s​o​n​O​b​j​e​c​t​)​;​
 ​ ​ ​ ​s​e​l​f​.​c​o​u​r​s​e​s​ ​=​ ​j​s​o​n​O​b​j​e​c​t​[​@​"​c​o​u​r​s​e​s​"​]​;​

 ​ ​ ​ ​N​S​L​o​g​(​@​"​%​@​"​,​ ​s​e​l​f​.​c​o​u​r​s​e​s​)​;​
}​

Now, still in BNRCoursesViewController.m, update the data source methods so that each of the course titles are shown in the table. You will also want to override viewDidLoad to register the table view cell class.

-​ ​(​v​o​i​d​)​v​i​e​w​D​i​d​L​o​a​d​
{​
 ​ ​ ​ ​[​s​u​p​e​r​ ​v​i​e​w​D​i​d​L​o​a​d​]​;​

 ​ ​ ​ ​[​s​e​l​f​.​t​a​b​l​e​V​i​e​w​ ​r​e​g​i​s​t​e​r​C​l​a​s​s​:​[​U​I​T​a​b​l​e​V​i​e​w​C​e​l​l​ ​c​l​a​s​s​]​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​o​r​C​e​l​l​R​e​u​s​e​I​d​e​n​t​i​f​i​e​r​:​@​"​U​I​T​a​b​l​e​V​i​e​w​C​e​l​l​"​]​;​
}​

-​ ​(​N​S​I​n​t​e​g​e​r​)​t​a​b​l​e​V​i​e​w​:​(​U​I​T​a​b​l​e​V​i​e​w​ ​*​)​t​a​b​l​e​V​i​e​w​
 ​n​u​m​b​e​r​O​f​R​o​w​s​I​n​S​e​c​t​i​o​n​:​(​N​S​I​n​t​e​g​e​r​)​s​e​c​t​i​o​n​
{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​[​s​e​l​f​.​c​o​u​r​s​e​s​ ​c​o​u​n​t​]​;​
}​

-​ ​(​U​I​T​a​b​l​e​V​i​e​w​C​e​l​l​ ​*​)​t​a​b​l​e​V​i​e​w​:​(​U​I​T​a​b​l​e​V​i​e​w​ ​*​)​t​a​b​l​e​V​i​e​w​
 ​ ​ ​ ​ ​ ​ ​ ​ ​c​e​l​l​F​o​r​R​o​w​A​t​I​n​d​e​x​P​a​t​h​:​(​N​S​I​n​d​e​x​P​a​t​h​ ​*​)​i​n​d​e​x​P​a​t​h​
{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​n​i​l​;​
 ​ ​ ​ ​U​I​T​a​b​l​e​V​i​e​w​C​e​l​l​ ​*​c​e​l​l​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​t​a​b​l​e​V​i​e​w​ ​d​e​q​u​e​u​e​R​e​u​s​a​b​l​e​C​e​l​l​W​i​t​h​I​d​e​n​t​i​f​i​e​r​:​@​"​U​I​T​a​b​l​e​V​i​e​w​C​e​l​l​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​o​r​I​n​d​e​x​P​a​t​h​:​i​n​d​e​x​P​a​t​h​]​;​

 ​ ​ ​ ​N​S​D​i​c​t​i​o​n​a​r​y​ ​*​c​o​u​r​s​e​ ​=​ ​s​e​l​f​.​c​o​u​r​s​e​s​[​i​n​d​e​x​P​a​t​h​.​r​o​w​]​;​
 ​ ​ ​ ​c​e​l​l​.​t​e​x​t​L​a​b​e​l​.​t​e​x​t​ ​=​ ​c​o​u​r​s​e​[​@​"​t​i​t​l​e​"​]​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​c​e​l​l​;​
}​

The main thread

Modern iOS devices have multi-core processors that enable the devices to run multiple chunks of code concurrently. Fittingly, this is referred to as concurrency, and each chunk of code runs on a separate thread. So far in this book, all of our code has been running on the main thread. The main thread is sometimes referred to as the UI (user interface) thread, as any code that modifies the UI has to run on the main thread.

When the web service completes, you need to reload the table view data. By default, NSURLSessionDataTask runs the completion handler on a background thread. You need a way to force code to run on the main thread in order to reload the table view, and you can do that easily using the dispatch_async function.

In BNRCoursesViewController.m, update the completion handler to reload the table view data on the main thread.

^​(​N​S​D​a​t​a​ ​*​d​a​t​a​,​ ​N​S​U​R​L​R​e​s​p​o​n​s​e​ ​*​r​e​s​p​o​n​s​e​,​ ​N​S​E​r​r​o​r​ ​*​e​r​r​o​r​)​ ​{​
 ​ ​ ​ ​N​S​D​i​c​t​i​o​n​a​r​y​ ​*​j​s​o​n​O​b​j​e​c​t​ ​=​ ​[​N​S​J​S​O​N​S​e​r​i​a​l​i​z​a​t​i​o​n​ ​J​S​O​N​O​b​j​e​c​t​W​i​t​h​D​a​t​a​:​d​a​t​a​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​o​p​t​i​o​n​s​:​0​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​o​r​:​n​i​l​]​;​
 ​ ​ ​ ​s​e​l​f​.​c​o​u​r​s​e​s​ ​=​ ​j​s​o​n​O​b​j​e​c​t​[​@​"​c​o​u​r​s​e​s​"​]​;​

 ​ ​ ​ ​N​S​L​o​g​(​@​"​%​@​"​,​ ​s​e​l​f​.​c​o​u​r​s​e​s​)​;​
 ​ ​ ​ ​
 ​ ​ ​ ​d​i​s​p​a​t​c​h​_​a​s​y​n​c​(​d​i​s​p​a​t​c​h​_​g​e​t​_​m​a​i​n​_​q​u​e​u​e​(​)​,​ ​^​{​
 ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​.​t​a​b​l​e​V​i​e​w​ ​r​e​l​o​a​d​D​a​t​a​]​;​
 ​ ​ ​ ​}​)​;​
}​

Build and run the application. After the web service completes, you should see a list of Big Nerd Ranch’s courses.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset