Having problems with uploading photos to TwitPic using OAuth in Objective C on the iPhone - iphone

I have been working on an iPhone app that has a feature of uploading photos to TwitPic. I have it working with basic authentication.
I am trying to get it working with OAuth. I am getting authentication errors. I have studied very carefully the TwitPic documentation.
I am authorising the app by displaying a UI Web View and the it returns a PIN value. I enter the PIN value in the app and request the token.
I am able to upload status updates to Twitter but not photos.
My code is based on some example code from here:
Example iPhone app using OAuth
Here is my code:
NSString *url = #"http://api.twitpic.com/2/upload.json";
NSString *oauth_header = [oAuth oAuthHeaderForMethod:#"POST" andUrl:url andParams:nil];
NSLog(#"OAuth header : %#\n\n", oauth_header);
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:url]];
[request addRequestHeader:#"User-Agent" value:#"ASIHTTPRequest"];
request.requestMethod = #"POST";
[request addRequestHeader:#"X-Auth-Service-Provider" value:#"https://api.twitter.com/1/account/verify_credentials.json"];
[request addRequestHeader:#"X-Verify-Credentials-Authorization" value:oauth_header];
NSData *imageRepresentation = UIImageJPEGRepresentation(imageToUpload, 0.8);
[request setData:imageRepresentation forKey:#"media"];
[request setPostValue:#"Some Message" forKey:#"message"];
[request setPostValue:TWITPIC_API_KEY forKey:#"key"];
[request setDelegate:self];
[request setDidFinishSelector:#selector(requestDone:)];
[request setDidFailSelector:#selector(requestFailed:)];
[request start];
Here is the OAuth Header:
OAuth realm="http://api.twitter.com/", oauth_timestamp="1275492425", oauth_nonce="b686f20a18ba6763ac52b689b2ac0c421a9e4013", oauth_signature_method="HMAC-SHA1", oauth_consumer_key="zNbW3Xi3MuS7i9cpz6fw", oauth_version="1.0", oauth_token="147275699-jmrjpwk3B6mO2FX2BCc9Ci9CRBbBKYW1bOni2MYs", oauth_signature="d17HImz6VgygZgbcp845CD2qNnI%3D"

HA! I found it!
We should create the header with https://api.twitter.com/1/account/verify_credentials.json and post to http://api.twitpic.com/2/upload.json! (And use GET)
NSString *fakeurl = #"https://api.twitter.com/1/account/verify_credentials.json";
NSString *oauth_header = [oAuth oAuthHeaderForMethod:#"GET" andUrl:fakeurl andParams:nil];
NSLog(#"OAuth header : %#\n\n", oauth_header);
NSString *url = #"http://api.twitpic.com/2/upload.json";
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:url]];
request.delegate = self;
[request addRequestHeader:#"User-Agent" value:#"ASIHTTPRequest"];
request.requestMethod = #"GET";
[request addRequestHeader:#"X-Verify-Credentials-Authorization" value:oauth_header];
[request addRequestHeader:#"X-Auth-Service-Provider" value:#"https://api.twitter.com/1/account/verify_credentials.json"];
NSData *imageRepresentation = UIImageJPEGRepresentation([UIImage imageNamed:#"IMG_0717.jpg"], 0.2);
if (imageRepresentation) {
NSLog(#"Pic not nil");
}
[request setData:imageRepresentation forKey:#"media"];
[request setPostValue:#"twitpic, i hate you. die painfully." forKey:#"message"];
[request setPostValue:twitPicKey forKey:#"key"];
[request setDelegate:self];
[request setDidFinishSelector:#selector(requestDone:)];
[request setDidFailSelector:#selector(requestFailed:)];
[request start];

Use GSTwitPicEngine: https://github.com/Gurpartap/GSTwitPicEngine
Using GSTwitPicEngine:
Initialize the engine with class or as needed:
self.twitpicEngine = (GSTwitPicEngine *)[GSTwitPicEngine twitpicEngineWithDelegate:self];
Find the authorization token and supply to twitpicEngine with:
[twitpicEngine setAccessToken:token];
Then to upload image and attach a text message along with it (does not post to twitter):
[twitpicEngine uploadPicture:[UIImage imageNamed:#"mypic.png"] withMessage:#"Hello world!"]; // This message is supplied back in success delegate call in request's userInfo.
To upload image only:
[twitpicEngine uploadPicture:uploadImageView.image];
Upon end of request, one of the delegate methods is called with appropriate data and information.
GSTwitPicEngineDelegate protocol specifies two delegate methods:
- (void)twitpicDidFinishUpload:(NSDictionary *)response {
NSLog(#"TwitPic finished uploading: %#", response);
// [response objectForKey:#"parsedResponse"] gives an NSDictionary of the response one of the parsing libraries was available.
// Otherwise, use [[response objectForKey:#"request"] objectForKey:#"responseString"] to parse yourself.
if ([[[response objectForKey:#"request"] userInfo] objectForKey:#"message"] > 0 && [[response objectForKey:#"parsedResponse"] count] > 0) {
// Uncomment to update status upon successful upload, using MGTwitterEngine's instance.
// [twitterEngine sendUpdate:[NSString stringWithFormat:#"%# %#", [[[response objectForKey:#"request"] userInfo] objectForKey:#"message"], [[response objectForKey:#"parsedResponse"] objectForKey:#"url"]]];
}
}
and
- (void)twitpicDidFailUpload:(NSDictionary *)error {
NSLog(#"TwitPic failed to upload: %#", error);
if ([[error objectForKey:#"request"] responseStatusCode] == 401) {
// UIAlertViewQuick(#"Authentication failed", [error objectForKey:#"errorDescription"], #"OK");
}
}
All set?

OAuth method to generate a header must be GET. Not POST.
Also url must be https://api.twitter.com/1/account/verify_credentials.json

Thanks, this helped me get it working too :) I also updated http://github.com/jaanus/PlainOAuth with working example code.

Related

How to send POST XML from iOS app?

So, I've ran over and over the web in search for anything about sending XML with POST from iPhone app - no luck so far, none!
I'm using in my app KissXML, which I find very easy and useful when it comes to getting XML out of response - but quite opposite when sending XML to server...
Here is my method for connecting and receiving XML. I tried to put NSString containing simply my XML request into body of POST request, but it doesn't work as planned.
-(void)prepareTransaction{
NSLog(#"FXSecondVC: preparing transaction...");
NSString *login = [[NSUserDefaults standardUserDefaults] stringForKey:#"kUsername"];
NSString *password = [[NSUserDefaults standardUserDefaults] stringForKey:#"kPassword"];
NSString *host = [[NSUserDefaults standardUserDefaults] stringForKey:#"kURLServer"];
NSURL *url = [[NSURL alloc] initWithString:host];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
[httpClient setAuthorizationHeaderWithUsername:login password:password];
[httpClient registerHTTPOperationClass:[AFKissXMLRequestOperation class]];
NSString *xmlString = #"<RootEl xmlns=\"http://some.url/goes/here\">"
"<Element1>12678967.543233</Element1>"
"<Element2>"
"<string xmlns=\"bla.bla/url\">"
"String content</string>"
"<string xmlns=\"bla.bla/url\">"
"String content</string>"
"</Element2>"
"<Element3>true</Element3>"
"<Element4>String content</Element4>"
"<Element5>1999-05-31T11:20:00</Element5>"
"<Element6>true</Element6>"
"</RootEl>";
NSMutableURLRequest *request = [httpClient requestWithMethod:#"POST" path:kServerRequestURL parameters:nil];
[request setHTTPBody:[xmlString dataUsingEncoding:NSUTF8StringEncoding]];
AFKissXMLRequestOperation *operation = [AFKissXMLRequestOperation XMLDocumentRequestOperationWithRequest:request success:^(NSURLRequest *req, NSHTTPURLResponse *resp, DDXMLDocument *XMLDocument){
NSLog(#"[SUCCESS]: XMLDocument: %#", XMLDocument);
}failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, DDXMLDocument *XMLDocument) {
NSLog(#"error parsing: %#", [error localizedDescription]);
}];
[operation start];
}
This is what I'm getting in response:
2012-11-21 19:40:09.884 FXApp[19662:707] FXSecondVC: preparing transaction...
2012-11-21 19:40:10.011 FXApp[19662:707] error parsing: Expected status code in (200-299), got 400
Am I missing something here? I want to use KissXML, because it the simplest way (at least known to me) to use already prepared XML document in successful response, but if solution requires changing framework - don't hesitate. The priority is to get it working.
I hit dead end - this is driving me crazy, especially it is really urgent matter.
Mystery solved:
it appears that all I had to do was to set Content-Type for xml - which I wasn't doing. Solution found here
Here you go:
[request setValue:#"application/xml; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
In my case, I had to use setValue:#"text/xml" to get the desired JSON response from the server (PHP server).
i.e. I used the following:
[request addValue:#"text/xml" forHTTPHeaderField:#"Content-Type"];

How to avoid URL NOT SUPPORTED error on iphone

I am calling a URL through POST HTTP method. When I am not connected to 3G or WiFi, it will never fetch the URL correctly and will never get the response from the URL correctly.
Connection is made through post:
NSString *URL = [NSString stringWithFormat:#"http://www.abc.com/webservices/client_server.cfc];
[[NSURLCache sharedURLCache] removeAllCachedResponses];
NSString *time_stamp =[NSString stringWithFormat:#"time_stamp=%#", self.today];
NSData *postData = [time_stamp dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%d",[postData length]];
NSMutableURLRequest *URLRequest = [[[NSMutableURLRequest alloc] init] autorelease];
[URLRequest setURL:[NSURL URLWithString:URL]];
[exhURLRequest setHTTPMethod:#"POST"];
[URLRequest setValue:postLength forHTTPHeaderField:#"Content-Length"];
[URLRequest setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[URLRequest setHTTPBody:postData];
self.connection =[[[NSURLConnection alloc] initWithRequest:URLRequest delegate:self] autorelease];
NSAssert(self.connection != nil, #"Failure to create URL connection.");
I am checking if I get the response through :
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
// check for HTTP status code for proxy authentication failures
// anything in the 200 to 299 range is considered successful
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if (([httpResponse statusCode]/100) == 2) {
self.data = [NSMutableData data];
} else {
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:
NSLocalizedString(#"HTTP Error",
#"Error message displayed when receving a connection error.")
forKey:NSLocalizedDescriptionKey];
NSError *error = [NSError errorWithDomain:#"HTTP" code:[httpResponse statusCode] userInfo:userInfo];
[self handleError:error];
}
}
But some how when the URL is not connected lets say when I am in the metro or something, I cannot connect to URL and it throws me "URL NOT SUPPORTED". How can I avoid such a scenario and allow the app to push forward without throwing such an error ? If anybody has used offline redirection to the app without keep on waiting the user on the front screen.
Download appleā€™s Reachability sample code. Add Reachability.h+.m to your project. Link SystemConfiguration framework. Then use their Reachability class like so.
#import "Reachability.h"
for WiFi:
-(void)doStuff{
Reachability *wifi = [Reachability reachabilityForLocalWiFi];
if (wifi.currentReachabilityStatus == ReachableViaWiFi){
// do connected stuff
}
else {
// do offline stuff
}
}
For Wifi or 3G:
-(void)doStuff{
NetworkStatus connectivity = [Reachability reachabilityForInternetConnection].currentReachabilityStatus;
if (connectivity == ReachableViaWiFi || connectivity == ReachableViaWWAN){
// do connected stuff
}
else {
// do offline stuff
}
}
If you will be continually doing network tasks add the reachability object to your class. That will improve performance and allow you to use it's notifier.
some time this "URL NOT SUPPORTED error" is due to the url not encoding.
for this encode url by using this.or there are lots of ways on internet.
dont encode whole url or base url (as it will encode ":" and '/' too and it may cause errors)
only special charecter needs to be encode like &,# etc.
NSString *encodedString = (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(
NULL,
(CFStringRef)pathRequest,
NULL,
(CFStringRef)#"!*'();:#&=+$,%#[]",
kCFStringEncodingUTF8 ));
How to check for an active Internet connection on iOS or OSX?
Use the solution propsed here to avoid sending a request when you don't have an active internet connection. Simple.

Do a GET request with parameters on iPhone SDK?

I'm doing an app which retrieves user info, and it does that using a GET request, but I don't know how could I make one. Already tried ASIHTTPRequest with this code:
NSURL *url = [NSURL URLWithString:#"http://forrst.com/api/v2/users/info"];
// Now, set up the post data:
ASIFormDataRequest *request = [[[ASIFormDataRequest alloc] initWithURL:url] autorelease];
[request setPostValue:#"1" forKey:#"id"];
[request setRequestMethod:#"GET"];
// Initiate the WebService request
[request setDelegate:self];
[request startAsynchronous];
But I get this response from forrst:
2011-06-05 16:59:32.189 Forrsty[4335:207] {"resp":{"error":"you must pass either id or username"},"stat":"fail","in":0.0557,"authed":false,"authed_as":false,"env":"prod"}
Which I understand I'm not doing the GET request ok, so how I would do it? Thanks :)
A Get Parameter ?
So why don't you try "http://forrst.com/api/v2/users/info?id=1" ?
[ NSString stringWithFormat:#"http://forrst.com/api/v2/users/info?id=%d", 1 ];
By the way, take a look at this librayry : http://allseeing-i.com/ASIHTTPRequest/
Good luck !

Twitter API Rate Limit - Overcoming on an unauthenticated JSON Get with Objective C?

I see the rate limit is 150/hr per IP. This'd be fine, but my application is on a mobile phone network (with shared IP addresses).
I'd like to query twitter trends, e.g. GET /trends/1/json.
This doesn't require authorization, however what if the user first authorized with my application using OAuth, then hit the JSON API?
The request is built as follows:
- (void) queryTrends:(NSString *) WOEID {
NSString *urlString = [NSString stringWithFormat:#"http://api.twitter.com/1/trends/%#.json", WOEID];
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *theRequest=[NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:10.0];
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self startImmediately:YES];
if (theConnection) {
// Create the NSMutableData to hold the received data.
theData = [[NSMutableData data] retain];
} else {
NSLog(#"Connection failed in Query Trends");
}
//NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:urlString]];
}
I have no idea how I'd build this request as an authenticated one however, and haven't seen any examples to this effect online.
I've read through the twitter OAuth documentation, but I'm still puzzled as to how it should work.
I've experimented with OAuth using Ben Gottlieb's prebuild library, and calling this in my first viewDidLoad:
OAuthViewController *oAuthVC = [[OAuthViewController alloc]
initWithNibName:#"OAuthTwitterDemoViewController" bundle:[NSBundle mainBundle]];
// [self setViewController:aViewController];
[[self navigationController] pushViewController:oAuthVC animated:YES];
This should store all the keys required in the app's preferences, I just need to know how to build the GET request after authorizing!
Maybe this just isn't possible? Maybe I'll have to proxy the requests through a server side application?
Any insight would be appreciated!
Authorizing through OAuth will provide you an authorization token, which you need to pass to each request you make later on.
Refer to Twitter docs, read about how authorization works.
Okay, after a lot of searching I've managed to figure how to construct a request to the JSON API programmatically in Xcode.
Firstly, you need to use the OAuth demo code to authenticate and authorize your application.
Then, you'll be retrieving the key by doing: [prefs stringForKey:#"authData"] - if this doesn't exist, you haven't been OAuth'd properly.
I had to reverse engineer this by looking through the code of the OAuth library, and while it's easy to use the library for stuff like sending a status update, it doesn't allow you to retrieve trends...:
#import "OAMutableURLRequest.h"
#import "MGTwitterHTTPURLConnection.h"
NSMutableString *dataString;
// Using OAuth:
OAConsumer *consumer = [[OAConsumer alloc] initWithKey:#"YOURCONSUMERKEY"
secret:#"YOURCONSUMERSECRET"];
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSString *authData = [prefs stringForKey:#"authData"];
// [_engine
OAMutableURLRequest *theRequest = [[[OAMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://stream.twitter.com/1/statuses/filter.json"]
consumer:consumer
token: (authData) ? [[OAToken alloc] initWithHTTPResponseBody:authData] : nil
realm: nil
signatureProvider:nil] autorelease];
[theRequest setHTTPMethod:#"POST"];
[theRequest setHTTPBody: [httpBody dataUsingEncoding:NSUTF8StringEncoding]];
[theRequest setHTTPShouldHandleCookies:NO];
// Set headers for client information, for tracking purposes at Twitter.
[theRequest setValue:#"Trendy" forHTTPHeaderField:#"X-Twitter-Client"];
[theRequest setValue:#"1.0" forHTTPHeaderField:#"X-Twitter-Client-Version"];
[theRequest setValue:#"http://www.inisdesign.com" forHTTPHeaderField:#"X-Twitter-Client-URL"];
// Set the request body if this is a POST request.
[theRequest prepare];
// Create a connection using this request, with the default timeout and caching policy,
// and appropriate Twitter request and response types for parsing and error reporting.
MGTwitterHTTPURLConnection *connection;
connection = [[MGTwitterHTTPURLConnection alloc] initWithRequest:theRequest
delegate:self
requestType:MGTwitterFollowedTimelineRequest // Wrong type
responseType:MGTwitterStatuses]; // as above - doesnt seem to matter
if (!connection) {
return;
} else {
// [_connections setObject:connection forKey:[connection identifier]];
// [connection release];
dataString = [[NSMutableData data] retain];
[connection start];
}
}
The rest is implemented as a standard URL connection with didReceiveData methods etc..
I haven't verified this is alleviating my rate limiting problems, but hey.. It's a start if anybody has similar problems.

Parsing Data returned from Twitpic API

I just wanted to ask you if anyone can help me parsing the returned data from the Twitpic API?
I'm creating a HTTPFormRequest using the ASIHTTPRequest Wrapper for Cocoa. This all happens in an iPhone application:
NSURL *url = [NSURL URLWithString:#"http://twitpic.com/api/upload"];
NSString *username = t_user;
NSString *password = t_pass;
NSData *twitpicImage = UIImagePNGRepresentation(imageView.image);
// Now, set up the post data:
ASIFormDataRequest *request = [[[ASIFormDataRequest alloc] initWithURL:url] autorelease];
[request setPostValue:twitpicImage forKey:#"media"];
[request setPostValue:username forKey:#"username"];
[request setPostValue:password forKey:#"password"];
[request setData:twitpicImage forKey:#"media"];
// Initiate the WebService request
[request start];
if ([request error]) {
NSLog(#"%#", [request error]);
} else if ([request responseString]) {
NSLog(#"%#", [request responseString]);
}}
Now comes the hard part, I don't know how to parse the data that is in [request responseString]. I know I need to use NSXMLParser, but I dunno how to use it. All I need is to get the url of the image.
Thx in advance.
Feel free to have a look at my little XML parse classes here http://www.memention.com/blog/2009/10/31/The-XML-Runner.html
I have started to use them for parsing the response from image upload to yfrog.com
Basically I do like this...
In NameValueParser.m I changed the entry tag to rsp like this
entryName = [[NSString stringWithString:#"rsp"] retain];
then where the response has been received I parse it like this
NameValueParser *parser = [NameValueParser parser];
[parser addFieldName:#"statusid"];
[parser addFieldName:#"userid"];
[parser addFieldName:#"mediaid"];
[parser addFieldName:#"mediaurl"];
[parser addFieldName:#"err"];
[parser parseData:responseData]; // the response received by ASIHTTPRequest
NSArray *rspArray = [parser list];
NSLog(#"%#", rspArray); // Have a look at it here
Try it as written at the bottom of this tutorial click here using NSScanner. They are showing exactly what you need, retrieving only the mediaurl = URL of uploaded image.
NSScanner *scanner = [NSScanner scannerWithString:responseString]; ...
GSTwitPicEngine does XML and JSON parsing both: http://github.com/Gurpartap/GSTwitPicEngine
Though, why not use JSON format for the Twitpic API responses? It's easy to parse and deal with using yajl, TouchJSON, json-framework or other Cocoa JSON libraries