Twitter is on everyone's hot list right now. I enjoy it myself and we have one for Switch On The Code. Anyway, being the Month of iPhone, I decided to do something a little different with it. Twitter Counter is a service that tracks how many followers you have but takes it a step further - they will estimate future number of followers using historical data. Basically stats for tracking Twitter followers. This tutorial is going to show how to use the Twitter Counter API to get follower information on any account.
You can see a screenshot below of the application we are going to build today. It is a fairly simple application that will grab a couple bits of information. This data includes current number of followers and estimates for next week and next month. Although, you could expand this to include all kinds of information or even graph out the followers for the last month.
The first step is to create a new project, in this case we are going to create a View-based Application. This is going to create us a view nib file and view controller so we don't have to. Next up is putting together the interface, this is done in Interface Builder. The interface is built of a UITextField, UIButton, and multiple UILabel controls. There's a quick video below showing the interface being put together.
Next on the list of fun things to do includes setting up some IBOutlet variables on our view controller that was already created for us. We add one for the text field and the labels we are going to write out to. We also want to set up our view controller as the delegate for our text field so we mark it in our header. One other item we are going to add here is a method that will be called to go and get the information for our Twitter account - this method needs to return IBAction and take in (id)sender. The header is updated to something like below.
@interface Twitter_CounterViewController : UIViewController
<UITextFieldDelegate> {
IBOutlet UITextField *txtUser;
IBOutlet UILabel *labelUser;
IBOutlet UILabel *labelFollowers;
IBOutlet UILabel *labelFollowersTomorrow;
IBOutlet UILabel *labelFollowersNextMonth;
}
- (IBAction)getTwitterInfo:(id)sender;
@end
A little bit of house cleaning can be done at this point. We can add the needed text field delegate method to our view controller implementation file. This method is -(BOOL)textFieldShouldReturn:(UITextField *)textField. All we are going to do in it is check if the passed in text field is ours and resign the first responder. A blank implementation of getTwitterInfo is added. Along with these, I removed all the commented out lines that come standard with the class. With these updates the code looks likes the following.
@implementation Twitter_CounterViewController
- (IBAction)getTwitterInfo:(id)sender {
}
- (void)dealloc {
[super dealloc];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
if(textField == txtUser) {
[textField resignFirstResponder];
}
return YES;
}
@end
Adding the IBOutlet variables and IBAction method to our header is only one part of actually hooking up to the interface. Now, we have to connect the dots, literally, in Interface Builder. We connect our variables to the correct items on the interface, the action to the Touch Up Inside event, and the text field delegate. Since I adore video here is another short one showing how to do this.
To keep the information that is going to be pulled from TwitterCounter.com organized I created an object class that holds the information in a few properties. The class header has properties for name, follower information, and also avatar. Why avatar? Well, just to pull some more info. The header and implementation files follow.
The header file, TwitterAccount.h.
@interface TwitterAccount : NSObject {
NSString *user_name;
NSString *avatar;
int followers;
int followers_tomorrow;
int followers_next_month;
}
@property (nonatomic, copy) NSString *user_name;
@property (nonatomic, copy) NSString *avatar;
@property (nonatomic) int followers;
@property (nonatomic) int followers_tomorrow;
@property (nonatomic) int followers_next_month;
@end
And the implementation file, TwitterAccount.m.
@implementation TwitterAccount
@synthesize user_name;
@synthesize avatar;
@synthesize followers;
@synthesize followers_tomorrow;
@synthesize followers_next_month;
@end
Taking a look back into our view controller header I added a property for the current account, named account. Make sure to include the TwitterAccount.h file. In the implementation file I synthesized it and made sure to clean up in dealloc by releasing the property.
We're getting down to pretty much just the logic code that handles retrieving the information. To make things a little bit easier I creating a class that use the NSXMLParser class to handles requesting and parsing the xml from Twitter Counter's API. I named my class TwitterCounterHandler. The header file is below.
#import "TwitterAccount.h"
@interface TwitterCounterHandler : NSObject {
TwitterAccount *account;
NSMutableString *currentStringValue;
}
- (TwitterAccount *)getTwitterAccount:(NSString *)name;
- (int)getIntValueString:(NSString *)value;
@end
As you can see we keep a instance variable for the account we are working with and the current string value (more explained on this later). There are two methods. The first one, getTwitterAccount, handles getting the information for an account and returning it, the second is used for internal use which will take the string in the xml and turn it into a number we can use.
Inside the implementation file we start by looking at getTwitterAccount which starts by creating a new account object and building the url for the request to the API. Then a NSURL object is created for the url. The next part is where all the action happens - we create a NSXMLParser object that is filled with the data from our url (the xml response). Since, NSXMLParser uses the delegate pattern we need to set a delegate for the parser, in this case self. Setting self as the delegate means we have to implement the delegate function in the class we are in. Parsing the data is next, this all happens in an event based way, the delegate functions for NSXMLParser all correspond with events that happen during the parsing. We, however, are only going to be worrying about when we hit a value of a xml node or the end of a node. After parsing we release our parser and return the account. This leaves the method looking like:
account = [[[TwitterAccount alloc] init] autorelease];
NSString *twitterCounterUrl = [NSString
stringWithFormat:@"http://twittercounter.com/api/?username=%@&results=1",
name];
NSURL *url = [NSURL URLWithString:twitterCounterUrl];
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:url];
parser.delegate = self;
[parser parse];
[parser release];
return account;
}
Below you can see the code for parsing the xml.
if (!currentStringValue) {
// currentStringValue is an NSMutableString instance variable
currentStringValue = [[NSMutableString alloc] initWithCapacity:50];
}
[currentStringValue appendString:string];
}
- (void)parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName {
if([elementName isEqualToString:@"user_name"]) {
account.user_name = currentStringValue;
} else if([elementName isEqualToString:@"avatar"]) {
account.avatar = currentStringValue;
} else if([elementName isEqualToString:@"followers_current"]) {
account.followers = [self getIntValueString:currentStringValue];
} else if([elementName isEqualToString:@"tomorrow"]) {
account.followers_tomorrow = [self getIntValueString:currentStringValue];
} else if([elementName isEqualToString:@"next_month"]) {
account.followers_next_month = [self getIntValueString:currentStringValue];
}
// currentStringValue is an instance variable
[currentStringValue release];
currentStringValue = nil;
}
// replaces \n and \t in string to get actual value
- (int)getIntValueString:(NSString *)value {
NSString *intValueString = [value
stringByReplacingOccurrencesOfString:@"\n"
withString:@""];
intValueString = [intValueString
stringByReplacingOccurrencesOfString:@"\t"
withString:@""];
return [intValueString intValue];
}
Inside the chuck of code we have two delegate methods, foundCharacters and didEndElement, both start with parser:(NSXMLParser *)parser, of course - did I mention I hate Objective-C's method naming. The first is called when characters in a node value are parsed, we simply check if our variable is created and if not create it; then add the value to the string. The second method is called when the end element of a node is parsed ( for example). When this happens we want to check which element it was, take the value for the element, currentStringValue, and set it on the account. Because the follower numbers come with some junk in front of them I created the getIntValueString method. Finally, we release the current string value and set it to nil, so next element will have a clean string to set the value on.
That takes care of getting the data and parsing it, we just need to call our handler's getTwitterAccount method we created earlier. We also need to take the values from the account and set our labels - again being done in our view controller's method. This leaves us with a very clean view controller implementation file that looks like the following. from our IBAction
#import "TwitterCounterHandler.h"
@implementation Twitter_CounterViewController
@synthesize account;
- (IBAction)getTwitterInfo:(id)sender {
TwitterCounterHandler *tcHandler = [[[TwitterCounterHandler alloc] init]
autorelease];
self.account = [tcHandler getTwitterAccount:txtUser.text];
labelUser.text = self.account.user_name;
labelFollowers.text = [NSString stringWithFormat:@"%d",
self.account.followers];
labelFollowersTomorrow.text = [NSString stringWithFormat:@"%d",
self.account.followers_tomorrow];
labelFollowersNextMonth.text = [NSString stringWithFormat:@"%d",
self.account.followers_next_month];
}
- (void)dealloc {
[account release];
[super dealloc];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
if(textField == txtUser) {
[textField resignFirstResponder];
}
return YES;
}
@end
Notice that we grab the username from the text field and pass it in and the rest is pretty straight forward. Well that wraps up this tutorial. As always, the project source files are included below. If anyone has any questions feel free to leave them in the comments or if they're more general head on over to our forums.
Add Comment
[language] [/language]
Examples:
[javascript] [/javascript]
[actionscript] [/actionscript]
[csharp] [/csharp]
See here for supported languages.
Javascript must be enabled to submit anonymous comments - or you can login.