Tuesday 5 March 2013

Dealing with the Twitter Oauth-Apocalypse


Twitter changed its access policies and now requires OAuth from all third party applications that access Twitter user accounts. This is a large change from how many iPhone developers having been incorporating Twitter into their applications. What is OAuth exactly? How can iPhone developers get their apps up to date so they do not break their Twitter incorporation? Well we have all the info you need to know about the OAuth Apocalypse.

What is Oauth?

Twitter has a brief explanation of the difference between Basic Authentication and OAuth. They use the example of a letter and how it is addressed as their metaphor, and I think this gets the idea across clearly. You can see their entire explanation alongwith pros and cons here .
TL, DR With Basic Auth you would make every request and would include a username and password with each. This method is very insecure because It allows applications to Actively hold user's credentials and does not have any accountability for Which application is performing what action on who's account. If you imagine your Twitter account as a room, this is like having access to the room be granted based on a single key that many people have copies of. OAuth makes access to your room be granted by a key pad. And everyone that has access to the room has a different code they input into the keypad. You can see who came in and when and revoke anyone's code at any time.
Twitter is evolving and now Requiring applications that register. As a result, gain more control over user's what services have access to their account. Additionally, Twitter can be more effective at targeting malicious applications, and developers can get more accurate feedback on the frequency of their use throughout app's Twitter.With all this said, OAuth is tough to implement from scratch. So today we are going to go through the installation and use of a collection of classes that takes care of the dirty work for us, and let developers update the Twitter functionality of their application easily.

Required Classes

Today we are going to be building off a collection of classes that were created by several different people. The main portion of the class is the MGTwitterEngine Which was created by Matt Legend Gemmell who's website can be found here . From here, Ben Gottlieb took the classes and added his own drop in viewcontroller to them.With all this together we have a simple view controller that will perform login and OAuth, and from there an engine that wants to perform any type of Twitter request we are looking to do. You can get a zip file of the root folder that contains all of these files here .

Installation

We are going to start out with a blank, view based iPhone project called iCodeOAuth.Once the project has come up, take the folder you downloaded called "Twitter + OAuth" and drag it into the "Other Sources" folder within Xcode. Make sure you check the box to copy the sources into the project folder. If we do a build now, you will get a ton of errors. That is because these classes require that libXML be a target of the project build as well. This can be accomplished by clicking the arrow next to targets in the left column of the Xcode project. Here there will be an application called iCodeOAuth. If we right click on this and select Get Info we will see the info about our target. From here we click the Build tab and search for the field "Header Search Paths". You need to add the following into the Header Search Paths:
$ (SDKROOT) / usr/include/libxml2
EDIT: I forgot to include here so that you must include a special XML library for the build to be successful. The framework is called libxml2.dylib.You can find it at
/ Macintosh HD/Developer/Platforms/iPhoneOS.platform/Developer/SDK/iPhoneOS4.1SDK/usr/lib
If you build again, you should see no errors and we can move forward with using this awesome set of classes.

Getting your creds from Twitter

Now that we have these classes properly installed, it is time to take care of some registration requirements for OAuth. In order to use OAuth, you must identify your app to Twitter. Once you do so, Twitter wants to Ooops OAuthConsumerKey with you and to OAuthConsumerSecretKey. These are going to need to be provided to the classes we have just added into our project in order to talk with Twitter's OAuth system To register your application and get these creds go tohttp://dev.twitter.com/apps/new . I have created a demo application called the iCodeBlog OAuth, Whose credentials are included in the sample app which i have provided. For your own personal app you will need to go create your own Twitterapplication and get your own keys.

Using SA_OAuthTwitter Engine

Ben Gottlieb used a great design pattern to create a very easy to use access point for the more complex MGTwitterEngine Which lies underneath. In order to use these classes we will go out into viewcontroller and add the following code to the header:
# Import "SA_OAuthTwitterEngine.h" 
# import "SA_OAuthTwitterController.h"
 
@ Interface iCodeOauthViewController : UIViewController   {
 
	IBOutlet UITableView * tableView;
	IBOutlet UITextField * textField;
 
	SA_OAuthTwitterEngine * _engine;
	 NSMutableArray  * tweets;
 }
 
@ Property  ( nonatomic, retain ) IBOutlet UITableView * tableView;
 @ property  ( nonatomic, retain ) IBOutlet UITextField * textField;
 
- ( IBAction ) update stream : ( id ) sender;
 - ( IBAction ) tweet : ( id ) sender;
 
@ End
And add the following into the Main
-  ( void ) viewDidAppear : ( BOOL ) animated {
 
	if ( _engine )  return ;
 
	_engine =  [ [ SA_OAuthTwitterEngine alloc ] initOAuthWithDelegate : self ] ;
	_engine.consumerKey =  @ "PzkZj9g57ah2bcB58mD4Q" ;
	_engine.consumerSecret =  @ "OvogWpara8xybjMUDGcLklOeZSF12xnYHLE37rel2g" ;
 
	UIViewController * controller =  [ SA_OAuthTwitterController controllerToEnterCredentialsWithTwitterEngine : _engine delegate : self ] ;
 
	if  ( controller ) 
		[ self presentModalViewController : controller animated :  YES ] ;
	 else  { 
		tweets =  [ [ NSMutableArray alloc ] init ] ;
		 [ selfupdate stream : nil ] ;
	 } 
}
This will instantiate our engine with The appropriate consumer and consumer secret key. With this done we will create a controller. If you run the app now you will see a modal web view come up and lead to a sign in page for Twitter. This is a web view, but the great classes written by Ben are programmatically set up to handle the progression of these web views as the user signs in.
Scrolling to the bottom of this page there will be a username and password field to fill out. Do not put in your Twitter credentials yet. We need to fill out a few delegate methods to handle the callback from SA_OAuthTwitterEngine.

Login handling callbacks

Insert the following into your main class.
 
- ( IBAction ) update stream : ( id ) sender {
 
}
 
- ( IBAction ) tweet : ( id ) sender {
 
}
 
# Pragma mark SA_OAuthTwitterEngineDelegate
 
-  ( void ) storeCachedTwitterOAuthData :  ( NSString  * ) data forUsername :  ( NSString  * ) username {
 
	NSUserDefaults 	* defaults =  [ NSUserDefaults standard user defaults ] ;
 
	[ defaults setObject : data forKey :  @ "authData" ] ,
	 [ defaults synchronize ] ;
 }
 
-  ( NSString  * ) cachedTwitterOAuthDataForUsername :  ( NSString  * ) username {
 
	return  [ [ NSUserDefaults standard user defaults ] objectForKey :  @ "authData" ] ;
 }
 
# Pragma mark Delegate SA_OAuthTwitterController
 
-  ( void ) OAuthTwitterController :  ( SA_OAuthTwitterController * ) controller authenticatedWithUsername :  ( NSString  * ) username {
 
	NSLog ( @ "Authenticated with user% @" , username ) ;
 
	tweets =  [ [ NSMutableArray alloc ] init ] ;
	 [ selfupdate stream : nil ] ;
 }
 
-  ( void ) OAuthTwitterControllerFailed :  ( SA_OAuthTwitterController * ) controller {
 
	NSLog ( @ "Authentication Failure" ) ;
 }
 
-  ( void ) OAuthTwitterControllerCanceled :  ( SA_OAuthTwitterController * ) controller {
 
	NSLog ( @ "Authentication Cancelled" ) ;
 }
We just implemented the SA_OAuthTwitterControllerDelegate and the SA_OAuthTwitterEngineDelegate. The SA_OAuthTwitterEngineDelegate methods take care of OAuth Storing the data string in a plist as that when the app is launched again the user will not have to sign in. SA_OAuthTwitterControllerDelegate methods are callbacks Depending on what happens upon sign in. In this case when sign is in another successful method in our class called updateTweets will fire. For now we have those methods defined but we do not have them filled in. We will get to that in a few steps. If you run the application and login using some Twitter credentials, you should see a successful authentication message appear in your debug screen. With this done, let's add some interface elements to our viewController XIB Sun that we can start interacting with Twitter. Our final product is going to look like this:

Building the Interface

Build the interface to open up the XIB for your viewcontroller. We are going to be putting. In 2 buttons, a UITextField and a UITableView Lay out the elements like Sun
Make sure to connect the delegate and datasource of the table view to the file owner. So connect the "Tweet This" button to the tweet method and the update to the update method tweets stream method. Finally, make sure the IBOutlets for the UITextField and the UITableView are set. With these in place we can fill in the final methods to take advantage of our Twitter engine.

Filling in our IBActions

Put the following code in for the IBActions Which We defied before.
# Pragma mark IBActions
 
- ( IBAction ) update stream : ( id ) sender {
 
	[ _engine getFollowedTimelineSinceID : 1 startingAtPage : 1 count : 100 ] ;
 }
 
- ( IBAction ) tweet : ( id ) sender {
 
	[ textField resignFirstResponder ] ,
	 [ _engine send update : [ textField text ] ] ,
	 [ selfupdate stream : nil ] ;
 }
The update stream method will ask our engine to get the Twitter timeline of the people you follow. It will retrieve the first page of the first 100 tweets. There is a delegate method that fires off on completing this request that we will fill out in a moment. The tweet method dismisses the keyboard and then uses our engine to send an update. Once the update is sent we update the tweet view below.

Making a really simple Tweet Object

To help us with displaying tweets we are going to make a very quick Tweet object.This will be a simple NSObject subclass. Use this code for the header:
@ Interface Tweet :  NSObject  {
 
	NSDictionary  * contents;
 }
 
- ( NSString * ) tweet;
 - ( NSString * ) author;
 
@ End
And this code for the main
@ Implementation Tweet
 
- ( id ) initWithTweetDictionary : ( NSDictionary * ) _contents {
 
	if ( self =  [ super init ] )  {
 
		contents = _contents;
		 [ contents retain ] ;
	 }
 
	return self;
 }
 
- ( NSString * ) tweet {
 
	return  [ contents objectForKey : @ "text" ] ;
 }
 
- ( NSString * ) author {
 
	return  [ [ contents objectForKey : @ "user" ] objectForKey : @ "screen_name" ] ;
 } 
@ end
Finally make sure to import the class within the main class of your viewcontroller.These will simply give easy methods to get the info we want out of each tweet that the dictionary MGTwitterEngine will return to us.

Filling in our MGTwitterEngineDelegate Methods

The MGTwitterEngine is doing most of the heavy lifting here when it comes to interacting with Twitter. The methods we are using to get to send tweets and tweets are all defined within the MGTwitterEngine. There is thus a defined MGTwtterEngineDelegate Which defines the callback methods that fire upon these requests finishing. For the sake of completeness, I have filled out all the methods, although only a few of them will be called in the case of our application working properly. Insert the following code into your main.
# Pragma mark MGTwitterEngineDelegate Methods
 
-  ( void ) requestSucceeded : ( NSString  * ) connection identifier {
 
	NSLog ( @ "Request suceeded:% @" , connection identifier ) ;
 }
 
-  ( void ) statusesReceived : ( NSArray  * ) statuses forRequest : ( NSString  * ) connection identifier {
 
	tweets =  [ [ NSMutableArray alloc ] init ] ;
 
	for ( NSDictionary  * d in statuses )  {
 
		NSLog ( @ "See dictionary:% @" , d ) ;
 
		Tweet * tweet =  [ [ Tweet alloc ] initWithTweetDictionary : d ] ,
		 [ tweets addObject : tweet ] ;
		 [ tweet release ] ;
	 }
 
	[ self.tableView reloadData ] ;
 }
 
-  ( void ) receivedObject : ( NSDictionary  * ) dictionary forRequest : ( NSString  * ) connection identifier {
 
	NSLog ( @ "Recieved Object:% @" , dictionary ) ;
 }
 
-  ( void ) directMessagesReceived : ( NSArray  * ) messages forRequest : ( NSString  * ) connection identifier {
 
	NSLog ( @ "Direct Messages Received:% @" , messages ) ;
 }
 
-  ( void ) userInfoReceived : ( NSArray  * ) userInfo forRequest : ( NSString  * ) connection identifier {
 
	NSLog ( @ "User Info Received:% @" , userInfo ) ;
 }
 
-  ( void ) miscInfoReceived : ( NSArray  * ) miscInfo forRequest : ( NSString  * ) connection identifier {
 
	NSLog ( @ "Misc Info Received:% @" , miscInfo ) ;
 }
These methods are all very straight forward in their naming. The only one we fill out Significantly is the statusesReceived: forRequest method. Here is where tweets will be returned to us, each as a separate dictionary when we request the timeline for a user. We will clear the tweets array that we have defined for our class and create a Tweet object for each of the dictionaries we have representing a tweet. From there we will ask our table view to reload. The only task we have left to perform is to fill in our table view data source methods to show all the tweets.

Table View Data Source and Delegate Methods

Now we have everything in place. We just need to create a UITableViewCell for every tweet we have. We will also do some tweaking of the cell size of each cell and the number of lines of each UITextField within each tableView. These methods are very common so I wont go into much detail on them. Here are the Necessary Data Source and Delegate Methods.
# Pragma mark UITableViewDataSource Methods
 
-  ( NSInteger ) tableView : ( UITableView * ) tableView numberOfRowsInSection : ( NSInteger ) section {
 
	return  [ tweets count ] ;
 }
 
-  ( UITableViewCell * ) tableView : ( UITableView * ) tableView cellForRowAtIndexPath : ( NSIndexPath  * ) indexPath {
 
	NSString  * identifier =  @ "Cell" ;
	UITableViewCell * cell =  [ tableView dequeueReusableCellWithIdentifier : identifier ] ;
 
	if ( ! cell )  {
 
		cell =  [ [ UITableViewCell alloc ] initWithFrame style : UITableViewStyleGrouped reuse identifier : identifier ] ;
		 / / [cell setBackgroundColor: [UIColor clearColor]]; 
	}
 
	[ cell.textLabel setNumberOfLines : 7 ] ,
	 [ cell.textLabel setText : [ ( Tweet * ) [ tweets objectAtIndex : indexPath.row ] tweet ] ] ;
 
	return cell;
 }
 
-  ( CGFloat ) tableView : ( UITableView * ) tableView heightForRowAtIndexPath : ( NSIndexPath  * ) indexPath {
 
	return  150 ;
 }
With this in place you will have an OAuth twitter client that is capable of doing any type of interaction with Twitter. You can find the source for the project here

Source : icodeblogs