Today I’m going to take a bit of a break from writing about Orny to discuss Apple’s APIs for Twitter integration, newly introduced in the iOS 5 SDK.
Specifically, we’re going to cover the Twitter framework (an elaboration of NSHTTPRequest
), the Accounts framework (a central framework and daemon for the storage and management of account credentials) and we’ll briefly touch on NSJSONSerialization
, Apple’s previously private (and now public) implementation of a JSON-to-Foundation-Object parser (it also works the other way ‘round).
Beginnings
I’m going to assume that you’ve read our previous iOS Tutorials and know how to start a new project. Things are a little different in the new version of Xcode, but not so different that past instructions are too badly out of date. Create a new project, using the following images for clues.
For those without images we’re using the Single View Application template, adding a Class Prefix of “TWAPIExample”, the Device Family is “Universal” and all three checkboxes are ticked, because we are using Storyboard, Automatic Reference Counting and we want to Include Unit Tests.
Sparse Documentation
At the time of writing, the documentation on some of these Frameworks is still in its early days. I’ve done my best to piece things together from the API documentation and present a coherent overview here. You should, however, definitely review Apple’s documentation before you use any of this code (as it may have changed by the time you read this document).
Forming a Coherent Request
To begin with, we’ll do a simple request to get some results off Twitter’s public Firehose. This does not require authentication. We’re going to grab a single public tweet, and pop it in a UILabel
on the screen when the app loads. Nothing fancy.
Adding the Framework
Before we can use the TWRequest
class, we need to add the Framework to our project. If you’re not familiar with Frameworks, they’re optional sections of the iOS API that your application can include at compile time. Adding the Framework lets Xcode know that you want to use these features in your app.
Select your project, and scroll down to the Linked Frameworks and Libraries section (under the Summary heading). Click the plus arrow, and scroll down until you find ‘Twitter.framework’. Add it. (At the time of writing, I found the text-input search box at the top of this list failed to work. YMMV; scroll manually if in doubt).
Specifying the URL
The first thing we need to do is specify the URL we’re going to hit to grab the data. We’re provided some excellent API documentation, for example the full Twitter REST API Resources list. We’re going to use the REST API for this application. Also on offer is a Twitter Streaming API for high-volume, near-realtime access to voluminous Twitter data, well beyond the scope of what we want to achieve in this example.
Apple’s documentation on TWRequest
doesn’t really say what form it expects the URL to take, but as I suspected and discovered experimentally, it wants a full URL to Twitter’s API, plus the path specified in the documentation. In this case, the URL we want is:
https://api.twitter.com/1/statuses/public_timeline.json
Specifying Parameters
The TWRequest
object allows us to specify optional parameters, possible values as described in the Twitter API documentation. We’ll encode these as a Foundation Dictionary object (key-value pairs), specifying that we only want a single Tweet to be returned. The code to do so looks like this:
NSDictionary *parameters = [NSDictionary dictionaryWithObjectsAndKeys:@"1", @"count", nil];
Adding the UILabel Object
In the latest version of Xcode, we now have access to two Storyboards.
The Storyboard replaces the previous panoply of NIB files present in an iOS project. Now all Views
appear on the one Storyboard, and are attached to their respective ViewControllers
. You can much more easily get a feel for how your application hangs together from the Storyboard, though it can become more cluttered.
Click the Storyboard for your iPhone UI (MainStoryboard_iPhone.storyboard
) and select the View
. Drag and drop a Label
into the center of the View
, select the Label
, and navigate to its properties. Get through the following three screenshots and we’ll meet at the bottom to change the properties.
Give the View
a tag (I’ve used “1’ in this example, just an integer and nothing special). I chose to center-align my text with the Alignment control, and have increased Lines to 5 (allowing the Label to expand to 5 lines rather than truncating text.)
Next, go across to the ‘View Properties’ tab for your Label
. You’ll want to resize the Label
to be nice and big, so it can display an entire Tweet. Grab the edges and expand it to fill the available space. You’ll also want to make it a multi-line label, so in the field called Lines (see the screenshots, above) increase the number to 3 or 4. Repeat the above steps for your iPad Storyboard ensuring sure to use the same tag!
Creating the TWRequest Object
We’ll add a message, getTweet
, to our ViewController
object. First, we need to add the Twitter API headers to our ViewController
’s header class TWAPIExampleViewController.h
around line 10. We’ll specify the functions and methods we need in the header while we’re here, around line 13.
#import <UIKit/UIKit.h>
#import <Twitter/Twitter.h>
@interface TweetAMaticViewController : UIViewController
@property (nonatomic, retain) UILabel *tweetLabel;
-(void)getTweet;
@end
We’ll use the tweetLabel
property as a handle to our UILabel
in the View
, so we can reference it easily at will. We’ve defined a @property
, so of course we need to @synthesize
it in TWAPIExampleViewController.m
at the start of our implementation:
@implementation TWAPIExampleViewController
@synthesize tweetLabel;
- (void)didReceiveMemoryWarning
{
Now, let’s define the function we’ll use to get the tweet, just before the @end
of our implementation in TWAPIExampleViewController.m
.
#pragma mark - Twitter Interactions
-(void)getTweet
{
// Specify the URL and parameters
NSURL *url = [NSURL URLWithString:@"https://api.twitter.com/1/statuses/public_timeline.json"];
NSDictionary *parameters = [NSDictionary dictionaryWithObjectsAndKeys:@"1", @"count", nil];
// Create the TweetRequest object
TWRequest *tweetRequest = [[TWRequest alloc] initWithURL:url parameters:parameters requestMethod:TWRequestMethodGET];
[tweetRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
// Request completed and we have data
// Output it!
NSError *jsonError = nil;
id timelineData = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableContainers error:&jsonError];
if(timelineData == NULL) {
// There was an error changing the data to a Foundation Object,
// so we'll output a bunch of debug information.
NSString *myString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
NSLog(@"nnConversion to object failed.");
NSLog(@"HTTP Response code: %d", [urlResponse statusCode]);
NSLog(@"Output from server: %@", myString);
NSLog(@"JSON Error: %@nn", [jsonError localizedDescription]);
abort();
// TODO: Show a graceful error message here
}
NSDictionary *timelineDict = (NSDictionary*) timelineData;
NSLog(@"nnConversion succeeded!");
NSLog(@"%@nn", timelineDict);
self.tweetLabel.text = [[(NSArray*)timelineDict objectAtIndex:0] objectForKey:@"text"];
}];
}
Whew, busy function! We could probably refactor this substantially by decomposing it and moving much functionality to another class, but for the moment we’ll leave it as-is. Consider this refactoring an exercise for the reader, homework if you will.
NSLog()
will output information to the Console, which you can view using Console.App. This is a pretty cheesy way to get information out of your application (you’re much better off getting comfortable with the Debugger) but it’s a simple way to get started and diagnose simple problems (the Debugger sometimes has trouble with large strings and displaying complex data.)
We use a block after performRequestWithHandler
to handle the response from the server. A block is the Objective-C 2.0 implementation of a Closure, and explaining them is beyond the scope of this article, but it’s a handy way to encapsulate a simple block of code without having to encase it in a function unto itself (and should be familiar to any Javascript developers in the room).
Finally, we set the text attribute of self.tweetLabel
… oh, but we haven’t a handle to that yet, have we?
Handling & Displaying the Response
We need to grab ahold of our Label
, and also invoke our getTweet
message. We’ll do this in our ViewController
’s viewDidLoad
message, which defines actions to be taken once the View
has been loaded from a NIB file.
- (void)viewDidLoad
{
[super viewDidLoad];
self.tweetLabel = (UILabel*)[self.view viewWithTag:1];
[self getTweet];
}
Remember how we set a tag on our Label
in the View
earlier? We now use this tag to refer to that View
component and take ahold of it, so we can modify it.
Conclusion
If you compile and run the program, it should pull a single Tweet off Twitter’s public Firehose. Who knows where it will be from, or what language it will be in!?
You should now be armed with enough knowledge to display a Tweet via Twitter’s API, including some of its metadata like the author’s screen name and the date and time, it’s just a matter of copying what we did with the label. If you’re feeling especially industrious you could even show a map using location information, if the Tweet includes it…
Andy is a software developer who regularly uses PHP, Ruby, and Objective-C to build for the Web and mobile web. His passions are teaching and enabling people, and building new things. In addition to holding down a secretive day job, he runs OiOi Studios by night.