Managing two NSURLConnection - iphone

I want to do two async request from two different kml file, so I setup two requests first:
NSString *server1URL = [NSString stringWithFormat:...];
NSMutableURLRequest *firstRequest =[NSMutableURLRequest requestWithURL:[NSURL URLWithString:server1URL]];
[firstRequest setHTTPMethod:#"GET"];
NSURLConnection *AConnection = [NSURLConnection connectionWithRequest:firstRequest delegate:self];
NSString *server2URL = [NSString stringWithFormat:...];
NSMutableURLRequest *secondRequest =[NSMutableURLRequest requestWithURL:[NSURL URLWithString:server2URL]];
[secondRequest setHTTPMethod:#"GET"];
NSURLConnection *BConnection = [NSURLConnection connectionWithRequest:secondRequest delegate:self];
Then I init NSMutableData I will be using:
AResponseData = [[NSMutableData alloc] init];
BResponseData = [[NSMutableData alloc] init];
Then, I refer this post and did this:
connectionToInfoMapping = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(connectionToInfoMapping, AConnection, [NSMutableDictionary dictionaryWithObject:AResponseData forKey:#"receivedData"]);
CFDictionaryAddValue(connectionToInfoMapping, BConnection, [NSMutableDictionary dictionaryWithObject:BResponseData forKey:#"receivedData"]);
OK, then there're delegates:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
const NSMutableDictionary *connectionInfo = CFDictionaryGetValue(connectionToInfoMapping, connection);
[[connectionInfo objectForKey:#"receivedData"] appendData:data];
}
So with this I can get data append to the correct NSMutableData that matches the connection.
Now in - (void)connectionDidFinishLoading:(NSURLConnection *)connection, I want to "If A finish, do this, if B finish, do this", and my question is, how can I do this?

- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
if( [connection isEqual: AConnection] ){
// do connection A stuff
}
else if( [connection isEqual: BConnection] ){
// do connection B stuff
}
}

Use GCD with sendSynchronousRequest: requests, they will be run in the background.
Example:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSURLRequest *request = [NSURLRequest requestWithURL:url1];
NSURLResponse *response;
NSError *error;
NSData *data1 = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
// do something with the data
});
dispatch_async(queue, ^{
NSURLRequest *request = [NSURLRequest requestWithURL:url2];
NSURLResponse *response;
NSError *error;
NSData *data2 = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
// do something with the data
});

How about assigning tags to each connection and checking the tags via an if/else or switch in the connectionDidFinishLoading?

Related

NSURLRequest : Post data and read the posted page

In iOS (current target 5.0, Base SDK 5.1) how can I send a post request to a server, and then read the contents of the page. For example, the page takes a username and password, and then echos true or false. This is just for a better understanding of NSURLRequest.
Thanks in Advance!
A few things first
Decide how you want to encode your data - JSON or url-encoding are a good start.
Decide upon a return value - will it be 1, TRUE or 0, FALSE, or even YES/non-nil nothing/nil.
Read up on the URL Loading System, it's your friend.
Aim to make all your url connections asynchronous so your UI remains responsive. You can do this with NSURLConnectionDelegate callbacks. NSURLConnection has a small drawback: your code must be decoupled. Any variables you want available in the delegate functions will need to be saved to ivars or in your request's userInfo dict.
Alternatively you can use GCD, which, when coupled with the __block qualifiers, allows you to specify error/return code at the point you declare it - useful for one off fetches.
Without further ado, here's a quick and dirty url-encoder:
- (NSData*)encodeDictionary:(NSDictionary*)dictionary {
NSMutableArray *parts = [[NSMutableArray alloc] init];
for (NSString *key in dictionary) {
NSString *encodedValue = [[dictionary objectForKey:key] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *encodedKey = [key stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *part = [NSString stringWithFormat: #"%#=%#", encodedKey, encodedValue];
[parts addObject:part];
}
NSString *encodedDictionary = [parts componentsJoinedByString:#"&"];
return [encodedDictionary dataUsingEncoding:NSUTF8StringEncoding];
}
Using a JSON library like JSONKit makes encoding things easier, consider it!
Method 1 - NSURLConnectionDelegate async callbacks:
// .h
#interface ViewController : UIViewController<NSURLConnectionDelegate>
#end
// .m
#interface ViewController () {
NSMutableData *receivedData_;
}
#end
...
- (IBAction)asyncButtonPushed:(id)sender {
NSURL *url = [NSURL URLWithString:#"http://localhost/"];
NSDictionary *postDict = [NSDictionary dictionaryWithObjectsAndKeys:#"user", #"username",
#"password", #"password", nil];
NSData *postData = [self encodeDictionary:postDict];
// Create the request
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"POST"];
[request setValue:[NSString stringWithFormat:#"%d", postData.length] forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request
delegate:self];
[connection start];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[receivedData_ setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[receivedData_ appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"Succeeded! Received %d bytes of data", [receivedData_ length]);
NSString *responeString = [[NSString alloc] initWithData:receivedData_
encoding:NSUTF8StringEncoding];
// Assume lowercase
if ([responeString isEqualToString:#"true"]) {
// Deal with true
return;
}
// Deal with an error
}
Method 2 - Grand Central Dispatch async function:
// .m
- (IBAction)dispatchButtonPushed:(id)sender {
NSURL *url = [NSURL URLWithString:#"http://www.apple.com/"];
NSDictionary *postDict = [NSDictionary dictionaryWithObjectsAndKeys:#"user", #"username",
#"password", #"password", nil];
NSData *postData = [self encodeDictionary:postDict];
// Create the request
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"POST"];
[request setValue:[NSString stringWithFormat:#"%d", postData.length] forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Peform the request
NSURLResponse *response;
NSError *error = nil;
NSData *receivedData = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
if (error) {
// Deal with your error
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
NSLog(#"HTTP Error: %d %#", httpResponse.statusCode, error);
return;
}
NSLog(#"Error %#", error);
return;
}
NSString *responeString = [[NSString alloc] initWithData:receivedData
encoding:NSUTF8StringEncoding];
// Assume lowercase
if ([responeString isEqualToString:#"true"]) {
// Deal with true
return;
}
// Deal with an error
// When dealing with UI updates, they must be run on the main queue, ie:
// dispatch_async(dispatch_get_main_queue(), ^(void){
//
// });
});
}
Method 3 - Use an NSURLConnection convenience function
+ (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler
Hope this helps.
NSData *postData = [someStringToPost dataUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:someURLString];
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
[req setHTTPMethod:#"POST"];
[req setValue:[NSString stringWithFormat:#"%d", postData.length] forHTTPHeaderField:#"Content-Length"];
[req setValue:#"application/x-www-form-urlencoded charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[req setHTTPBody:postData];
NSError *err = nil;
NSHTTPURLResponse *res = nil;
NSData *retData = [NSURLConnection sendSynchronousRequest:req returningResponse:&res error:&err];
if (err)
{
//handle error
}
else
{
//handle response and returning data
}
This project might be quite handy for your purpose. It will take care of your downloads and store it locally. Check out the link https://github.com/amitgowda/AGInternetHandler

Asynchronously getting data instead of using initWithContentsOfURL

I am currently using this code to get data from a URL:
NSURL *url = [[NSURL alloc] initWithString:urlstring];
NSString *stringfromFB = [[NSString alloc] initWithContentsOfURL:url];
I was wondering how I could gather this data asynchronously so that my app does not freeze everytime I need to execute this. Thanks!
The simplest way is available in os5 like this:
NSString *stringfromFB;
NSURL *url = [[NSURL alloc] initWithString:urlstring];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (data) {
stringfromFB = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding]; // note the retain count here.
} else {
// handle error
}
}];
If you're stuck in os<5 for some reason, you'll need to start your connection with a delegate, and implement the delegate protocol as illustrated here (and many places elsewhere).
You can do it with GCD:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSURL *url = [[NSURL alloc] initWithString:urlstring];
NSString *stringfromFB = [[NSString alloc] initWithContentsOfURL:url]
});
// Do not alloc init URL obj. for local use.
NSString *urlString = #"put your url string here";
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
The above delegate methods are of NSURLConnectionDelegate, where you need to handle all things like response error etc.. It is by default provided so we can directly override it without
I have use once in my project It will work for async request but If you received number of images as well then also use IconDownloader or EGOImageView which implements the lazy loading of image and make much low chances of freezing of app.
if you don’t want to wait for a connection to complete loading a url, you can load it asynchronously using NSURLConnection.
[NSURLConnection connectionWithRequest:
[NSURLRequest requestWithURL:
[NSURL URLWithString:yourUrlString]]
delegate:self];
Now that NSURLConnection is deprecated you need to use NSURLSession.
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest: request
completionHandler: ^(NSData *data, NSURLResponse *response, NSError *networkError) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if ([httpResponse statusCode] == STATUS_OK) {
// use data
}
else {
NSLog(#"Network Session Error: %#", [networkError localizedFailureReason]);
}
}];
[task resume];

iPhone: Handle two(or multiple) responses for two requests in the same delegate

I am calling NSURLConnection asynchronous method calls in my view controller. I would like to handle TWO RESPONSES FOR TWO REQUEST in the same Delegate. Please suggest me what would the best approach to achieve this? I'm developing in iOS 5 SDK.
UPDATED:
// Class A
[serverconns setDelegate:self];
connection = [serverconns executeAsyncHttpPost :firstjsonrequest];
[serverconns setDelegate:self];
connection = [serverconns executeAsyncHttpPost :secondjsonrequest];
- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
}
- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.appendData appendData:data];
}
- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// logs the error
}
- (void) connectionDidFinishLoading:(NSURLConnection *)connection
{
NSData *responseData = [[NSData alloc] initWithData:appendData];
//HOW CAN WE HANDLE TWO RESPONSES FOR TWO REQUEST in the same Delegate
if (responseData)
{
// doing something
}
}
//Class B: ServerConnection
- (NSURLConnection *) executeAsyncHttpPost :(id) jsonParams
{
NSString *urlstr = [NSString stringWithFormat:#"%#", baseURL];
urlstr = [urlstr stringByAppendingFormat:method];
NSURL *pUrl = [NSURL URLWithString:urlstr];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:pUrl];
NSData *requestData = [NSData dataWithBytes:[jsonParams UTF8String] length:[jsonParams length]];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-type"];
[request setHTTPBody: requestData];
return [[NSURLConnection alloc] initWithRequest:request delegate:delegateResponder startImmediately:YES];
}
-(void) setDelegate:(id)newDelegate
{
delegateResponder = newDelegate;
}
save your connections somewhere (maybe ivar of your delegate)
- (void) connectionDidFinishLoading:(NSURLConnection *)connection
{
NSData *responseData = [[NSData alloc] initWithData:appendData];
//HOW CAN WE HANDLE TWO RESPONSES FOR TWO REQUEST in the same Delegate
if (responseData)
{
if (connection == yourFirstConnection) {
// doing something for first connection
} else {
// doing something for second connection
}
}
}
just point out some minor problem of your code
NSString *urlstr = [NSString stringWithFormat:#"%#", baseURL];
urlstr = [urlstr stringByAppendingFormat:method];
should replace to
NSString *urlstr = [baseURL absoluteString];
urlstr = [urlstr stringByAppendingString:method];
and add two(or more or array) weak/assign property of NSURLConnection to your class A (connection delegate)
#property (assign) NSURLConnection *myFirstConnection;
#property (assign) NSURLConnection *mySecondConnection;
// assume only need to handle two connection otherwise NSArray should be used instead
than in your class B (create connection)
- (NSURLConnection *) executeAsyncHttpPost :(id) jsonParams
{
NSString *urlstr = [baseURL absoluteString];
urlstr = [urlstr stringByAppendingString:method];
NSURL *pUrl = [NSURL URLWithString:urlstr];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:pUrl];
NSData *requestData = [NSData dataWithBytes:[jsonParams UTF8String] length:[jsonParams length]];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-type"];
[request setHTTPBody: requestData];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:delegateResponder startImmediately:YES];
delegateResponder.myFirstConnection = connection;
// delegateResponder.mSecondConnection = connection;
return connection;
}
If I were you I would create a CustomClass which inherits the NSURLConnection. And I will add property called tag.
When I initiate the CustomClass, I would set the tag property and use that to determine which request is being worked on
CustomURLConnection *connection = [[CustomURLConnection alloc] initWithRequest:request delegate:self tag:1];
- (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate tag:(int)_tag
{
if(self = [super initWithRequest:request delegate:delegate])
{
self.tag = _tag;
}
Now in the code you posted add this
- (void) connectionDidFinishLoading:(NSURLConnection *)connection
{
NSData *responseData = [[NSData alloc] initWithData:appendData];
//HOW CAN WE HANDLE TWO RESPONSES FOR TWO REQUEST in the same Delegate
if (responseData)
{
if (connection.tag == 1){
}
}
}
return self;
}
I think all the mentioned solutions are "ugly". I would not implement a solution with delegate methods but instead create a blocks-based solution. I could post an example if you're interested. I would make use of the AFNetworking classes for this approach.
What follows is an example of a class that handles 2 different responses without using a delegate implementation, opting for blocks instead with the AFNetworking library.
- (void)JSONFromService
{
// create the first request and set the methods that handle the return values (either NSData or NSError in this case) in blocks ...
NSURL *firstURL = [NSURL URLWithString:#"http://dl.dropbox.com/u/6487838/test1.html"];
NSURLRequest *firstRequest = [NSURLRequest requestWithURL:firstURL];
AFHTTPRequestOperation *firstOperation = [[AFHTTPRequestOperation alloc] initWithRequest:firstRequest];
[firstOperation setCompletionBlockWithSuccess:^ (AFHTTPRequestOperation *operation, id object)
{
NSString *firstString = [[NSString alloc] initWithData:object encoding:NSASCIIStringEncoding];
NSLog(#"%#", firstString);
} failure:^ (AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"%#", error);
}];
[firstOperation start];
// create the second request and set the methods that handle the return values (either NSData or NSError in this case) in blocks ...
NSURL *secondURL = [NSURL URLWithString:#"http://dl.dropbox.com/u/6487838/test2.html"];
NSURLRequest *secondRequest = [NSURLRequest requestWithURL:secondURL];
AFHTTPRequestOperation *secondOperation = [[AFHTTPRequestOperation alloc] initWithRequest:secondRequest];
[secondOperation setCompletionBlockWithSuccess:^ (AFHTTPRequestOperation *operation, id object) {
NSString *secondString = [[NSString alloc] initWithData:object encoding:NSASCIIStringEncoding];
NSLog(#"%#", secondString);
} failure:^ (AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"%#", error);
}];
[secondOperation start];
}
I usually subclass NSURLConnection and add properties to store whatever context I need to handle the response.
Since the delegate methods get NSURLConnection passed in, you can just cast it back to your subclass and access the context.
Take a look at this example.
I think you should keep all of your connections in an activeConnections array. Every time one finishes, you do [activeConnections indexForObject:connection] and you update your delegate method accordingly, using the index.
Now, a cleaner way to do it( and a better way from my point of view, but this depends on how large is the data you want to transfer) is to use queues. I'll provide a small example and add comments to it:
// we assume you have 2 requests: req1, req2
//now, create a new dispatch queue
dispatch_queue_t netQueue = dispatch_queue_create("com.mycompany.netqueue",DISPATCH_QUEUE_SERIAL);
//execute the operations in the queue ASYNC
//is very important to dispatch this ASYNC on a background thread, otherwise your UI will be stuck until the request finishes
dispatch_async(netQueue,
^{
// We are on a background thread, so we won't block UI events (or, generally, the main run loop)
NSHTTPURLResponse *response = nil;
NSError *error = nil;
NSData *data = nil;
//We can call the request synchronous and block this thread until completed
data = [NSURLConnection sendSynchronousRequest:req1
returningResponse:&response
error:&error];
dispatch_async(dispatch_get_main_queue(),
^{
//call your delegate with the appropriate method for req1
//be sure to copy the contents in data, as we will reuse it with the next request
});
//We can call the other request synchronous and block this thread until completed
data = [NSURLConnection sendSynchronousRequest:req2
returningResponse:&response
error:&error];
dispatch_async(dispatch_get_main_queue(),
^{
//call your delegate with the appropriate method for req2
});
//and this can go on forever. If you have many requests to execute, simply put them in a loop
});
dispatch_release(netQueue);

Objective C - POST data using NSURLConnection

I'm very slowly working my way through learning the URL loading system for iOS development, and I am hoping someone could briefly explain the following piece of code:
NSString *myParameters = [[NSString alloc] initWithFormat:#"one=two&three=four"];
[myRequest setHTTPMethod:#"POST"];
[myRequest setHTTPBody:[myParameters dataUsingEncoding:NSUTF8StringEncoding]];
Eventually I would like to be able to create an application that logs into my ISP's website and retrieves how much data I have left for the rest of the month, and I feel as though I should get my head around setHTTPMethod/setHTTPBody first.
Kind regards
This is a pretty simple HTTP request setup; if you have more specific questions you might do better asking those.
NSString *myParameters = #"paramOne=valueOne&paramTwo=valueTwo";
This sets up a string containing the POST parameters.
[myRequest setHTTPMethod:#"POST"];
The request needs to be a POST request.
[myRequest setHTTPBody:[myParameters dataUsingEncoding:NSUTF8StringEncoding]];
This puts the parameters into the post body (they need to be raw data, so we first encode them as UTF-8).
Step 1 : set URL definitions:
// Create the request
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"http://192.168.0.232:8080/xxxx/api/Login"]];
// Specify that it will be a POST request
request.HTTPMethod = #"POST";
// This is how we set header fields
[request setValue:#"application/x-www-form-urlencoded; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
NSMutableDictionary *postDict = [[NSMutableDictionary alloc] init];
[postDict setValue:#"Login" forKey:#"methodName"];
[postDict setValue:#"admin" forKey:#"username"];
[postDict setValue:#"123456" forKey:#"password"];
[postDict setValue:#"mobile" forKey:#"clientType"];
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:postDict options:0 error:nil];
// Checking the format
NSString *urlString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
// Convert your data and set your request's HTTPBody property
NSString *stringData = [[NSString alloc] initWithFormat:#"jsonRequest=%#", urlString];
//#"jsonRequest={\"methodName\":\"Login\",\"username\":\"admin\",\"password\":\"12345678n\",\"clientType\":\"web\"}";
NSData *requestBodyData = [stringData dataUsingEncoding:NSUTF8StringEncoding];
request.HTTPBody = requestBodyData;
// Create url connection and fire request
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if (!theConnection) {
// Release the receivedData object.
NSMutableData *responseData = nil;
// Inform the user that the connection failed.
}
Step 2:
// Declare the value for NSURLResponse URL
//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];
NSError *error=nil;
// Convert JSON Object into Dictionary
NSDictionary *JSON = [NSJSONSerialization JSONObjectWithData:_responseData options:
NSJSONReadingMutableContainers error:&error];
NSLog(#"Response %#",JSON);
}
- (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
}
The first line created an string, it can be replaced with:
NSString *myParameters = #"one=two&three=four";
It's written in initWithFormat so you can extend it to assign parameter value.
Second line indicate this is HTTP post request.
The third line, setHTTPBody method take NSData type, so you need to convert string type to NSData using dataUsingEncoding method.
please use below code.
+(void)callapi:(NSString *)str withBlock:(dictionary)block{
NSData *postData = [str dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%lu",(unsigned long)[postData length]];
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:#“%#/url”,WebserviceUrl]]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:120.0];
[urlRequest setHTTPMethod:#"POST"];
[urlRequest setValue:postLength forHTTPHeaderField:#"Content-Length"];
[urlRequest setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[urlRequest setHTTPBody:postData];
[NSURLConnection sendAsynchronousRequest:urlRequest queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError)
{
if (!data) {
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setObject:[NSString stringWithFormat:#"%#",AMLocalizedString(SomethingWentWrong, nil)] forKey:#"error"];
block(dict);
return ;
}
NSError *error = nil;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
//////NSLog(#"%#",dict);
if (!dict) {
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setObject:AMLocalizedString(ServerResponceError, nil) forKey:#"error"];
block(dict);
return ;
}
block(dict);
}];
}

How to send json data in the Http request using NSURLRequest

I'm new to objective-c and I'm starting to put a great deal of effort into request/response as of recent. I have a working example that can call a url (via http GET) and parse the json returned.
The working example of this is below
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[responseData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[responseData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog([NSString stringWithFormat:#"Connection failed: %#", [error description]]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
//do something with the json that comes back ... (the fun part)
}
- (void)viewDidLoad
{
[self searchForStuff:#"iPhone"];
}
-(void)searchForStuff:(NSString *)text
{
responseData = [[NSMutableData data] retain];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.whatever.com/json"]];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
}
My first question is - will this approach scale up? Or is this not async (meaning I block the UI thread while the app is waiting for the response)
My second question is - how might I modify the request part of this to do a POST instead of GET? Is it simply to modify the HttpMethod like so?
[request setHTTPMethod:#"POST"];
And finally - how do I add a set of json data to this post as a simple string (for example)
{
"magic":{
"real":true
},
"options":{
"happy":true,
"joy":true,
"joy2":true
},
"key":"123"
}
Thank you in advance
Here's what I do (please note that the JSON going to my server needs to be a dictionary with one value (another dictionary) for key = question..i.e. {:question => { dictionary } } ):
NSArray *objects = [NSArray arrayWithObjects:[[NSUserDefaults standardUserDefaults]valueForKey:#"StoreNickName"],
[[UIDevice currentDevice] uniqueIdentifier], [dict objectForKey:#"user_question"], nil];
NSArray *keys = [NSArray arrayWithObjects:#"nick_name", #"UDID", #"user_question", nil];
NSDictionary *questionDict = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
NSDictionary *jsonDict = [NSDictionary dictionaryWithObject:questionDict forKey:#"question"];
NSString *jsonRequest = [jsonDict JSONRepresentation];
NSLog(#"jsonRequest is %#", jsonRequest);
NSURL *url = [NSURL URLWithString:#"https://xxxxxxx.com/questions"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
NSData *requestData = [jsonRequest dataUsingEncoding:NSUTF8StringEncoding];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setValue:[NSString stringWithFormat:#"%d", [requestData length]] forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody: requestData];
NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:request delegate:self];
if (connection) {
receivedData = [[NSMutableData data] retain];
}
The receivedData is then handled by:
NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSDictionary *jsonDict = [jsonString JSONValue];
NSDictionary *question = [jsonDict objectForKey:#"question"];
This isn't 100% clear and will take some re-reading, but everything should be here to get you started. And from what I can tell, this is asynchronous. My UI is not locked up while these calls are made.
I struggled with this for a while. Running PHP on the server. This code will post a json and get the json reply from the server
NSURL *url = [NSURL URLWithString:#"http://example.co/index.php"];
NSMutableURLRequest *rq = [NSMutableURLRequest requestWithURL:url];
[rq setHTTPMethod:#"POST"];
NSString *post = [NSString stringWithFormat:#"command1=c1&command2=c2"];
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding];
[rq setHTTPBody:postData];
[rq setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:rq queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
if ([data length] > 0 && error == nil){
NSError *parseError = nil;
NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
NSLog(#"Server Response (we want to see a 200 return code) %#",response);
NSLog(#"dictionary %#",dictionary);
}
else if ([data length] == 0 && error == nil){
NSLog(#"no data returned");
//no data, but tried
}
else if (error != nil)
{
NSLog(#"there was a download error");
//couldn't download
}
}];
I would suggest to use ASIHTTPRequest
ASIHTTPRequest is an easy to use
wrapper around the CFNetwork API that
makes some of the more tedious aspects
of communicating with web servers
easier. It is written in Objective-C
and works in both Mac OS X and iPhone
applications.
It is suitable performing basic HTTP
requests and interacting with
REST-based services (GET / POST / PUT
/ DELETE). The included
ASIFormDataRequest subclass makes it
easy to submit POST data and files
using multipart/form-data.
Please note, that the original author discontinued with this project. See the followring post for reasons and alternatives: http://allseeing-i.com/%5Brequest_release%5D;
Personally I am a big fan of AFNetworking
Most of you already know this by now, but I am posting this, just incase, some of you are still struggling with JSON in iOS6+.
In iOS6 and later, we have the NSJSONSerialization Class that is fast and has no dependency on including "outside" libraries.
NSDictionary *result = [NSJSONSerialization JSONObjectWithData:[resultStr dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];
This is the way iOS6 and later can now parse JSON efficiently.The use of SBJson is also pre-ARC implementation and brings with it those issues too if you are working in an ARC environment.
I hope this helps!
Here is a great article using Restkit
It explains on serializing nested data into JSON and attaching the data to a HTTP POST request.
Since my edit to Mike G's answer to modernize the code was rejected 3 to 2 as
This edit was intended to address the author of the post and makes no
sense as an edit. It should have been written as a comment or an
answer
I'm reposting my edit as a separate answer here. This edit removes the JSONRepresentation dependency with NSJSONSerialization as Rob's comment with 15 upvotes suggests.
NSArray *objects = [NSArray arrayWithObjects:[[NSUserDefaults standardUserDefaults]valueForKey:#"StoreNickName"],
[[UIDevice currentDevice] uniqueIdentifier], [dict objectForKey:#"user_question"], nil];
NSArray *keys = [NSArray arrayWithObjects:#"nick_name", #"UDID", #"user_question", nil];
NSDictionary *questionDict = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
NSDictionary *jsonDict = [NSDictionary dictionaryWithObject:questionDict forKey:#"question"];
NSLog(#"jsonRequest is %#", jsonRequest);
NSURL *url = [NSURL URLWithString:#"https://xxxxxxx.com/questions"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
NSData *requestData = [NSJSONSerialization dataWithJSONObject:dict options:0 error:nil]; //TODO handle error
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setValue:[NSString stringWithFormat:#"%d", [requestData length]] forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody: requestData];
NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:request delegate:self];
if (connection) {
receivedData = [[NSMutableData data] retain];
}
The receivedData is then handled by:
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSDictionary *question = [jsonDict objectForKey:#"question"];
Here's an updated example that is using NSURLConnection +sendAsynchronousRequest: (10.7+, iOS 5+), The "Post" request remains the same as with the accepted answer and is omitted here for the sake of clarity:
NSURL *apiURL = [NSURL URLWithString:
[NSString stringWithFormat:#"http://www.myserver.com/api/api.php?request=%#", #"someRequest"]];
NSURLRequest *request = [NSURLRequest requestWithURL:apiURL]; // this is using GET, for POST examples see the other answers here on this page
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if(data.length) {
NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
if(responseString && responseString.length) {
NSLog(#"%#", responseString);
}
}
}];
You can try this code for send json string
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:ARRAY_CONTAIN_JSON_STRING options:NSJSONWritin*emphasized text*gPrettyPrinted error:NULL];
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSString *WS_test = [NSString stringWithFormat:#"www.test.com?xyz.php&param=%#",jsonString];