Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

iOS 5 beta support #81

Open
peetz opened this Issue · 27 comments

7 participants

@peetz

I have used this fantastic code within an app without any issues. It all works with iOS 4.3 (latest release), but I cannot get it to work under iOS 5 beta. Every time it returns with "Authenticated for (null)", where null is username.

Any ideas folks?

I know we cannot discuss iOS 5 beta per se, but any suggestions to tweaks to code would be much appreciated.

@robreuss

I've got this working under the iOS 5 beta, but only in the simulator. I just posted regarding the failure I'm getting on the device.

Did you ultimately solve the problem you were having? You might try rebuilding the libraries.

@peetz

Not yet found a solution. Switched to use ShareKit instead without any problems, but would prefer to switch back for a bit more functionality than ShareKit offers.

How do I rebuild the libraries?

@robreuss

Yeah, I really need to consume rather than only publish info. I spent a few hours seeing if I could sub-class from ShareKit to use it to call API methods directly (leveraging it's authentication, etc.) but wasn't able to put something together. There's an example in the docs (bottom of this page http://getsharekit.com/docs/) for making SHKRequests, but it seems to be based on xAuth authentication rather than oAuth (it uses username/password in the parameters). I may dig into it again and try to figure out how to put together the necessary set of oAuth headers.

Are you using ShareKit to make requests?

@peetz

I'm basically just publishing at the moment, but do need to retrieve the username to display the user that they are currently connected to twitter as @username. I can't get this by using ShareKit, but I can get the authentication and publish to work quite simply. No luck authenticating with Twitter-oAuth-iPhone on iOS5, although on iOS4.3 it all works nicely, retrieves usernames, etc.

I have looked into making SHKRequests, but username cannot be requested if you use oAuth with Twitter AFAIK.

@robreuss

You inspired me to try again with Sharekit, and I managed to get oath access working, so I was able to pull data from account/verify_credentials (which gives the username) and statuses/home_timeline. Works in iPhone 5.0 simulator.

I basically hacked my solution together and will now need to clean it up, find the best point to integrate - perhaps sub-class to keep some separation from the kit.

This makes me happy - using Sharekit was my first choice, since it offers so much more than just Twitter.

Happy to post instructions and code here if it'll help!

@peetz

Sounds like you've been busy.

If you would be kind enough to share how you retrieved the twitter username (hacked solution is fine), i'd be very grateful.

@robreuss

So I tested on a 4.3 iPad 1 and 5.0 iPad 2 and it worked fine as far as I could tell.

Start from the ShareKit sample app.

Add the following two method declarations to SHKTwitter.h:

- (void)getTimeline;
- (void)getTimeline:(OAServiceTicket *)ticket didFinishWithData:(NSData *)data;

Add the following two methods to SHKTwitter.m:

- (void)getTimeline
{
    //OAMutableURLRequest *oRequest = [[OAMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://api.twitter.com/1/account/verify_credentials.json"]
    OAMutableURLRequest *oRequest = [[OAMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://api.twitter.com/1/statuses/home_timeline.json"]
                                                                    consumer:consumer
                                                                       token:accessToken
                                                                       realm:nil
                                                           signatureProvider:nil];

    [oRequest setHTTPMethod:@"GET"];
    OAAsynchronousDataFetcher *fetcher = [OAAsynchronousDataFetcher asynchronousFetcherWithRequest:oRequest
                                                                                          delegate:self
                                                                                 didFinishSelector:@selector(getTimeline:didFinishWithData:)
                                                                                   didFailSelector:@selector(sendStatusTicket:didFailWithError:)];  

    [fetcher start];
    [oRequest release];
}


- (void)getTimeline:(OAServiceTicket *)ticket didFinishWithData:(NSData *)data {

    if (ticket.didSucceed) {
        [self sendDidFinish];

        NSString* responseString = [[[NSString alloc] initWithData:data
                                                          encoding:NSUTF8StringEncoding]
                                    autorelease];

        SBJsonParser *jsonParser = [[SBJsonParser new] autorelease];

        id result = [jsonParser objectWithString:responseString];

        NSArray *myresultarray = (NSArray *)result;

        for (NSDictionary *tweet in myresultarray) {
            NSLog(@"Tweet text: %@", [tweet objectForKey:@"text"]);
        }

    } else {
        [self sendDidFailWithError:nil];
    }
}


Modify the SendStatus method in the same file to intercept an attempt to share text to twitter and redirect it to GetTimeline:

- (void)sendStatus
{
    [self getTimeline];
    return;
...

As you can see above, I'm using the SBJson parser (https://github.com/stig/json-framework/) to get the raw response made into objects, and then walking the array and displaying the tweet text to test.

If you want to test account/verify_credentials API method, use the comment method in GetTimeline, but also know that my data handler assumes an array in the JSON - the account/settings method returns a dictionary not an array, so it will throw an error, but if you send the output to log, you'll see that it is receiving the response without any authentication problems.

When you run the app, attempt to share text through Twitter, and it should make the call to GetTimeline. You may need to go through an authentication process and relaunch - not sure.

I haven't yet developed an approach to how to integrate in my app, in terms of the authentication workflow, etc. I'm going try and do it in a general purpose way and share. If you have any thoughts on how best to generalize this, would love to hear them.

@robreuss

You asked above how to rebuild the libraries for Twitter-OAuth-iPhone. It requires the project here to generate them, and see additional instructions in the read me on that page:

https://github.com/bengottlieb/Twitter-OAuth-iPhone/tree/master/OAuthConsumeriPhoneLib

As I said, the end result for me was not helpful, since I couldn't get it to execute on my devices.

@robreuss

I initially implemented this down inside SHKTwitter because I wasn't sure how to initialize the session, but I figured it out, so I've moved the method all up to ShareKitAppDelegate.m, to prove these calls could be done without all the UI stuff (I'm doing my retrieving on a background thread) - this was just a matter of adding an isAuthorized method call, which puts together the session, including setting the token on SHKTwitter:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    
    // Override point for customization after app launch    

    [window addSubview:[navigationController view]];
    [window makeKeyAndVisible];

    navigationController.topViewController.title = SHKLocalizedString(@"Examples");
    [navigationController setToolbarHidden:NO];

    [self performSelector:@selector(testOffline) withObject:nil afterDelay:0.5];

        // my code...
    SHKTwitter *myTwitterRequest = [[SHKTwitter alloc] init];
    BOOL gottaSession = [myTwitterRequest isAuthorized];
    [myTwitterRequest getTimeline];
    [myTwitterRequest release];
    // end my code

    return YES;
}

Obviously, a proper implantation would be testing "gottaSession" and present the OAuth authentication screens to deal with the lack of a session - and for me, will probably be returning a parsed result (array or dictionary).

MGTwitterEngine contains a lot of valuable methods that provide excellent abstraction of Twitter's URL-based API methods, so I'd like to leverage that without pulling the entire framework into my app and attempting to integrate with the ShareKit authentication token, etc. Not sure how I'll approach that.

@peetz

Thanks for the code, i'm going to get down to it this evening and try to pull the username using it. Once I get it working I will have a think about the best way to integrate it. ShareKit has this kind of functionality in other services as a method called authorizationFormFields which returns an NSArray, but not in the Twitter (or Facebook) service.

Will let you know how I get on.

@robreuss

This is not working out for me very well. While I'm pretty sure ShareKit would be able to address my Twitter needs, I've not had much luck using the Facebook functionality. It seems like ShareKit has a pretty old version of Facebook Connect. I tried using this (ideashower/ShareKit#158 (commits)) but that too seems older, and FBRequest seems to be missing required methods.

At an impasse.

@peetz

I haven't tried that solution. Didn't get a chance to do anything with oAuth yesterday, but about to start now.

On the issues you are having with ShareKit's outdated Facebook Connect, have you looked at some of the forks? https://github.com/ideashower/ShareKit/network

@robreuss

Yeah, I tried using the one by colinhumber, but ultimately without any real success. I'm not very experienced with github, so perhaps that is already in the master and I didn't know it, but the master seems to have a much older version of Facebook Connect.

My new direction to try is to just take the standard ShareKit and "tear out" the Facebook sharing mechanisms (already proved - easy to do), and then bring the current version of Facebook Connect into my project (I'm very comfortable with it already). I'll integrate it with ShareKit at the UI level, and use ShareKit itself for the other services, especially Twitter.

@peetz

Had a quick tinker with your code to try and retrieve the username (screen_name as twitter calls it in the returned json). I have managed to get it working and output screen_name using an NSLog, but i'm stuck as to how to pass this back as an NSString.

Basically, I am have changed the method name to getUsername from getTimeline, and have not called it from within sendStatus, but instead from viewDidAppear in my own code. The log entry returns every time the view appears, but what I want to do it to display this in an UILabel.

As you can probably guess, I am not the most experienced ... yet ... but i'm getting there. Thanks for your help with this.

@robreuss

You should be able to assign the screen name directly from the JSON object to an NSString:

NSString *screenName = [json_object objectForKey:@"screen_name"];

or something like that.

Or, you send it straight to your label's text property:

myLabel.text = [json_object objectForKey:@"screen_name"];

Hope that helps!

@peetz

Thanks. Sorry, I should have been more clear. I want to return the data as an NSString from the getUsername method in SHKTwitter, so that it can be used in different methods to assign to a UILabel.

@robreuss

Okay - maybe this is what you are looking for - sorry if I'm giving overly basic answers to your issues!

Modify the method declaration for your getUsername method to return a string value:

  • (NSString *) getUsername {

...your code here...

return userNameString;

}

and be sure to modify the method declaration in your SHKTwitter.h file as well.

@peetz

Thanks, but because this is an asynchronous call (i.e. didFinishWithData) I can't do it like that. I'm currently up to my eyeballs in delegation documentation.

@robreuss

Ah, got it.

A simpler way that defining a protocol for delegation is to create an public property on SHKTwitter called "owner" set to the type of the class that creates the SHKTwitter instance, and sets it to itself. That owner object would expose a userName property that you could then set within didFinishWithData.

Or, create a public property on your AppDelegate and set that property from within didFinishWithData. Let me know if you want more details on that approach, which is more or less what I'm going. This has the obvious benefit of exposing that value to the whole system, by having any context which needs access create a pointer to the AppDelegate.

Another way would be to send a notification, but that's overkill when you're sending a single message between just two objects.

@cooliopenguin

Just in case you still didn't get this to work.
I would say the real issue here is that the oAuth access token request fails because it's using non-https (http) requests that are no longer supported. The http addresses forward to the https ones but iOS5 doesn't seem to automatically resolve this.
The simple workaround would be to change

http://twitter.com/oauth/request_token
http://twitter.com/oauth/access_token
http://twitter.com/oauth/authorize

to https://...
in lines 65-67 here
https://github.com/bengottlieb/Twitter-OAuth-iPhone/blob/master/Twitter+OAuth/SAOAuthTwitterEngine/SA_OAuthTwitterEngine.m

@nadangergeo

@cooliopenguin is that the only change required?

@cooliopenguin

Yes, that did the trick for me.
Both timeline and tweeting works now using the original code, just switching to https.
I believe Twitter doesn't support the non-SSL-API anymore.

@nadangergeo

Awesome! Thank you :)

@moeseth

awesome.. thanks a lot.

"The http addresses forward to the https ones but iOS5 doesn't seem to automatically resolve this."

@rigovides

Thanks @cooliopenguin, changing the URLs for the OAuth dance to HTTPS worked for me!

@chewedon

-------- UPDATE 3 ------------

Uh....ok....what...the...hell.

Not sure what I did but...I regenerated my twitter app's token and revert my authentication path back to the original http://twitter.com/oauth/authorize URL and now it's all working....

= /


-------- UPDATE 2 -----------------
OK... it must've been something with the development phone. I tested on a colleague's 3gs and it behaved like the iPhone 4 where it crashes the app when I post a twitter message with the following error:

Authentication for (null)

That's good to know.

-------- END OF UPDATE 2 --------

-------- UPDATE 1 -----------------

It seems like I was able to get the authorisation screen on an iPhone 4 that's running iOS 5...

but when I hit the post, I get Authentication for (null).

However, I'm still experiencing the same problem I described below on a 3GS that has a locked sim and no carrier signal, just WiFi signal (it's a development phone).

-------- END OF UPDATE 1 --------

Guys, I haven't touched the Twitter+OAuth library since months back in May / June. I tried changing the authorization url to use:

https://twitter.com/oauth/request_token
https://twitter.com/oauth/access_token
https://twitter.com/oauth/authorize

as well as these (as shown on my application page on Twitter developer page)

https://api.twitter.com/oauth/request_token
https://api.twitter.com/oauth/authorize
https://api.twitter.com/oauth/access_token

When I click on Twitter button in my app, it brings up the loading screen but it just says "Please Wait..." with the activity indicator wheel. It hangs there and never show the login fields. (My app is still responding though but I basically can't use the Twitter functionality).

Do I need to download a new and updated version of the Twitter+OAuth library?

I also hope this library still works for devices running iOS 5

My email is chewedon@gmail.com if anyone know the solution. Please help, I'm going to go bald soon pulling me hair out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.