I'm trying to track an event in my app using Yahoo Web Analytics. The code I am using looks like
ASIHTTPRequest *yahooTrack = [ASIHTTPRequest requestWithURL:
[NSURL URLWithString:#"http://s.analytics.yahoo.com/p.pl?a=xxxxxxxxxxxxx&js=no&b=yyyyyyyyyyyy&cf6=zzzzzzzzzzz"]];
yahooTrack.didFinishSelector = #selector(statisticsFinished:);
yahooTrack.delegate = self;
[yahooTrack startAsynchronous];
Then the statisticsFinished looks like:
NSLog(#"Cookies: %#", request.requestCookies);
NSLog(#"Redircount: %d", [request redirectCount]);
NSLog(#"Responsecode %d %#\nMsg: %#", request.responseStatusCode,
request.responseStatusMessage, [request responseString]);
And all the information I get back looks correct. Cookies are set, redirectcount is 1 the first time (as it redirects to s.analytics.yahoo.com/itr.pl?.... a normal browser does). Then the redirectcount is 0 for subsequent request until the app is restarted and session cleared. The responseString returns GIF89a.
Even if the data looks correct, Yahoo still won't track. As soon as I call the tracking url directly in my browser it works as expected.
I realize Flurry is a better option, but I'm forced to use Yahoo in this case. Also, using a UIWebView probably would work, but I'm against putting in a webview just for tracking purposes.
Is there any difference in how ASIHTTPRequest and Safari would handle a call to a simple URL as this? Or do you see anything else that could explain why the tracking isn't working?
I finally found the problem. ASIHTTPRequest creates a user-agent based on your applications name, and requests from this user agent is ignored by Yahoo somehow (bug?). As stated in the documentation, you can override the user-agent as follows:
[request addRequestHeader:#"User-Agent" value:#"My-User-Agent-1.0"];
I used the user-agent string of Safari on iPhone, and it worked immediately! BTW; the same problem applies for Android, and the same fix works.
Related
i want to like a post in Facebook, i have post_id , and i am not able to find the FQL Query for liking particular post from the Facebook developer page in IOS SDK.
From Facebook developer page, it says that you can like a post with the used of HTTP POST method it means we can't use GraphAPI or fql.query to like a post.
Can anyone please share HTTP POST URL to like a post in Facebook.
is anyone here who develop the like button functionality for Facebook post using custom button in iOS.
Thanks in advance.
Here is an example if you are using Facebook SDK in iOS:
[FBRequestConnection startWithGraphPath:[NSString stringWithFormat:#"%#/likes", post_id]
parameters:[NSDictionary dictionary]
HTTPMethod:#"POST"
completionHandler:^(FBRequestConnection *connection,
id result,
NSError *error)
{
if (error)
{
NSLog(#"Error: %#", [error localizedDescription]);
}
else
{
NSLog(#"Result: %#", result);
}
}];
I see you are asking for fields(parameters) for HTTP POST URL. An HTTP POST request does not (usually) contain parameters on which you are probably used to when you pass them in a classic GET request such as ?param1=value¶m2=value after the script name in some URL.
POST request sends data to the server inside the message body, check out: http://en.wikipedia.org/wiki/POST_(HTTP)
Now that you know that, this is what you can do:
You CAN get the number of likes with a classic GET request, an URL that you can paste into any web browser and get the response, for example:
https://graph.facebook.com/260895413924000_605362559477282/likes?access_token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
This url will give you a response with all the people who liked that post/photo.
You can leave out the ?access_token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx if you know the post/photo is public as this one is (https://www.facebook.com/photo.php?fbid=605362542810617&set=a.260905783922963.82517.260895413924000).
If it is not you need to generate one actual access_token(also for posting you NEED to generate one) and for testing you can do it here: https://developers.facebook.com/tools/explorer/
Now if you want to actually like the photo you can't simply form an URL that you can copy/paste inside your browser and which will trigger the like action. That's because browsers do not do POST requests, you need to do it trough code as Ivo Patrick Tudor Weiss suggested or eventually for testing purposes you can do it with curl utility from console like this:
curl --data "access_token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" https://graph.facebook.com/260895413924005362559477282/likes
and you can undo the like with HTTP DELETE ... like this:
curl --data "access_token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -X DELETE https://graph.facebook.com/260895413924000_605362559477282/likes
- UPDATE, for additional questions made by OP in the comments:
It is of course possible to use ASIHTTPRequest to make GET, POST and DELETE HTTP requests. However I would not advise the use of that library for your case. One reason is that the author of ASIHTTPRequest has stopped working on the library, and the other reason is that Facebook SDK for iOS is a better choice since with it you have many other things already taken care for you.
That being said here are the examples:
First type either one of these three combinations depending on what you want:
Get all people who liked the specific post:
(for simplicity I omitted the access_token here but you can append it to the URL if needed)
NSURL *url = [NSURL URLWithString:#"https://graph.facebook.com/260895413924000_605362559477282/likes"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
Like the specific post yourself:
NSURL *url = [NSURL URLWithString:#"https://graph.facebook.com/260895413924000_605362559477282/likes"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request appendPostData:[#"access_token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" dataUsingEncoding:NSUTF8StringEncoding]];
//[request setRequestMethod:#"POST"]; // <--- NOT NEEDED since it is the default if you previously called appendPostData
Unlike the post:
NSURL *url = [NSURL URLWithString:#"https://graph.facebook.com/260895413924000_605362559477282/likes"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request appendPostData:[#"access_token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" dataUsingEncoding:NSUTF8StringEncoding]];
[request buildPostBody];
[request setRequestMethod:#"DELETE"];
Then execute the actual request:
[request startSynchronous];
NSString *response = [request responseString];
NSLog(#"Response: %#", response);
Remember synchronous request is OK for testing but your GUI is going to be unresponsive if you use it on the main thread in an actual app. Learn how to do an asynchronous request here: http://allseeing-i.com/ASIHTTPRequest/How-to-use
As for your iOS example. It would be too much to write all the code here. And you already got the answer from Ivo Patrick Tudor Weiss which is perfectly correct. The only thing that is missing is the boilerplate code that you need to have to authenticate on Facebook and establish an FBSession.
I would advise you to go over this material here: https://developers.facebook.com/docs/getting-started/facebook-sdk-for-ios/
Download the latest SDK which contains also the sample code, and follow the tutorial on Facebook web. Then when you get the basics configured, get back to the answer you got from Ivo.
You can use Graph API to post a like to Facebook post. As it said in documentation here:
http://developers.facebook.com/docs/reference/api/post/
To create a like you need to issue a HTTP POST request to the POST_ID/likes connection with the publish_stream permission. You can suppress the notification created when liking a Post by passing a notify parameter with value of false.
I am writing an app that displays content from a Wordpress Site, and also allows reading of comments as well as posting comments. I am handling logging in to leave a comment and posting a comment via XML-RPC. All that is working quite well. However, this particular site does not allow anonymous commenting. So, I need to allow Registering for an account through the app.
Currently, I take the desired "username" and "email" and submit via POST as follows:
ASIFormDataRequest *request = [[ASIFormDataRequest alloc] initWithURL:[NSURL URLWithString:#"http://www.lamebook.com/wp-signup.php"]];
[request setPostValue:#"example" forKey:#"user_name"];
[request setPostValue:#"example#test.com" forKey:#"user_test"];
[request setDelegate:self];
[request setDidFinishSelector:#selector(registerFinished:)];
[request setDidFailSelector:#selector(registerFailed:)];
[request startAsynchronous];
This works in that it will create the account. However, my issue is that in my registerFinished method:
- (void)registerFinished:(ASIFormDataRequest *)request {
NSString *response = [[NSString alloc] initWithData:[request responseData] encoding:NSASCIIStringEncoding];
NSLog(#"response %#", response);
}
The response is simply the HTML of the registration page. The HTML contains no information about the success or failure of the registration.
When using the webform the returned HTML has entries if any error occurred, for example:
<p class="error">Username must be at least 4 characters</p>
However, I do not seem to get these elements in the HTML I receive on the phone. Is there a proper way to do registration on the phone?
If you have access to the site, which I guess you do, you should be able to write a small plugin that let's you perform the registration by posting data to an URL specified by your plugin. This would be fairly simple, just hook up a function to the init action and check for the $_POST variable for any input.
Then simply use username_exists to check for existing users and wp_create_user to perform the registration. These functions will give return values that you in turn can send as a JSON reponse (or whatever is appropriate) back to you application.
In fact, my experience with XML-RPC is that it's somewhat limited, and not really up to date with the rest of WordPress, so I often make these little mini API's to handle situations like this. All that might have changed in the latest releases, however.
I'd like to know if it was possible, if a user wishes to subscribe to updates of my applications, take a form that is automatically subscribed to this newsletter at this address http://www.gseo.it/lists/?p=subscribe&id=2 (this is my mailing list with double opt in) but I'd like to know that a user can subscibe this newsletter directly from my iphone app.
Thanks
You could do an HTTP POST to that form using ASIFormDataRequest.
This isn't working code, but it might look something like:
NSURL *url = [NSURL URLWithString:#"http://www.gseo.it/lists/?p=subscribe&id=2"];
ASIFormDataRequest *request = [[[ASIFormDataRequest alloc] initWithURL:url] autorelease];
[request setPostValue:#"someone#example.com" forKey:#"email"];
[request startSynchronous];
You can get the library here.
Yes of course you can, open up a UIWebview with the the url provided. Don't forget that this may look not good in the iphone browser so providing a custom html code depending on the user agent may improve things.
For the purposes of this, I'm going to pretend the original url is http://host/form and the new url is https://host/form. (Note that before I ship this, both URLs are going to be secure. However, the nonsecure-to-secure seems like a convenient redirect to test this on.)
I'm accessing a web API using NSURLConnection that redirects me. Basically, I want to take everything I just submitted to http://hostaform and re-submit it to https://host/form. I thought this would be the default behavior, but it looks like the body is being lost in the redirect.
So I think I need to handle the connection:willSendRequest:redirectResponse: event of the NSURLConnection's delegate and re-attach the body. The problem is this message seems woefully underdocumented. The only info I can find on this method is NSURLConnection Class Reference, which isn't very helpful. Among other things, it includes this:
redirectResponse: The URL response that caused the redirect. May be nil in cases where this method is not being sent as a result of involving the delegate in redirect processing.
I'm not sure what this means. Combined with an initial willSendRequest: invocation, I think this is means willSendRequest: is being sent even for my initial request, prior to the redirect response. Is that correct?
So I've added code to my delegate to retain the body an extra time, and added this willSendRequest: handler:
- (NSURLRequest *)connection: (NSURLConnection *)inConnection
willSendRequest: (NSURLRequest *)inRequest
redirectResponse: (NSURLResponse *)inRedirectResponse;
{
if (inRedirectResponse) {
NSMutableURLRequest *r = [[inRequest mutableCopy] autorelease];
[r setURL: [inRedirectResponse URL]];
[r setHTTPBody: body];
return r;
} else {
return inRequest;
}
}
It doesn't work. But I'm not even sure if this is the right approach. It seems excessively hackish to me. What should I be doing? Is this documented anywhere? I've found nothing useful in Apple's documentation or using Google so far.
(This is on the iPhone, although there doesn't seem to be much difference in these classes.)
There's a note in section 10.3.2 of RFC 2616 about this behaviour:
Note: When automatically redirecting a POST request after
receiving a 301 status code, some existing HTTP/1.0 user agents
will erroneously change it into a GET request.
So this behaviour seems to be non-standard but historical. That GET request is not a POST, and it'll be missing the payload.
Interestingly enough, this is also in the same section:
If the 301 status code is received in response to a request other
than GET or HEAD, the user agent MUST NOT automatically redirect the
request unless it can be confirmed by the user, since this might
change the conditions under which the request was issued.
That's pretty clear and seems to indicate we can't fix this, but I think ignoring this for the purpose of our own web service clients for services we pick (or control) is probably the least bad alternative.
So how do we solve this?
Instead of the willSendResponse: in the original question, I'm using this:
- (NSURLRequest *)connection: (NSURLConnection *)connection
willSendRequest: (NSURLRequest *)request
redirectResponse: (NSURLResponse *)redirectResponse;
{
if (redirectResponse) {
// we don't use the new request built for us, except for the URL
NSURL *newURL = [request URL];
// Previously, store the original request in _originalRequest.
// We rely on that here!
NSMutableURLRequest *newRequest = [_originalRequest mutableCopy];
[newRequest setURL: newURL];
return newRequest;
} else {
return request;
}
}
The idea here is that instead of cloning the new request and trying to shape it the same as the one Cocoa Touch sends me, I create a clone of the original request and change just the URL to match the request Cocoa Touch sent me. That original request is still a POST with the payload attached.
If you control the server, it's worth reading RFC 2616, section 10.3 in its entirety to see if there's a better code you can use (while checking, of course, that iOS handles the better code as it should).
You could also make a mutable copy of the redirected request and replace its HTTP method with the HTTP method of the original request. Same general principle, though that would favour keeping things from the new request rather than the old. In some circumstances that might work better, but I haven't tested this yet.
You should be checking the HTTP response status code sent by the server to determine whether to send a GET or repeat the POST. For 303 (or 302), send a GET request. For 307, repeat the POST.
i had the same problem with redirecting.
Thanks to AJSoaks!
I tried as he suggested and the problem is resolved.
So, i was trying to post the username and password through the POST method, and i saw that server redirected my request. As AJSoaks says, in case if there is 302 error you should repeat the request but this time using GET method instead of previous POST.
... at some point you have the following lines:
... it can be inside if your IBAction (button pressed) method or wherever you want...
NSMutableString *postString = [[NSMutableString alloc] init];
[postString appendString:#"username=YourUsername&password=YourPassword"];
//the original URL (https means that it supports SSL protocol)
//it doesn't change anything, don't worry about it
NSURL *URL = [NSURL URLWithString:#"https://loginWebpageURL"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:URL];
[request setHTTPMethod:#"POST"];
[request setValue:[NSString stringWithFormat:#"%d", [postString length]] forHTTPHeaderField:#"Content-length"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-type"];
[request setHTTPBody:[postString dataUsingEncoding:NSUTF8StringEncoding]];
[NSURLConnection connectionWithRequest:request delegate:self];
[postString release];
[request release];
Than you should also implement the redirect NSURLConnection delegate method, with the following signature:
- (NSURLRequest *)connection:(NSURLConnection *)connection
willSendRequest:(NSURLRequest *)request
redirectResponse:(NSURLResponse *)redirectResponse
inside of this method, in case if you have SERVER's Error 302 or 303 you should implement something similar to the code bellow, just copy the code that you see and replace it with the new URL (redirected). The new URL you can see in the browser or if you want it is very useful, also in the future, checking it with Firebug (Firefox plugin) or Safari WEB INSPECTOR. If you use Firebug all information you can find under the "Net" option:
if (redirectResponse) {
NSLog(#"REDIRECT");
NSMutableURLRequest *requestTmp = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"https://areaclienti.tre.it/selfcare/areaclienti133/4552_infoCosti_ITA_HTML.xsl"]];
return [requestTmp autorelease];
}
//return original request in case thay there is no redirecting...
else return request;
NSURLConnection does not add the originalRequest headers into the redirected request in the "willSendRequest: (NSURLRequest *)inRequest".
You can workaround this problem by adding "originalRequest.headers" into the redirected request.
I'm working on an IPhone application that works with a Google App Engine application. I manage to get logged by using a google account and I get the authentication token. I'm also able to GET data from the GAE service (I did it after reading another question written here) but now I need to POST data so I need to send the authentication token in the header of the POST request. I tried several options but none of them worked.
Here is the code I use to put that auth into the header:
NSString* urlStr = [NSString stringWithFormat:#"%#%#", HOST, url];
NSMutableURLRequest* urlPost = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlStr]];
NSString* authStr = [NSString stringWithFormat:#"GoogleLogin auth=%#", token];
[urlPost addValue:authStr forHTTPHeaderField:#"Authorization"];
but it doesn't work.
Any help?
You need to use [request setHTTPMethod: #"POST"] and [request setHTTPBody: postdata] to properly configure the POST components. See the NSMutableURLRequest docs for more details.
Whenever I'm troubleshooting a problem related to HTTP, the first tool I'll grab is Charles HTTP Proxy. It will show you the entire request and response for closer examination.
If you're authenticating against an App Engine app, you need to obtain and send an authentication cookie, rather than using the GoogleLogin authentication. The source of appengine_rpc.py in the Python SDK demonstrates how.