Read iOS Programming: The Big Nerd Ranch Guide, 3/e (Big Nerd Ranch Guides) Online
Authors: Aaron Hillegass,Joe Conway
Tags: #COM051370, #Big Nerd Ranch Guides, #iPhone / iPad Programming
So far,
Nerdfeed
deals with XML data only. While XML is nice, it is becoming less popular, and a new data format called JSON (
JavaScript object notation
) is coming into favor. Like XML, JSON is a human-readable data interchange format. However, JSON is a lot more concise, so it takes up less memory and is faster to transfer across networks.
There is a class that deals with JSON in iOS called
NSJSONSerialization
. This class can take a chunk of JSON data and turn it into objects. It can also go in the other direction: take objects and turn them into JSON data. Using this class means you don’t have to know with what JSON really looks like. (But, you should anyway, so be sure to read the section at the end of this chapter.)
The top songs RSS feed from Apple currently returns XML, but we can ask it to return JSON instead. Let’s do this to get a chance to try out
NSJSONSerialization
. In
BNRFeedStore.m
, change the service URL to request JSON.
Of course, when this request completes,
BNRConnection
and
RSSChannel
will be pretty confused by this new type of data. Thus, we need to give
BNRConnection
the ability to determine whether the data is XML or JSON and parse it appropriately. We also need to teach
RSSChannel
and
RSSItem
how to gather their instance variables from JSON data.
First, here’s how
NSJSONSerialization
works. When JSON data is received, you send the message
JSONObjectWithData:options:error:
to the class
NSJSONSerialization
. This method takes the data and returns either an
NSArray
or an
NSDictionary
. Within this dictionary or array, there will be strings, numbers, or more dictionaries and arrays.
Each dictionary returned from
NSJSONSerialization
is an instance of
NSDictionary
and represents a single object within the JSON data. For example, the channel in the feed will be represented by an
NSDictionary
. This
NSDictionary
will contain an array of
NSDictionary
instances, and each dictionary will be an individual
RSSItem
.
While having the JSON data parsed into these common data structures is really convenient, it is sometimes useful to move this data into the appropriate model objects. In
Nerdfeed
, the
ListViewController
knows how to present
RSSChannel
and
RSSItem
s but not
NSArray
s and
NSDictionary
s. To keep things simple, we want to transfer the data from the dictionaries and arrays into our own classes,
RSSChannel
and
RSSItem
.
To do this, we will create a new protocol. This protocol will have one method,
readFromJSONDictionary:
, that takes an
NSDictionary
as an argument. A class that conforms to this protocol will implement
readFromJSONDictionary:
to retrieve its instance variables from the dictionary. Both
RSSChannel
and
RSSItem
will conform to this protocol. Create a new file from the Objective-C protocol template (
Figure 28.10
).
Figure 28.10 Creating an Objective-C protocol
Name this protocol
JSONSerializable
. In
JSONSerializable.h
, add a method to the protocol.
In a moment, you will write the code so that both
RSSChannel
and
RSSItem
conform to this protocol. Right now, though, let’s improve
BNRConnection
so that it can parse JSON data and pass the result to its root object. In
BNRConnection.h
, import the file
JSONSerializable.h
.
Then, add a new property to this class.
When the
BNRFeedStore
creates a
BNRConnection
, it will either supply a
jsonRootObject
or an
xmlRootObject
to the connection, depending on the type of data it expects back from the server. When there is a
jsonRootObject
, the connection will parse the JSON data and hand the result off to the root object (instead of using
NSXMLParser
). In
BNRConnection.m
, synthesize this new property and modify the implementation of
connectionDidFinishLoading:
.
In
BNRFeedStore.m
, change the method
fetchTopSongs:withCompletion:
so that it supplies a
jsonRootObject
to the connection instead of an
xmlRootObject
.
You can build the application to check for syntax errors. You will see one warning in
BNRFeedStore.m
that says
RSSChannel
is incompatible with
setJsonRootObject:
. That’s because
RSSChannel
doesn’t yet conform to
JSONSerializable
.
In
RSSChannel.h
, import this file and declare that
RSSChannel
conforms to this protocol.
Now that
RSSChannel
and
RSSItem
conform to
JSONSerializable
, once the connection completes, they will pull their data from the dictionary when sent
readFromJSONDictionary:
.
The implementation of
readFromJSONDictionary:
should take values from the dictionary argument and move them into the instance variables of the
RSSChannel
. The channel needs to grab the title and all of the entries from the channel. In
RSSChannel.m
, implement this method to do just that.
Do the same thing in
RSSItem.h
.
And in
RSSItem.m
, grab the title and link from the entry dictionary.
You can now build and run the application. Once it starts running, flip to Apple’s RSS feed. Voilà! Now that the
RSSItem
is grabbing the link, you can tap an entry in the table view and listen to a sample clip. (The debugger will spit out a lot of angry nonsense about not finding functions or frameworks, but after a moment, it will shut up, and the clip will play.)
Notice that, in MVCS (and in MVC, too), model objects are responsible for constructing themselves given a stream of data. For example,
RSSChannel
conforms to both
NSXMLParserDelegate
and
JSONSerializable
– it knows how to pack and unpack itself from XML and JSON data. Also, think about
BNRItem
instances in
Homepwner
: they implemented the methods from
NSCoding
, so that they could be written to disk.