Hostingheaderbarlogoj
Join InMotion Hosting for $3.49/mo & get a year on Tuts+ FREE (worth $180). Start today.
Advertisement

iPhone SDK: First Steps With JSON Data Using the Twitter API

by
Gift

Want a free year on Tuts+ (worth $180)? Start an InMotion Hosting plan for $3.49/mo.

Web accessible APIs provide a wealth of resources for extending the functionality of your iPhone apps. These APIs tend to be consumed in two primary data formats: XML and JSON. The iPhone SDK has built-in support for XML parsing, and this will be covered in a separate tutorial. In this tutorial, we will be focusing on adding JSON support to your iOS applications by demonstrating how to create a Twitter reading client app.

JSON Twitter Client Final Preview

Introduction

The JSON Framework is an extremely popular framework. It is used to power a large portion of the commonly used apps on the App Store, including the Facebook app, the Google Maps app, and pretty much every single Twitter client out there.

Why JSON?

There are a couple of reason to chose JSON over XML. The primary reason JSON is preferred is because JSON files are generally smaller in file size than their XML counterparts, and this naturally results in less load time. Another reason worth considering is that Apple's Push Notification system works best with JSON payloads.

1. Getting Started

Creating an Xcode Project

  1. Create a new Xcode project of your choosing. I'm going to use the View-Based template.
    Creating a new JSON project in Xcode
  2. Download the JSON Framework here.

Adding the Framework

Adding the framework is simple. You just need to drag the files into your project. There is an option to link the library to your project, but that option is now deprecated and not supported.

  1. Drag the JSON folder from the DMG and drop it in your Xcode project. You can add it to the "Other Sources" group if you would like.
  2. Select the 'Copy items into destination group's folder' option when prompted.
    Adding the JSON Framework in Xcode
  3. Add:
    #import "JSON.h"
    

    into the source files where you will be using the framework.

2. Using the JSON Library

A Sample JSON Message

The following is a sample JSON message representing what a person object might look like:

{
         "firstName": "John",
         "lastName": "Smith",
         "age": 25,
         "address": {
             "streetAddress": "21 2nd Street",
             "city": "New York",
             "state": "NY",
             "postalCode": "10021"
         },
         "phoneNumber": [
             { "type": "home", "number": "212 555-1234" },
             { "type": "fax", "number": "646 555-4567" }
         ]
}

Parsing a JSON String

To parse the above, we first load the JSON message into an NSString:

 
NSString *jsonString = [[NSString alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"data" ofType:@"json"] encoding:NSUTF8StringEncoding error:&error];

From looking at the sample JSON message, we can see that data is encapsulated with both brackets (e.g. [ ] ) and braces (e.g. { } ). The brackets are arrays of data and the braces are the dictionaries of data. As you can see, we are able to nest arrays of dictionaries, and dictionary objects can contain arrays for multiple values.

In the example above, we have a dictionary for contact with keys such as firstname, lastname, etc. The address key points to another dictionary which contains its own key-value pairs. The phoneNumber key in the dictionary contains an array of dictionaries.

To parse an array we use:

 
NSArray *results = [jsonString JSONValue];

To parse a Dictionary, we use:

 
NSDictionary *dictionary = [jsonString JSONValue];
NSArray *keys = [dictionary allKeys]; // the keys for your dictionary

Choose the one you need based on your root object. For example, if your API is returning a list of contacts, you would get back an Array of contacts dictionaries. Similarly, if you ask for a twitter timeline, you're getting back an array of tweets where each tweet is a dictionary.

Creating a JSON String

Creating a JSON string is very simple and straightforward. You create either an NSDictionary with the appropriate Keys and Values or an NSArray with the list of objects. This process is fairly similar to how we parsed the JSON in the previous step.

You would create a dictionary with your data.

 
NSDictionary *contactData = [NSDictionary dictionaryWithObjectsAndKeys:
								 _titleField.text, @"title",
								 _summaryField.text, @"summary",
								 _urlField.text, @"url",
								 _phoneField.text, @"phone",
								 _bdayField.text, @"birthday",
								 _addrField.text, @"address",
								 nil];

Add your newly created data dictionary to its class

 
NSDictionary *finalData = [NSDictionary dictionaryWithObject:contactData forKey:@"contact"];

Then generate the JSON Representation of your class dictionary.

 
NSString *newJSON = [finalData JSONRepresentation];

You can also create your arrays and nest dictionaries or arrays accordingly.

Note: There is no method in the JSON framework that will validate whether a message is valid JSON.

3. Putting It All Together

Let's create a simple iPhone app that shows a list of the last 5 tweets that contains mobtuts.

We're going to use the Twitter Search API for simplicity because it doesn't require authentication or authorization.

A Tweet

The URL for our request is:

http://search.twitter.com/search.json?q=mobtuts&result_type=recent

This will return to us a dictionary result which contains an array of tweets.

To make life a little easier, let's concentrate on just the user's profile image url, the tweet text, and the user's twitter name.

A single resulting tweet looks like this:

 
{"profile_image_url":"http://a3.twimg.com/profile_images/949941117/zucker_normal.jpg","created_at":"Thu, 10 Jun 2010 03:54:22 +0000","from_user":"mariacarol","metadata":{"result_type":"recent"},"to_user_id":null,"text":"RT @mobtuts: How to Install Android 2.2 Froyo on iPhone http://bit.ly/c8kBb6","id":15824617764,"from_user_id":311442,"geo":null,"iso_language_code":"en","source":"Seesmic"},{"profile_image_url":"http://a1.twimg.com/profile_images/655595496/retro9_normal.gif","created_at":"Thu, 10 Jun 2010 03:52:28 +0000","from_user":"cead22","metadata":{"result_type":"recent"},"to_user_id":null,"text":"Quiero un #iPhone4 con #FroYo -- How to Install Android 2.2 Froyo on iPhone http://bit.ly/c8kBb6 (@mobtuts)","id":15824510273,"from_user_id":34036310,"geo":null,"iso_language_code":"en","source":"Twitter for BlackBerry\u00ae"},{"profile_image_url":"http://a1.twimg.com/profile_images/769690946/mobiletuts_icon_normal.png","created_at":"Thu, 10 Jun 2010 03:45:51 +0000","from_user":"mobtuts","metadata":{"result_type":"recent"},"to_user_id":null,"text":"Subscribe to the @mobtuts weekly podcast now! Choose RSS http://bit.ly/9LMbGX or iTunes http://bit.ly/bq0QMC","id":15824135971,"from_user_id":104427899,"geo":null,"iso_language_code":"en","source":"HootSuite"},{"profile_image_url":"http://a1.twimg.com/profile_images/63581538/tutsplus_normal.jpg","created_at":"Thu, 10 Jun 2010 03:25:00 +0000","from_user":"tutsplus","metadata":{"result_type":"recent"},"to_user_id":null,"text":"Subscribe to the @mobtuts weekly podcast now! Choose RSS http://bit.ly/9LMbGX or iTunes http://bit.ly/bq0QMC","id":15822900558,"from_user_id":2295627,"geo":null,"iso_language_code":"en","source":"TweetDeck"},{"profile_image_url":"http://a1.twimg.com/profile_images/234225566/illustration_normal.jpg","created_at":"Thu, 10 Jun 2010 03:22:33 +0000","from_user":"GreatTwitTips","metadata":{"result_type":"recent"},"to_user_id":null,"text":"RT @mobtuts: RT @berryizm_feeds: 5 BlackBerry Theme Developer Tips from MMMOOO a BlackBerry theme and app dev comp... http://bit.ly/agf0ND","id":15822755393,"from_user_id":19637346,"geo":null,"iso_language_code":"en","source":"twitterfeed"}

We can see that we're looking for the "profile_image_url", "from_user", and "text" keys for our app.

The Parser

Let's create a simple JSON Parser and make the request from the API.

Twitter_SearchAppDelegate.h

 
#import <UIKit/UIKit.h>
@class Twitter_SearchViewController;
 
@interface Twitter_SearchAppDelegate : NSObject <UIApplicationDelegate> {
    UIWindow *window;
    NSMutableData *responseData;
    Twitter_SearchViewController *viewController;
}
 
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet Twitter_SearchViewController *viewController;
 
@end

Now we'll add the JSON framework to the import statements.

Twitter_SearchAppDelegate.m

 
#import "JSON.h"

From here, we'll modify didFinishLaunchingWithOptions method in the App Delegate and make an NSURLRequest to Twitter with the URL to grab the Tweets we want to process. We'll also set the App Delegate to be the NSURLRequest's delegate.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    
    
    // Override point for customization after application launch.
 
    // Add the view controller's view to the window and display.
	responseData = [[NSMutableData data] retain];
	tweets = [NSMutableArray array];
	NSURLRequest *request = [NSURLRequest requestWithURL:
							 [NSURL URLWithString:@"http://search.twitter.com/search.json?q=mobtuts&rpp=5"]];
	[[NSURLConnection alloc] initWithRequest:request delegate:self];
 
    return YES;
}

Now, since our App Delegate is the NSURLRequest's delegate, we must implement those delegate methods. We're primarily interested in being notified when the data is being sent to us and when the request is complete and there is no more data to receive.

 
#pragma mark NSURLConnection Delegate methods
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
	[responseData setLength:0];
}
 
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
	[responseData appendData:data];
}
 
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
	label.text = [NSString stringWithFormat:@"Connection failed: %@", [error description]];
}

This is where our parser is doing most of the work. It is parsing the JSON file and creating the array of tweets for us. We get the array and pass it on to the TableViewController to display it.

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
	[connection release];
	NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
	[responseData release];
	
	NSDictionary *results = [responseString JSONValue];
	
	NSArray *allTweets = [results objectForKey:@"results"];
	
	[viewController setTweets:allTweets];
    [window addSubview:viewController.view];
    [window makeKeyAndVisible];
}

A Simple TableView

Now we simply use the array we created from the parser to display the data in our TableView.

Twitter_SearchViewController.h

 
#import  
 
@interface Twitter_SearchViewController : UITableViewController {
	NSArray *tweets;  // this is the array that was passed to us from the App Delegate
}
@property (nonatomic, retain) NSArray *tweets;
@end

TweetsTableViewController.m

In the controller, we first need to synthesize the property and then set the number of rows that we'll be displaying

 
@synthesize tweets;
 
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    // Return the number of rows in the section.
    return [tweets count];
}

Finally, we'll display the tweets by referencing their specific keys from the dictionary. From looking at the JSON string earlier, we know we're looking for the values associated with the "from_user", "profile_image_url", and "text" keys.

// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    static NSString *CellIdentifier = @"Cell";
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
    }
    
    // Configure the cell...
	NSDictionary *aTweet = [tweets objectAtIndex:[indexPath row]];
    cell.textLabel.text = [aTweet objectForKey:@"text"];
	cell.textLabel.adjustsFontSizeToFitWidth = YES;
	cell.textLabel.font = [UIFont systemFontOfSize:12];
	cell.textLabel.numberOfLines = 4;
	cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;
	
	cell.detailTextLabel.text = [aTweet objectForKey:@"from_user"];
	
	NSURL *url = [NSURL URLWithString:[aTweet objectForKey:@"profile_image_url"]];
	NSData *data = [NSData dataWithContentsOfURL:url];
	cell.imageView.image = [UIImage imageWithData:data];
	cell.selectionStyle = UITableViewCellSelectionStyleNone;
    return cell;
}

Additional Considerations

If you're planning on using either Three20 or httpriot, you do not need to add the JSON framework. Those project already include it in their code. If you're planning on using ASIHTTPRequest, the JSON framework is not included and you do need to add it yourself.

Advertisement