Convert curl statement into Objective-C - ios

I’m trying to use CloudApp’s API (https://github.com/cloudapp/objective-c) to develop my app. I currently want to make it where the user can see the details of their account (e.g.: email, subscription details, etc.). Their API doesn’t seem to work properly to do that sort of thing, but their curl example works perfectly.
curl --digest -u dev2#trijstudios.ca:trij2323 \
-H "Accept: application/json" \
"http://my.cl.ly/account"
Which gets outputted to this:
{"created_at":"2015-01-11T21:08:56Z","domain":null,"domain_home_page":null,"email":"dev2#trijstudios.ca","id":1778166,"private_items":true,"updated_at":"2015-01-11T21:08:56Z","activated_at":"2015-01-11T21:08:56Z","subscribed":false,"socket":{"auth_url":"http://my.cl.ly/pusher/auth","api_key":"4f6dbc3b89fa4ee9a8ff","app_id":"4721","channels":{"items":"private-items_1778166"}},"subscription_expires_at":null}
I wanted to do something as much as the curl statement as possible. I looked around Google and StackOverflow and found this answer (Objective-C equivalent of curl request):
NSURL *url = [NSURL URLWithString: [NSString stringWithFormat:#"http://%#:%##www.example.com/myapi/getdata", API_USERNAME, API_PASSWORD]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setURL:url];
[request setHTTPMethod:#"GET"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
NSError *error;
NSURLResponse *response;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
I had to slightly modify it, though:
NSString *apiUserName = [NSString stringWithFormat:#"dev2#trijstudios.ca"];
NSString *apiPassword = [NSString stringWithFormat:#"trij2323"];
NSURL *url = [NSURL URLWithString: [NSString stringWithFormat:#"http://%#:%##my.cl.ly/account", apiUserName, apiPassword]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setURL:url];
[request setHTTPMethod:#"GET"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
NSError *error;
NSURLResponse *response;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSLog(#"%#", data);
But when I tried it, instead of getting the result of the curl statement in Terminal, I got this in the console:
<3c21646f 63747970 65206874 6d6c3e0a 0a3c6874 6d6c2078 6d6c6e73 3a6f673d 22687474 703a2f2f 6f70656e 67726170 6870726f 746f636f 6c2e6f72 672f7363 68656d61 2f222078 6d6c6e73 3a66623d 22687474 703a2f2f 7777772e 66616365 626f6f6b 2e636f6d 2f323030 382f6662 6d6c2220 6974656d 73636f70 65206974 656d7479 70653d22 68747470 3a2f2f73 6368656d 612e6f72 672f5468 696e6722 20636c61 73733d22 73717561 72657370 6163652d 64616d61 736b2220 6c616e67 3d22656e 2d434122 3e0a0a20 203c6865 61643e0a 20202020 0a202020 203c6d65 74612063 68617273 65743d22 7574662d 38223e0a 20202020 3c6d6574 61206874 74702d65 71756976 3d22582d 55412d43 6f6d7061 7469626c 65222063 6f6e7465 6e743d22 49453d65 6467652c
…
61676522 297c7c68 61734174 74722861 5b625d2c 22646174 612d7372 63222929 26262266 616c7365 22213d3d 67657441 74747228 615b625d 2c226461 74612d6c 6f616422 292b2222 2626496d 6167654c 6f616465 722e6c6f 61642861 5b625d29 7d696e69 7428293b 77696e64 6f772e59 55492626 5955492e 61646428 22737175 61726573 70616365 2d696d61 67656c6f 61646572 222c6675 6e637469 6f6e2861 297b7d29 3b0a7d29 28293b3c 2f736372 6970743e 0a3c7363 72697074 3e537175 61726573 70616365 2e616674 6572426f 64794c6f 61642859 293b3c2f 73637269 70743e0a 0a0a2020 20200a20 203c2f62 6f64793e 0a0a3c2f 68746d6c 3e200a>
(There’s a lot more, but I won’t make it long. Let me know if you need to see the full version.)
I’m not too sure what to do from here. Is there anyone that knows how to fix this? Thanks in advance.

A couple of thoughts
Your response is HTML:
<!doctype html>
<html xmlns:og="http://opengraphprotocol.org/schema/" xmlns:fb="http://www.facebook.com/2008/fbml" itemscope itemtype="http://schema.org/Thing" class="squarespace-damask" lang="en-CA">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,
...
age")||hasAttr(a[b],"data-src"))&&"false"!==getAttr(a[b],"data-load")+""&&ImageLoader.load(a[b])}init();window.YUI&&YUI.add("squarespace-imageloader",function(a){});
})();</script>
<script>Squarespace.afterBodyLoad(Y);</script>
</body>
</html>
This generally means that there was some problem in the request (but without seeing the full text of the HTML, it's hard to say precisely what's wrong).
Your curl specified an Accept header of application/json, but the Objective-C example used that value for the Content-Type header. The request isn't JSON (in this case, at least), so I suspect you meant to set the Accept header in your Objective-C code, as in the curl, not the Content-Type header.
Your curl request specified "digest" authentication. The CloudApp's documentation also says it uses digest authentication. But the Objective-C code is not doing any authentication.
You are performing synchronous network request. You never want to perform synchronous requests from the main thread.
You'll probably want to perform your request using NSURLSession (or if you need to support iOS versions prior to 7.0, NSURLConnection). This solves both points three and four, where you can perform authentication, as well perform the request asynchronously. For example, with NSURLSession, you can use the authentication delegate method, while still enjoying the elegance of the completion block pattern for the request, itself:
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
{
if (challenge.previousFailureCount == 0) {
NSURLCredential *credential = [NSURLCredential credentialWithUser:self.userid password:self.password persistence:NSURLCredentialPersistenceForSession];
completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
} else {
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
}
}
// you might have to implement the session rendition of the above authentication routine; it depends upon your server configuration; the implementation will look almost identical to the above code
//
// - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
- (void)performQuery {
// note, no user credentials in the URL; will be handled by authentication delegate method
NSURL *url = [NSURL URLWithString: #"http://www.example.com/myapi/getdata"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"GET"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:[NSOperationQueue mainQueue]];
NSURLSessionTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (!data) {
NSLog(#"dataTaskWithURL error: %#", error);
return;
}
NSError *parseError;
id responseObject = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
if (responseObject) {
// got the JSON I was expecting; go ahead and use it; I'll just log it for now
NSLog(#"responseObject = %#", responseObject);
} else {
// if it wasn't JSON, it's probably some error, so it's sometimes useful to see what the HTML actually says
NSLog(#"responseString = %#", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}
}];
[task resume];
}

Related

Send email in Gmail API - Objective C

We are working on an iOS project that involves sending emails through the Gmail API and we are having trouble finding documentation on how to actually do this.
First, we haven't completely figured out authentication. We are using AppAuth to handle that, and it's worked pretty well so far, but we are not quite sure how to link that up to the Gmail API in our code.
Second, how do we send the message itself? We have the content and everything formatted, we just can't figure out how to actually send the message. All we are looking to do is send a simple message to a specified email address from the user's own email account; no attachments or anything like that. We have seen a couple swift examples, however we would prefer to use Objective C. Any ideas on how we could do this?
Update:
After playing around with things a bit more, we found another way to connect to Gmail. Instead of using the classes from the Google API Objective C Client for REST, we are simply trying to send the email using an HTTP POST method. This appears to be way easier than dealing with all of the errors we were getting before. The only problem we have now is that we still can't quite send messages. With nearly everything we've tried, the API just creates an empty message and puts it in our Sent mailbox; that's it. Here's what we have right now:
- (void)sendEmail{
NSURL *userinfoEndpoint = [NSURL URLWithString:#"https://www.googleapis.com/upload/gmail/v1/users/TEST_USERNAME/messages/send?uploadType=media"];
NSString *currentAccessToken = _authState.lastTokenResponse.accessToken;
[self logMessage:#"Trying to authenticate...."];
// Handle refreshing tokens
NSString *message = [NSString stringWithFormat:#"{\"raw\": \"%#\"}",[self generateMessage]];
NSLog(#"%#", message);
// creates request to the userinfo endpoint, with access token in the Authorization header
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:userinfoEndpoint];
NSString *authorizationHeaderValue = [NSString stringWithFormat:#"Bearer %#", accessToken];
[request addValue:authorizationHeaderValue forHTTPHeaderField:#"Authorization"];
[request setHTTPMethod:#"POST"];
[request setValue:#"message/rfc822" forHTTPHeaderField:#"Content-Type"];
[request setValue:[NSString stringWithFormat:#"%lu", (unsigned long)[message length]] forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody:[message dataUsingEncoding:NSUTF8StringEncoding];
NSURLSessionConfiguration *configuration =
[NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
delegate:nil
delegateQueue:nil];
// performs HTTP request
NSURLSessionDataTask *postDataTask =
[session dataTaskWithRequest:request
completionHandler:^(NSData *_Nullable data,
NSURLResponse *_Nullable response,
NSError *_Nullable error) {
// Handle response
}];
[postDataTask resume];
}];
}
- (NSString *)generateMessage{
NSString *message = [NSString stringWithFormat:#"From: <TEST_USER#domain.com>\nTo: <TEST_USER#domain.com>\nSubject: Test\n\nThis is a test"];
NSString *rawMessage = [message stringByReplacingOccurrencesOfString:#"\\n" withString:#"\n"];
NSData *encodedMessage = [rawMessage dataUsingEncoding:NSUTF8StringEncoding];
NSString *encoded = [encodedMessage base64EncodedStringWithOptions:0];
NSLog(#"%#", encoded);
return encoded;
}
We have tested the encoding part and it is making a proper base64 string, however after that point, something clearly is not formatted right or something. We get a confirmation that the message was successfully created, however all the API does is create an empty email with no recipient, subject, or body. Any ideas on what we could do to get this to work?
I'm not an expert in this but I remembered we have done something similar in the past. Follow the instructions at the following link and make sure that you select the proper option in Gmail API wizard
https://developers.google.com/gmail/api/quickstart/ios?ver=objc
I hope you find this helpful
After numerous experimentations, here is the code that seems to finally work for me, i worked it off your example above.
1st you need to create google project in dev console, get its Client ID and Api-Key(this may not be necessary) and implement Google SignIn in AppDelegete in - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions method:
[GIDSignIn sharedInstance].clientID = #"your proj client id here";
[GIDSignIn sharedInstance].delegate = self;
[GIDSignIn sharedInstance].scopes=[NSArray arrayWithObjects:#"https://www.googleapis.com/auth/gmail.send",#"https://www.googleapis.com/auth/gmail.readonly",#"https://www.googleapis.com/auth/gmail.modify", nil];
Now sending emails:
// refresh token
appDelegate.delAuthAccessToken=#"";
[[GIDSignIn sharedInstance] signInSilently];
NSDate *timeStart = [NSDate date];
NSTimeInterval timeSinceStart=0;
while([appDelegate.delAuthAccessToken isEqualToString:#""] && timeSinceStart<10){//wait for new token but no longer than 10s should be enough
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:[NSDate dateWithTimeIntervalSinceNow:1.0f]];//1sec increment actually ~0.02s
timeSinceStart = [[NSDate date] timeIntervalSinceDate:timeStart];
}
if (timeSinceStart>=10) {//timed out
return;
}
//compose rfc2822 message AND DO NOT base64 ENCODE IT and DO NOT ADD {raw etc} TOO, put 'To:' 1st, add \r\n between the lines and double that before the actual text message
NSString *message = [NSString stringWithFormat:#"To: %#\r\nFrom: %#\r\nSubject: EzPic2Txt\r\n\r\n%#", appDelegate.delToEmails, appDelegate.delAuthUserEmail, appDelegate.delMessage];
NSURL *userinfoEndpoint = [NSURL URLWithString:#"https://www.googleapis.com/upload/gmail/v1/users/me/messages/send?uploadType=media"];
NSLog(#"%#", message);
//create request
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:userinfoEndpoint];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:[message dataUsingEncoding:NSUTF8StringEncoding]];//message is plain UTF8 string
//add all headers into session config, maybe ok adding to request too
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
configuration.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
configuration.HTTPAdditionalHeaders = #{
#"api-key" : #"api-key here, may not need it though",
#"Authorization" : [NSString stringWithFormat:#"Bearer %#", appDelegate.delAuthAccessToken],
#"Content-type" : #"message/rfc822",
#"Accept" : #"application/json",
#"Content-Length": [NSString stringWithFormat:#"%lu", (unsigned long)[message length]]
};
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
// performs HTTP request
NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request
completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
// Handle response
}];
[postDataTask resume];
Hope it helps somebody
In my app I used to be able to use MailCore2 but it got blocked by Google (I got access denied when I switched to permitted send, readonly and modify scopes) since MailCore2 works only with FULL permissions. Google allowed to use ONLY send, readonly and modify scopes. There is no guide lines how to use their "great restful api" with Gmail in iOS though, so it seems like HTTP POST is the last resort until they shut it down too.
I cannot have my app to be deemed by Google as insecure. If you are OK with that you can still use MailCore2, no problem.
Receiving email with HTTP GET:
1st get up to 20 unread messages ids:
//get IDs of no more than 20 unread messages
//in query you can add extra filters, say messages only from specific emails
NSString *query=#"from:aaa#gmail.com|from:bbb#yahoo.com";
NSString *tmpStr=[NSString stringWithFormat:#"https://www.googleapis.com/gmail/v1/users/me/messages?maxResults=20&q=\"is:unread\" \"%#\"",query];
NSString *tmpStrURL=[tmpStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *userinfoEndpoint = [NSURL URLWithString:tmpStrURL];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:userinfoEndpoint];
[request setHTTPMethod:#"GET"];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
configuration.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
configuration.HTTPAdditionalHeaders = #{#"api-key" : #"your api key here",
#"Authorization" : [NSString stringWithFormat:#"Bearer %#", yourTokenHere],
#"Accept" : #"application/json"
};
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
// performs HTTP request
NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request
completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
// Handle response
if (!error){
NSMutableDictionary *jsondata = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
long jsonMsgsCnt = [[jsondata valueForKey:#"resultSizeEstimate"] longValue];
if(jsonMsgsCnt>0){
NSMutableArray *jsonMsgs = [jsondata objectForKey:#"messages"];
for (NSMutableDictionary *tmp in jsonMsgs){
[delMsgsReceived addObject:[tmp objectForKey:#"id"]];
}
}
NSLog(#"retrieve Email Id postDataTask n msg:%li",delMsgsReceived.count);
}else{
NSLog(#"retrieve Email Id postDataTask error:%#",error.description);
}
}];
[postDataTask resume];
Now delMsgsReceived contains messagesIds. Process them to get actual emails one by one:
NSString *tmpStr=[NSString stringWithFormat:#"https://www.googleapis.com/gmail/v1/users/me/messages/%#?format=full", msgId];//supply message id here
NSString *tmpStrURL=[tmpStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *userinfoEndpoint = [NSURL URLWithString:tmpStrURL];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:userinfoEndpoint];
[request setHTTPMethod:#"GET"];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
configuration.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
configuration.HTTPAdditionalHeaders = #{
#"api-key" : #"your api key",
#"Authorization" : [NSString stringWithFormat:#"Bearer %#", your auth token],
#"Accept" : #"application/json"
};
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
// performs HTTP request
NSURLSessionDataTask *postDataTask =
[session dataTaskWithRequest:request
completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
// Handle response
if (!error){
NSMutableDictionary *jsondata = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
NSString *body=[jsondata objectForKey:#"snippet"];//not full msg!
//for full message get the whole payload and extract what you need from there NSMutableArray *jsonPayload = [[jsondata objectForKey:#"payload"] objectForKey:#"headers"];
}else{
//deal with error
NSLog(#"retrieving message error:%#",error.description);
}
}];
[postDataTask resume];

How to send an asynchronous post request in iOS

I need some help with a LoginViewController.
Basically I have a small app, and I need to post some data to the app and Im new to POST and JSON. If I can get some help and understanding that would be highly appreciated. Below are some requirements im working with. My .m file is labled as LoginViewController. This is what I have so far
-(void)setRequest {
#pragma mark NSURLConnection Delegate Methods
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
// A response has been received, this is where we initialize the instance var you created
// so that we can append data to it in the didReceiveData method
// Furthermore, this method is called each time there is a redirect so reinitializing it
// also serves to clear it
_responseData = [[NSMutableData alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// Append the new data to the instance variable you declared
[_responseData appendData:data];
}
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse*)cachedResponse {
// Return nil to indicate not necessary to store a cached response for this connection
return nil;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// The request is complete and data has been received
// You can parse the stuff in your instance variable now
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
// The request has failed for some reason!
// Check the error var
}
-(void)PostRequest{
// Create the request.
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"http://dev.apppartner.com/AppPartnerProgrammerTest/scripts/login.php"]];
// Specify that it will be a POST request
request.HTTPMethod = #"POST";
// This is how we set header fields
[request setValue:#"application/xml; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
// Convert your data and set your request's HTTPBody property
NSString *stringData = #"some data";
NSData *requestBodyData = [stringData dataUsingEncoding:NSUTF8StringEncoding];
request.HTTPBody = requestBodyData;
// Create url connection and fire request
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}
}
I dont know if I'm even setting this up right. I saw many hTTP posts and what not, but im still confused on how I write this syntax and do I need to add anything additional.
I need to:
Send an asynchronous POST request to "some url"
The POST request must contain the parameters 'username' and 'password'
Will receive a JSON response back with a 'code' and a 'message'
Display the parsed code and message in a UIAlert along with how long the api call took in miliseconds
The only valid login is username: Super password: qwerty
When a login is successful, tapping 'OK' on the UIAlert should bring us back to the MainMenuViewController
I'm assuming the methods inside methods are a typo.
Unless you have a particular reason to implement all those delegate methods, you're probably better off using either
NSURLSessionDataTask *task =
[[NSURLSession sharedSession] dataTaskWithRequest:request
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
// Code to run when the response completes...
}];
[task resume];
or the equivalent using NSURLConnection's sendAsynchronousRequest:queue:completionHandler: method if you still need to support iOS 6 and earlier and/or OS X v10.8 and earlier.
But the big thing you're missing is the encoding of the request body. To do that, you'll probably want to use URL encoding and specify the appropriate MIME type for that as shown here:
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/URLLoadingSystem/WorkingwithURLEncoding/WorkingwithURLEncoding.html
Basically, you construct a string by string concatenation in the form "user=ENCODEDUSERNAME&pass=ENCODEDPASSWORD" where the two encoded values are constructed like this:
NSString *encodedString = (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(
kCFAllocatorDefault,
(__bridge NSString *)originalString,
NULL,
CFSTR(":/?#[]#!$&'()*+,;="),
kCFStringEncodingUTF8);
Do not be tempted to use stringByAddingPercentEscapesUsingEncoding: and friends. They will do the wrong thing if your strings contain certain reserved URL characters.
I would suggest that you try working with AFNetworking Library.
You can find the code here.
And a very good tutorial here.
You can do like that for this.
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request addValue:#"YourUsername" forHTTPHeaderField:#"Username"];
[request addValue:#"YourPassword" forHTTPHeaderField:#"Password"];
[NSURLConnection
sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
// TODO: Handle/Manage your response ,Data & errors
}];
-(IBAction)registerclick:(id)sender
{
if (_password.text==_repassword.text)
{
[_errorlbl setHidden:YES];
NSString *requstUrl=[NSString stringWithFormat:#"http://irtech.com/fresery/index.php?route=api/fresery/registerCustomer"];
NSString *postString=[NSString stringWithFormat:#"name=asd&email=sooraj&phonenumber=8111&password=soorajsnr&type=1&facebookid=&image_path="];
// _name.text,_email.text,_mobile.text,_password.text
NSData *returnData=[[NSData alloc]init];
NSMutableURLRequest *request=[[NSMutableURLRequest alloc]initWithURL:[NSURL URLWithString:requstUrl]];
[request setHTTPMethod:#"POST"];
[request setValue:[NSString stringWithFormat:#"%lu", (unsigned long)[postString length]] forHTTPHeaderField:#"Content-length"];
[request setHTTPBody:[postString dataUsingEncoding:NSUTF8StringEncoding]];
returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
resp=[NSJSONSerialization JSONObjectWithData:returnData options:NSJSONReadingMutableContainers error:nil];
c=[[resp valueForKey:#"status" ]objectAtIndex:0];
b=[[resp valueForKey:#"message"]objectAtIndex:0];

returning a value from asynchronous call using semaphores

I need to use NSURLSession to make network calls. On the basis of certain things, after I receive the response, I need to return an NSError object.
I am using semaphores to make the asynchronous call behave synchronously.
The problem is, the err is set properly inside call, but as soon as semaphore ends (after
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
), the err becomes nil.
Please help
Code:
-(NSError*)loginWithEmail:(NSString*)email Password:(NSString*)password
{
NSError __block *err = NULL;
// preparing the URL of login
NSURL *Url = [NSURL URLWithString:urlString];
NSData *PostData = [Post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
// preparing the request object
NSMutableURLRequest *Request = [[NSMutableURLRequest alloc] init];
[Request setURL:Url];
[Request setHTTPMethod:#"POST"];
[Request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[Request setHTTPBody:PostData];
NSMutableDictionary __block *parsedData = NULL; // holds the data after it is parsed
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
config.TLSMinimumSupportedProtocol = kTLSProtocol11;
NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:nil delegateQueue:nil];
NSURLSessionDataTask *task = [session dataTaskWithRequest:Request completionHandler:^(NSData *data, NSURLResponse *response1, NSError *err){
if(!data)
{
err = [NSError errorWithDomain:#"Connection Timeout" code:200 userInfo:nil];
}
else
{
NSString *formattedData = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"%#", formattedData);
if([formattedData rangeOfString:#"<!DOCTYPE"].location != NSNotFound || [formattedData rangeOfString:#"<html"].location != NSNotFound)
{
loginSuccessful = NO;
//*errorr = [NSError errorWithDomain:#"Server Issue" code:201 userInfo:nil];
err = [NSError errorWithDomain:#"Server Issue" code:201 userInfo:nil];
}
else
{
parsedData = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&err];
NSMutableDictionary *dict = [parsedData objectForKey:#"User"];
loginSuccessful = YES;
}
dispatch_semaphore_signal(semaphore);
}];
[task resume];
// but have the thread wait until the task is done
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return err;
}
Rob's answer tells you how to do it right, but not what mistake you made:
You have two variables named err, which are totally unrelated. It seems that you haven't turned on some important warnings, otherwise your code wouldn't even have compiled.
The parameter err that is passed to your completion block is the error from the URL request. You replace it without thinking with a timeout error - so the true error is now lost. Consider that timeout is not the only error.
But all the errors that you set only set the local variable err which was passed to you in the completion block; they never touch the variable err in the caller at all.
PS. Several serious errors in your JSON handling. JSON can come in UTF-16 or UTF-32, in which case formattedData will be nil and you incorrectly print "Server Issue". If the data isn't JSON there is no guarantee that it contains DOCTYPE or html, that test is absolute rubbish. Your user with the nickname JoeSmith will hate you.
Passing NSJSONReadingAllowFragments to NSJSONSerialization is nonsense. dict is not mutable; if you try to modify it your app will crash. You don't check that the parser returned a dictionary, you don't check that there is a value for the key "User", and you don't check that the value is a dictionary. That's lots of ways how your app can crash.
I would suggest cutting the Gordian knot: You should not use semaphores to make an asynchronous method behave synchronously. Adopt asynchronous patterns, e.g. use a completion handler:
- (void)loginWithEmail:(NSString *)email password:(NSString*)password completionHandler:(void (^ __nonnull)(NSDictionary *userDictionary, NSError *error))completionHandler
{
NSString *post = ...; // build your `post` here, making sure to percent-escape userid and password if this is x-www-form-urlencoded request
NSURL *url = [NSURL URLWithString:urlString];
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"POST"];
// [request setValue:postLength forHTTPHeaderField:#"Content-Length"]; // not needed to set length ... this is done for you
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"]; // but it is best practice to set the `Content-Type`; use whatever `Content-Type` appropriate for your request
[request setValue:#"text/json" forHTTPHeaderField:#"Accept"]; // and it's also best practice to also inform server of what sort of response you'll accept
[request setHTTPBody:postData];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
config.TLSMinimumSupportedProtocol = kTLSProtocol11;
NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:nil delegateQueue:nil];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *err) {
if (!data) {
dispatch_async(dispatch_get_main_queue(), ^{
completionHandler(nil, [NSError errorWithDomain:#"Connection Timeout" code:200 userInfo:nil]);
});
} else {
NSError *parseError;
NSDictionary *parsedData = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&parseError];
dispatch_async(dispatch_get_main_queue(), ^{
if (parsedData) {
NSDictionary *dict = parsedData[#"User"];
completionHandler(dict, nil);
} else {
completionHandler(nil, [NSError errorWithDomain:#"Server Issue" code:201 userInfo:nil]);
}
});
}
}];
[task resume];
}
And then call it like so:
[self loginWithEmail:userid password:password completionHandler:^(NSDictionary *userDictionary, NSError *error) {
if (error) {
// do whatever you want on error here
} else {
// successful, use `userDictionary` here
}
}];
// but don't do anything reliant on successful login here; put it inside the block above
Note:
I know you're going to object to restoring this back to asynchronous method, but it's a really bad idea to make this synchronous. First it's a horrible UX (the app will freeze and the user won't know if it's really doing something or whether it's dead) and if you're on a slow network you can have all sorts of problems (e.g. the watchdog process can kill your app if you do this at the wrong time).
So, keep this asynchronous. Ideally, show UIActivityIndicatorView before starting asynchronous login, and turn it off in the completionHandler. The completionHandler would also initiate the next step in the process (e.g. performSegueWithIdentifier).
I don't bother testing for HTML content; it is easier to just attempt parse JSON and see if it succeeds or not. You'll also capture a broader array of errors this way.
Personally, I wouldn't return my own error objects. I'd just go ahead and return the error objects the OS gave to me. That way, if the caller had to differentiate between different error codes (e.g. no connection vs server error), you could.
And if you use your own error codes, I'd suggest not varying the domain. The domain should cover a whole category of errors (e.g. perhaps one custom domain for all of your app's own internal errors), not vary from one error to another. It's not good practice to use the domain field for something like error messages. If you want something more descriptive in your NSError object, put the text of the error message inside the userInfo dictionary.
I might suggest method/variable names to conform to Cocoa naming conventions (e.g. classes start with uppercase letter, variables and method names and parameters start with lowercase letter).
There's no need to set Content-Length (that's done for you), but it is good practice to set Content-Type and Accept (though not necessary).
You need to let the compiler know that you will be modifying err. It needs some special handling to preserve that beyond the life of the block. Declare it with __block:
__block NSError *err = NULL;
See Blocks and Variables in Blocks Programming Topics for more details.

Wait until NSURLConnection sendAsynchronousRequest is finished

I have the following problem. I have a Model, called User. When the user now logins with Facebook, my app checks if the user exists already in the database. To not freeze the UI (since I'm coming from Android) I thought to use NSURLConnection sendAsynchronousRequest. What worked at first was the following:
My User Model had a method to do the whole task of the AsynchronousRequest and then when finished would set a variable to loading. Then other classes, could simply check with
while ( !user.loading ) if the Request was finished or not. The problem that came here to me, was, that now, I had to put this method in every Model. So instead of this, I created a new Class HTTPPost. This class now has the method that gets an NSDictionary passed and returns one. This works ALMOST. The problem I was now encountering is, that I couldn't really determine if the process was finished or not. So I started to create a new class called Globals and use global Variable loading. But the global variable is ALWAYS NO. So, what would be the best way to do this?
Here is my code:
This is where I check for the user and load it. resultDictionary is the NSDictionary where everything gets loaded in, but is always nil
[user loadModelFrom:[NSString stringWithFormat:#"WHERE facebookId='%#'", graphUser.id]];
NSLog(#"%#", user.resultDictionary);
if ( user.resultDictionary == nil ) {
NSLog(#"NIL");
} else {
NSLog(#"NOT NIL");
}
The problem now, is, that, since I'm sending an AsynchronousRequest, the resultDictionary is always nil. What I did before and worked was the following.
In my Model I had the HTTP Request and a variable named loading. Now I set loading to false until the response has been made into a NSDictionary
returnDict = [NSJSONSerialization JSONObjectWithData: [responseBody dataUsingEncoding:NSUTF8StringEncoding]
options: NSJSONReadingMutableContainers
error: &error];
But, then I had another problem. I had to do this in all my Models again... So I created a new Class that subclasses NSObject, that has the asynchronousRequest. This is the whole request
-(NSDictionary *)doHttpRequest:(NSDictionary *)postDict{
loading = NO;
__block NSDictionary *returnDict;
NSError *error;
NSString *jsonString;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:postDict
options:NSJSONWritingPrettyPrinted // Pass 0 if you don't care about the readability of the generated string
error:&error];
if (! jsonData) {
NSLog(#"Got an error: %#", error);
} else {
jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
NSURL *aUrl = [NSURL URLWithString:#"http://xx.xx-xx.xx/xx.xx"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:aUrl
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSString *authStr = [NSString stringWithFormat:#"%#:%#", #"xx", #"xx"];
NSData *authData = [authStr dataUsingEncoding:NSASCIIStringEncoding];
NSString *authValue = [NSString stringWithFormat:#"Basic %#", [authData base64EncodedString]];
[request setValue:authValue forHTTPHeaderField:#"Authorization"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-type"];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:[jsonString dataUsingEncoding:NSUTF8StringEncoding]];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSString *responseBody = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
returnDict = [NSJSONSerialization JSONObjectWithData: [responseBody dataUsingEncoding:NSUTF8StringEncoding]
options: NSJSONReadingMutableContainers
error: &error];
}];
[queue waitUntilAllOperationsAreFinished];
loading = YES;
return returnDict;
}
As you can see I have now a variable called loading. It is a global variable. But somehow, the variable is always NO.
What would be the best way to do this? I hope I'm understandable, I'm new to Objective-C, and English isn't my native language.
UPDATE
I modified the code to look like a user provided here, but still not working!
HTTPPost.h
-(void)doHttpRequest:(NSDictionary *)postDict completion:(void(^)(NSDictionary *dict, NSError *error))completion {
__block NSDictionary *returnDict;
NSError *error;
NSString *jsonString;
NSString *authValue;
NSString *authStr;
NSData *jsonData;
NSData *authData;
NSURL *aUrl;
NSMutableURLRequest *request;
NSOperationQueue *queue;
jsonData = [NSJSONSerialization dataWithJSONObject:postDict
options:NSJSONWritingPrettyPrinted
error:&error];
if (! jsonData) {
NSLog(#"Got an error: %#", error);
} else {
jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
aUrl = [NSURL URLWithString:#"http://xx.xx-xx.com/xx.php"];
request = [NSMutableURLRequest requestWithURL:aUrl
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
queue = [[NSOperationQueue alloc] init];
authStr = [NSString stringWithFormat:#"%#:%#", #"xx", #"xx"];
authData = [authStr dataUsingEncoding:NSASCIIStringEncoding];
authValue = [NSString stringWithFormat:#"Basic %#", [authData base64EncodedString]];
[request setValue:authValue forHTTPHeaderField:#"Authorization"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-type"];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:[jsonString dataUsingEncoding:NSUTF8StringEncoding]];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSString *responseBody = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
returnDict = [NSJSONSerialization JSONObjectWithData: [responseBody dataUsingEncoding:NSUTF8StringEncoding]
options: NSJSONReadingMutableContainers
error: &error];
if ( completion ) {
completion(returnDict, error);
}
}];
}
//User.h
[_httpPost doHttpRequest:_dbDictionary completion:^(NSDictionary *dict, NSError *error) {
NSLog(#"completed") // NEVER GETS FIRED
}];
It seems that you're trying to take an asynchronous process (sendAsynchronousRequest) , and make it behave like a synchronous process (i.e. you appear to want to wait for it). You should not do that. You should to embrace the asynchronous patterns rather than fighting them.
The sendAsynchronousRequest method has a completion block that specifies what you want to do when the request is done. Do not try to put the code after the block and (try to) wait for the block to complete, but rather put any of your code that is dependent upon the completion of the network request inside the completion block, or have the completion block call your code.
A common way would be to give your own methods their own completion blocks and then call those blocks in the completionHandler of sendAsynchronousRequest, something like:
- (void)performHttpRequest:(NSDictionary *)postDict completion:(void (^)(NSDictionary *dictionary, NSError *error))completion
{
// prepare the request
// now issue the request
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (error) {
if (completion)
completion(data, error);
} else {
NSString *responseBody = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
returnDict = [NSJSONSerialization JSONObjectWithData:data
options: NSJSONReadingMutableContainers
error: &error];
if (completion)
completion(returnDict, error);
}];
}
Now, when you want to perform your request, you simply do:
[self performHttpRequest:someDictionary completion:^(NSDictionary *dictionary, NSError *error) {
if (error) {
// ok, handle the error here
} else {
// ok, use the `dictionary` results as you see fit here
}
];
Note, the method that calls this performHttpRequest (let's imagine you called it from loadModelFrom ) now behaves asynchronously, itself. So you might want to employ this completion-block pattern again, e.g. adding your own completion block parameter to loadModelFrom, and then invoke that block in the completion handler loadModelFrom passes to performHttpRequest.
But hopefully you get the idea: Never try to wait for a completion block, but rather just put inside that block anything you want it to do when its done. Whether you use AFNetworking (which I'd advise), or continue to use sendAsynchronousRequest, this is a very useful pattern with which you should become familiar.
Update:
The revised code sample (largely) works great for me. Seeing your revised question, a couple of observations:
I am not familiar with this base64EncodedString method. In iOS 7, there is the native base64EncodedStringWithOptions method (or for earlier iOS versions use base64Encoding). Or are you using a third party base-64 NSData category?
There's no point in creating jsonString, only to then convert it back to a NSData. Just use jsonData in your request.
The same is true with responseBody: Why convert to string only to convert back to NSData?
There's no point in having returnDict to be defined as __block outside the sendAsynchronousRequest block. Just define it inside that block and the __block qualifier is then no longer necessary.
Why create a NSOperationQueue for the completionHandler of sendAsynchronousRequest? Unless I'm doing something really slow that merits running on a background queue, I just use [NSOperationQueue mainQueue], because you invariably want to update the app's model or UI (or both), and you want to do that sort of stuff on the main queue.
The request still runs asynchronously but the queue parameter just specifies which queue the completion block will run on.
By the way, in sendAsynchronousRequest, you aren't checking to see if the request succeeded before proceeding with JSONObjectWithData. If the request failed, you could theoretically be losing the NSError object that it returned. You really should check to make sure the request succeeded before you try to parse it.
Likewise, when you originally dataWithJSONObject the parameters in postDict, you really should check for success, and if not, report the error and quit.
I notice that you're using the NSJSONReadingMutableContainers option. If you really need a mutable response, I'd suggest making that explicit in your block parameters (replacing all the NSDictionary references with NSMutableDictionary). I assume you don't really need it to be mutable, so I therefore recommend removing the NSJSONReadingMutableContainers option.
Likewise, when creating the JSON, you don't need to use the NSJSONWritingPrettyPrinted option. It only makes the request unnecessary larger.
Combining all of this, that yields:
-(void)performHttpRequest:(NSDictionary *)postDict completion:(void(^)(NSDictionary *dict, NSError *error))completion {
NSError *error;
NSString *authValue;
NSString *authStr;
NSData *jsonData;
NSData *authData;
NSURL *aUrl;
NSMutableURLRequest *request;
jsonData = [NSJSONSerialization dataWithJSONObject:postDict options:0 error:&error];
if (!jsonData) {
if (completion)
completion(nil, error);
return;
}
aUrl = [NSURL URLWithString:#"...."];
request = [NSMutableURLRequest requestWithURL:aUrl
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
authStr = [NSString stringWithFormat:#"%#:%#", #"xx", #"xx"];
authData = [authStr dataUsingEncoding:NSASCIIStringEncoding];
if ([authData respondsToSelector:#selector(base64EncodedStringWithOptions:)])
authValue = [NSString stringWithFormat:#"Basic %#", [authData base64EncodedStringWithOptions:0]];
else
authValue = [NSString stringWithFormat:#"Basic %#", [authData base64Encoding]]; // if only supporting iOS7+, you don't need this if-else logic and you can just use base64EncodedStringWithOptions
[request setValue:authValue forHTTPHeaderField:#"Authorization"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-type"];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:jsonData];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
if (!data) {
if (completion)
completion(nil, error);
return;
}
NSError *parseError = nil;
NSDictionary *returnDict = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
if (completion) {
completion(returnDict, parseError);
}
}];
}
And if this is being called from another method that needs to handle the fact that this is happening asynchronously, then it would employ a completion block pattern, too:
- (void)authenticateUser:(NSString *)userid password:(NSString *)password completion:(void (^)(BOOL success))completion
{
NSDictionary *dictionary = #{ ... };
[self performHttpRequest:dictionary completion:^(NSDictionary *dict, NSError *error) {
if (error) {
completion(NO);
return;
}
// now validate login by examining resulting dictionary
BOOL success = ...;
// and call this level's completion block
completion(success);
}];
}
Then the view controller might access that method with something like:
// maybe add UIActivityIndicatorView here
[self.userModel authenticateUser:self.userTextField.text password:self.passwordTextField.text completion:^(BOOL success) {
// remove UIActivityIndicatorView here
if (success) {
// do whatever you want if everything was successful, maybe segue to another view controller
} else {
// show the user an alert view, letting them know that authentication failed and let them try again
}
}];
After seeing you adding specific code to handle request and its responses, I would point out that you should try using AFNetworking. It abstracts out lots of boiler plate code.
As you mentioned, you are new to obj-c, it may take some time to understand AFNetworking but in long run, it will save you lots of headache. Plus it is one of the widely used open source for network related stuff.
I hope this would be helpful.
If you want to wait for a request, then you should not use sendAsynchronousRequest.
Use sendSynchonousRequest instead. That's where it's made for:
NSURLResponse *response;
NSError * error;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
But, the UI is blocked when the synchronous call is made. I doubt if that is what you want.

iOS: using async

I currently have a screen with 2 tables. I'm getting the data synchronously and putting it on the screen. Code looks something like:
viewController.m
DBAccess_Error_T = [getList:a byCompanyID:1];
DBAccess_Error_T = [getList:b byCompanyID:2];
[self putListAOnScreen];
[self putListBOnScreen];
DBAccess.m
+ (DBAccess_Error_T)getList:(NSMutableArray*)a byCompanyID:(NSInteger)cID
{
// Pack this up in JSON form
[self queryDB:postData];
// Unpack and put it into variable a
}
+ (id)queryDB:(id)post
{
// Send request
// Get back data
}
I'm now trying to switch this over to async and I'm struggling. It's been hard even with website tutorials and documentations.
Since all of my database utilities are in separate files from the viewControllers, I'm not sure how I can use the didReceiveData and didReceiveResponse handlers. Also, since I have 2 arrays to fill for my 2 tables, how do I distinguish the difference in didReceiveData?
Instead, what I'm trying to do now is use sendAsynchronousRequest, but it seems I need to create an unpack function for every send function...let me know if I'm way off here...it looks something like:
viewController.m stays the same
DBAccess.m
+ (DBAccess_Error_T)getList:(NSMutableArray*)a byCompanyID:(NSInteger)cID
{
NSDictionary *post = /*blah blah*/
[self queryDB:post output:(a)];
}
+ (id)queryDB:(id)post output:(id)output
{
NSError *error;
NSData *jsonPayload = [NSJSONSerialization dataWithJSONObject:post options:NSJSONWritingPrettyPrinted error:&error];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
[theRequest setHTTPMethod:#"POST"];
[theRequest setHTTPBody:jsonPayload];
[NSURLConnection sendAsynchronousRequest:request
queue:[[NSOperationQueue alloc] init]
completionHandler:^(NSURLResponse *response,
NSData *data,
NSError *error)
{
if ([data length] >0 && error == nil)
{
[self unpackDataForList:output data:data]; // This function needs to be different depending on which function called queryDB...the data will be unpacked in a different way
}
}
}
+ (void)unpackDataForList:(id)output data:(NSData*)data
{
// Do my unpacking here and stick it into 'output'.
}
How can I call a different unpackData function? are function pointers the right way to do this? Is this approach way off? Any tips would be greatly appreciated!
Have you ever looked at ASIHTTPRequest? It makes your life a lot easier by allowing you to use blocks. Here's an example of how to make an asynchronous request:
- (IBAction)grabURLInBackground:(id)sender
{
NSURL *url = [NSURL URLWithString:#"http://allseeing-i.com"];
__block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setCompletionBlock:^{
// Use when fetching text data
NSString *responseString = [request responseString];
// Use when fetching binary data
NSData *responseData = [request responseData];
}];
[request setFailedBlock:^{
NSError *error = [request error];
}];
[request startAsynchronous];
}
You can find more information here:
http://allseeing-i.com/ASIHTTPRequest/

Resources