Archive

Posts Tagged ‘iphone’

Switching from Basic to xAuth with mgtwitterengine on iPhone

July 29th, 2010

I should preface this article by saying that I’ve consumed hundreds of code tutorials and walkthroughs – this is my first attempt to give back to the massive online library of free help. Also, I’ve never used git so I just zipped up the source and such instead of posting it on github. Hope you all enjoy it.

Background
I’m the technical co-founder of Sound Around. We make an online iphone app builder that lets bands and live performers create and manage their own custom iPhone applications. One of the features in the band’s app is to let the fans share out songs, news, shows, pics, etc, on twitter in just a couple clicks. All we use mgtwitterengine for is logging in, posting tweets, checking follow status and initiating following – your mileage may vary based on how extensively you use mgtwitterengine. Twitter will be shutting off Basic authentication soon, so I needed to venture out and implement xAuth and rebuild/resubmit all of our applications to apple to avoid the OAuthocalypse.

Step 1: Request xAuth permission from the API team at twitter

I simply sent a detailed message to api@twitter.com with my company information and intent to use xAuth. It took them 16 hours to get back to me with no questions asked. Not bad with a deadline so quickly approaching! http://dev.twitter.com/pages/xauth

Step 2: Get my stripped iPhone mgtwitterengine with xauth

This was the confusing part for me. The latest bleeding edge of mgtwitterengine on github had a lot of added fluff that I didn’t need for my iPhone project. There was added YAJL items and support for MAC OS X which just wasn’t necessary and was causing compile issues. I decided to strip all of that out and just include the files that were needed for the iPhone to be functional. Additionally, I picked up OAuthConsumer from http://github.com/jdg/oauthconsumer and dropped it into the project.

Download: http://com.soundaround.web.misc.s3.amazonaws.com/mgtwitterengine.zip

NOTE: I’m using MGTwitterEngine built as a static library that I compile as a dependency of my main project. You’ll need to look elsewhere for a tutorial on how to do that, or you can just copy the “Classes” source files into your project. The mgtwitterengine project from the zip file should build on its own with a couple warnings (not sure what from, didnt seem important to me)

Step 3: set your consumer key and consumer secret after instantiating your mgtwitterengine instance

Here is my code that gets called when my app launches. I have a single instance of mgtwitterengine that the whole app uses. Your key and secret will be available on your twitter app profile page.

self.m_twitterEngine = [[[MGTwitterEngine alloc] initWithDelegate:self] autorelease];
[self.m_twitterEngine setConsumerKey:@"your_key" secret:@"your_secret"];

Step 4: modify your “checkUserCredentials” type of function to use the new getXAuthAccessTokenForUsername function

This is the bread and butter “login” check for the credentials provided by your users. It will return an NSString identifier like all other requests do – good idea to store this value for later checking in delegate methods. I also store the username and a UISwitch value for if they want us to remember their login (and auto-login their account next time the app launches). Yours may vary but it should have some variant of what I have below.

- (void)initiateCredentialCheckWithUsername:(NSString*)_username password:(NSString*)_password remember:(BOOL)_remember {
    self.m_authenticateRequest = [self.m_twitterEngine getXAuthAccessTokenForUsername:_username password:_password];
    self.m_remember = _remember;
    self.m_username = _username;
}

Step 5: modify your delegate file to handle new “successful authentication” call

Your current delegate method should already have a mechanism to handle failed login. Now that you’re using xAuth, the handling for failed logins wont change at all. What does change, however, is the sequence of events on a successful login.

In the event of a successful login, two delegate methods will be called (not sure if they’re called in order, or if its deterministic at all). You’ll get a normal requestSucceeded callback, but you’ll also get a call to accessTokenReceived with an argument containing your access token that will be used for all subsequent requests. I put all of my “successful login” code in the latter method since it will only be called upon a successful authentication challenge, plus it has the cool token that you need to feed to your mgtwitterengine object.

- (void)accessTokenReceived:(OAToken *)token forRequest:(NSString *)connectionIdentifier {
    self.m_accessToken = token;
    [self.m_twitterEngine setAccessToken:self.m_accessToken];
   
    self.m_authenticateRequest = nil;
    self.m_authenticated = YES;
   
    if(self.m_remember)
        [self saveLoginCredentials];
   
    //kick off the request to check if they’re following the band
    [self initiateFollowStatusCheck];
   
    //NSLog(@"TWITTER: authenticate success", requestIdentifier);
    [[NSNotificationCenter defaultCenter] postNotificationName:@"twitter_login_success" object:@"twitter"];
}

The only important part of the above code is that you set the access token for your mgtwitterengine object. Whatever else you wish to do in this method is optional.

[self.m_twitterEngine setAccessToken:self.m_accessToken];

Step 6: put in mechanisms to save the token and recall it on app launch

Once you receive the token, the OAToken class has a built-in method that you can use to save it to NSUserDefaults. The following code snippet is the body of my [self saveLoginCredentials] that you see above after I successfully receive the token. Note that service provider name and prefix are strings that you can set to whatever you want.

[[NSUserDefaults standardUserDefaults] setObject:self.m_username forKey:@"twitter_username"];
[self.m_accessToken storeInUserDefaultsWithServiceProviderName:@"soundaround" prefix:@"token"];
[[NSUserDefaults standardUserDefaults] synchronize];

Now, on program init, we can check to see if NSUserDefaults has a token for us to use based on the provider and prefix strings we used to save it. Here is my init function that sets up my twitter stuff. You’ll notice that if I find a token via NSUserDefaults, I “trick” the class by manually calling the delegate method with the retrieved token as if we had received it from an authentication challenge – this allows the rest of the “successful login” code to execute as if they had entered their user credentials again.

- (id)init {
    if(self = [super init]) {
        self.m_twitterEngine = [[[MGTwitterEngine alloc] initWithDelegate:self] autorelease];
        [self.m_twitterEngine setConsumerKey:@"your_key" secret:@"your_secret"];
       
        self.m_accessToken = [[OAToken alloc] initWithUserDefaultsUsingServiceProviderName:@"soundaround" prefix:@"token"];
        if(self.m_accessToken != nil) {
            //token successfully restored, pull the username from user defaults
            self.m_username = [[NSUserDefaults standardUserDefaults] valueForKey:@"twitter_username"];
           
            //simulate received token as authentication
            self.m_authenticateRequest = @"shirley";
            [self accessTokenReceived:self.m_accessToken forRequest:self.m_authenticateRequest];
           
            //NSLog(@"TWITTER: restored username:%@", self.m_username);
        }
       
        return self;
    }
   
    return nil;
}

And, to be nice, destroy the token when they want to logout of their account.

[OAToken removeFromUserDefaultsWithServiceProviderName:@"soundaround" prefix:@"token"];

Step 7: start making calls like you normally would (aka. you’re done)

Everything else you’ve set up already to work with Basic Auth should work exactly as it did before, only now the underlying guts of mgtwitterengine are wrapping the pretty little OAToken information into all of your calls. Status, follow, retweet, etc…it all should work just as it did before even though you have the new authentication scheme up front. You will still continue to receive data and success/failure callbacks to the normal methods that you’ve implemented.

Conclusion

I hope this lets you focus what you needed to get your iPhone project running. Please tweet this article if it was helpful for you and link back to http://www.getsoundaround.com to help plug our company.

Also, we’re looking for another technical co-founder if you’re a rockstar ((Android || iPhone) && LAMP) programmer and want to get in on an equity basis. Email me at scott@getsoundaround.com

Author: scott Categories: Code Tags: , , ,