json parsing of multiple url, one after another in sequence or where to write another function calling in JSONRequestOperationWithRequest - iphone

I have parsed json. The result of json stored in array which contain list of video ID.
Now I want to parse another json which retrieve detail of video and this json will be parsed in loop videoIDArray.count times
Here is code:
- (void)viewDidLoad
{
[super viewDidLoad];
videoIDArray = [[NSMutableArray alloc] init];
viewArray = [[NSMutableArray alloc] init];
//======Json Parsing
NSString *urlstring = [NSString stringWithFormat:#"https://myURL/youtubeList"];
NSURL *url = [NSURL URLWithString:urlstring];
NSURLRequest *Request = [NSURLRequest requestWithURL:url];
conn = [[NSURLConnection alloc] initWithRequest:Request delegate:self];
if (conn) {
webdata = [[NSMutableData alloc] init];
}
//==========
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
if (connection==conn) {
[webdata setLength:0];
}
if (connection==conn2) {
[webdata2 setLength:0];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
if (connection==conn) {
[webdata appendData:data];
}
if (connection==conn2) {
[webdata2 appendData:data];
}
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
//-------conn getting list of videoID
if (connection == conn) {
NSString *str = [[NSString alloc] initWithBytes:[webdata bytes] length:[webdata length] encoding:NSUTF8StringEncoding];
NSDictionary *Result = [NSJSONSerialization JSONObjectWithData:[str dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];
[videoIDArray addObjectsFromArray:[[[Result valueForKey:#"items"] valueForKey:#"id"] valueForKey:#"videoId"]];
NSLog(#"Video ID %#",videoIDArray);
//======conn2 is for getting detail of video base on videoID object
for (int i=0; i<videoIDArray.count; i++) {
NSString *urlstring = [NSString stringWithFormat:#"https://mydetailURL/videos/%#?v=2&alt=json",[videoIDArray objectAtIndex:i]];
NSURL *url = [NSURL URLWithString:urlstring];
NSURLRequest *Request = [NSURLRequest requestWithURL:url];
conn2 = [[NSURLConnection alloc] initWithRequest:Request delegate:self];
if (conn2) {
webdata2 = [[NSMutableData alloc] init];
}
}
//==========
}
if (connection==conn2) {
[MBProgressHUD hideHUDForView:self.view animated:YES];
[youtubeTableView reloadData];
NSString *str = [[NSString alloc] initWithBytes:[webdata bytes] length:[webdata length] encoding:NSUTF8StringEncoding];
NSDictionary *Result = [NSJSONSerialization JSONObjectWithData:[str dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];
NSLog(#"ResultConn2 %#",Result);
[viewArray addObject:[[[Result valueForKey:#"entry"] valueForKey:#"yt$statistics"] valueForKey:#"viewCount"]];
NSLog(#"View Array %#",viewArray);
}
}
Problem is: it is not parsing as many times as in loop, only for last one connectionDidFinishLoading method called and crashed..
Can somebody tell me how to do this?
Is there any other way to do this?
EDIT
With AFNetworking
i changed my code like:
for (int i=0; i<videoArray.count; i++) {
[self parseWithUrl:[videoArray objectAtIndex:i]];
}
-(void)parseWithUrl: (NSString *)urlstr
{
NSString *tstr=[urlstr stringByReplacingOccurrencesOfString:#"\"" withString:#""];
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"https://myURL/feeds/api/videos/%#?v=2&alt=json",tstr]];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFJSONRequestOperation *operation =
[AFJSONRequestOperation JSONRequestOperationWithRequest: request
success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
//instead of NSLog i want to return result
NSDictionary *result = (NSDictionary *)JSON;
NSLog(#"VideoResult %#",result);
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
UIAlertView *av = [[UIAlertView alloc] initWithTitle:#"Error Retrieving Weather"
message:[NSString stringWithFormat:#"%#",error]
delegate:nil
cancelButtonTitle:#"OK" otherButtonTitles:nil];
[av show];
}];
[operation start];
}
I want to write:
-(NSDictionary *)parseWithUrl: (NSString *)urlstr
Is it possible?
if Yes then suggest me where i should return result?
if i want to call another method after completing json then where to write call code
here is my code:
[self getData:self.weather];
my method called number of times which i don't want.
success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
self.weather = (NSDictionary *)JSON;
[self getData:self.weather];
.....
.....
}

Your issue stems from the fact that each of these NSURLConnection connections run asynchronously and therefore you're running many requests concurrently, but your routine is using a single webData2, so your requests are tripping over each other.
If you want to stay with your current design, rather than having a for loop where you initiate all of the second set of requests, you should only request the first one. And then have the connectionDidFinishLoading for the second type of request initiate the next one. (You could manage this "next" process by keeping track of some numeric index indicating which request you're processing, incrementing it each time.)
But you ask how to do these requests sequentially, one after another. Is there any reason why you cannot do them concurrently (more accurately, when first request is done, then issue the detail requests for the individual videos concurrently). In that scenario, even better than the clumsy fix I outlined in the prior paragraph, a more logical solution is an NSOperation-based implementation that:
Uses a separate object for each connection so the individual requests don't interfere with each other;
Enjoys the concurrency of NSURLConnection, but constrains the number of concurrent requests to some reasonable number ... you will yield significant performance benefits by using concurrent requests; and
Is cancelable in case the user dismisses the view controller while all of these requests are in progress and you want to cancel the network requests.
If you're already familiar writing NSURLConnectionDataDelegate based code, wrapping that in an NSOperation is not much worse. (See Defining a Custom Operation Object in the Concurrency Programming Guide.) We can walk you through the steps to do that, but frankly much easier is to use AFNetworking. They've done the complicated stuff for you.
In your edit to your question, you ask whether it is possible to write:
- (NSDictionary *)parseWithUrl: (NSString *)urlstr
While it's technically possible to, you never want a method on the main queue waiting synchronously for a network request. If parseWithURL cannot do what it needs to do inside the success block of the AFNetworking call (e.g. you might initiate a [self.tableView reloadData] or whatever is needed for your UI), then have parseWithURL return the dictionary in a completion handler of its own, e.g.:
- (void)parseWithURL: (NSString *)urlstr completion:(void (^)(NSDictionary *))completion
{
...
AFJSONRequestOperation *operation =
[AFJSONRequestOperation JSONRequestOperationWithRequest: request
success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
completion(JSON); // call the completion block here
} failure:...];
}

Finally I am done parsing synchronous multiple json parsing with help of AFNetworking
Success of json parsing call another method in AFNetworking done by: link
Here is Code:
- (void)getResponse:(void (^)(id result, NSError *error))block {
NSString *weatherUrl = [NSString stringWithFormat:#"%#weather.php?format=json", BaseURLString];
NSURL *url = [NSURL URLWithString:weatherUrl];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 2
AFJSONRequestOperation *operation =
[AFJSONRequestOperation JSONRequestOperationWithRequest:request
// 3
success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
//Success
block(JSON,nil); //call block here
}
// 4
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
UIAlertView *av = [[UIAlertView alloc] initWithTitle:#"Error Retrieving Weather"
message:[NSString stringWithFormat:#"%#",error]
delegate:nil
cancelButtonTitle:#"OK" otherButtonTitles:nil];
[av show];
}];
// 5
[operation start];
}
calling will be:
[self getResponse:^(id result, NSError *error) {
//use result here
}];

Related

UIAlertView is showing up late

I am communicating with server in my ios app. I have following method in which I'm opening an alertview. I want to show a loading view while app is getting response from the server.
- (void) showDetailedQuestion:(id)sender
{
//loading view
self.loading_alert = [[UIAlertView alloc] initWithTitle:#"Loading\nPlease Wait..." message:nil delegate:self cancelButtonTitle:nil otherButtonTitles: nil];
[self.loading_alert show];
UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
// Adjust the indicator so it is up a few pixels from the bottom of the alert
indicator.center = CGPointMake(loading_alert.bounds.size.width / 2, loading_alert.bounds.size.height - 50);
[indicator startAnimating];
[self.loading_alert addSubview:indicator];
UIButton *btn = (UIButton*)sender;
int indx = btn.tag;
NSLog(#"tag:%d",indx);
answerAnQuestion *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"Answer"];
vc.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal ;
vc.que_id = [self.que_id valueForKey:[NSString stringWithFormat:#"%d",indx]];
vc.qid_list = self.que_id;
vc.crnt = indx;
[self presentViewController:vc animated:YES completion:nil];
[self.loading_alert dismissWithClickedButtonIndex:0 animated:YES];
}
and in another answerAnQuestion.m
- (void)viewDidLoad
{
NSString *address = [NSString stringWithFormat:#"%#%#%#%#%#%#%#", path,#"questions/",que_id,#"?token=",token,#"&user_id=",usrId];
NSURL *URL = [NSURL URLWithString:address];
NSLog(#"%#",address);
[NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:[URL host]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL cachePolicy:NSURLCacheStorageAllowedInMemoryOnly
timeoutInterval:60.0];
[request setHTTPMethod:#"GET"];
responseData = [[NSMutableData alloc] init];
NSURLResponse *response = nil;
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if (data)
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*) response;
//If you need the response, you can use it here
int statuscode = [httpResponse statusCode];
NSString *responseMsg = [NSHTTPURLResponse localizedStringForStatusCode:statuscode];
NSLog(#" Status code: %d",statuscode );
NSLog(#" Status msg: %#",responseMsg );
}
else
{
// Handle error by looking at response and/or error values
NSLog(#"%#",error);
}
}
My problem is alertview is only shown up for a moment when view is changing. It suppose to open when I click the button. What could be the reason? how to solve this?
EDIT 1:
If i make asynchronous request to server then i'm not able to set those data in my tableview. I can set those data in my tableview Only if send synchronous request,but it blocks the app. Why this is happening ?
Any help will be appreciated.
Thank you.
You are sending SynchronousRequest on main thread, so it is blocking your UI thread. Read multithreading you will get various tutorial on this. I can suggest you to go for GCD or NSOperation and NSOperationQueue. Google for any of the above and you will get various sample for the same.
Or you can send asynchronous request as follows...
[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){
//Write code you want to call when data is received,
//Like dismissing loading view and populating UI.
}];
Updated:
//Display alert view, before sending your request..
[alertview show];
//send first request
[NSURLConnection sendAsynchronousRequest:request1 queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){
//Write code you want to call when data is received,
//send second request
[NSURLConnection sendAsynchronousRequest:request2 queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){
//Write code you want to call when data is received,
//send third request
[NSURLConnection sendAsynchronousRequest:request3 queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){
//Write code you want to call when data is received,
//dismiss alert view on main thread
dispatch_async(getmainqueue, ^(void) {
// dismiss alert view...
});
}];
}];
}];
I have worked with Nuzhat Zari code, and I thank him for it, but also have experienced some issues with some core data operations between nested "sendAsynchronousRequest" (getting some weird thread and memory errors) so, my solution was unnest the calls to "sendAsynchronousRequest" and use some main thread variable validation.
#interface myMainThreadClass
#property (nonatomic,assign) NSInteger *currentAsyncTasks;
#end
#implementation
// Use init or viewDidLoad to make "currentAsyncTasks=0"!!
-(void)method
{
[self showLoadingAlert]; //or some ui update function
currentAsyncTasks++;
[NSURLConnection sendAsynchronousRequest:urlRequest queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){
//do something with data
dispatch_async(dispatch_get_main_queue(), ^(void) {
[self dismissAlertInFinalTask]
});
}];
currentAsyncTasks++;
[NSURLConnection sendAsynchronousRequest:urlRequest2 queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){
//do something with data
dispatch_async(dispatch_get_main_queue(), ^(void) {
[self dismissAlertInFinalTask]
});
}];
currentAsyncTasks++;
[NSURLConnection sendAsynchronousRequest:urlRequest3 queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){
//do something with data
dispatch_async(dispatch_get_main_queue(), ^(void) {
[self dismissAlertInFinalTask]
});
}];
}
-(void)dismissAlertInFinalTask
{
currentNetworkTasks--;
if (currentNetworkTasks == 0)
{
[self dismissLoadingAlert];//or some ui update function;
}
}
#end
I also want to know if someone has managed to do multiple request using NSURLConnection delegate NSURLConnectionDataDelegate and ui calls.

Making multiple connection in ios

I'm using 2 UICollectionView on one screen and fetching 2 different data sets from my api.
But the problem is that I can't resolve is differentiating the incoming data depending on which view requested.
Here's a code snippet of what I do in the UICollectionView:
- (void)viewDidLoad
{
[super viewDidLoad];
NSURL *headlineurl = [NSURL URLWithString:#"api1"];
headlinerequest = [NSURLRequest requestWithURL:headlineurl];
[[NSURLConnection alloc] initWithRequest:headlinerequest delegate:self];
NSURL *mostnewsurl = [NSURL URLWithString:#"api2"];
NSURLRequest *mostnewsrequest = [NSURLRequest requestWithURL:mostnewsurl];
[[NSURLConnection alloc] initWithRequest:mostnewsrequest delegate:self];
}
And this is the code from the delegate:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
//What should I do here ?
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)theData
{
//What should I do here ?
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
//What should I do here ?
}
Thanks a lot.
Your problem is pretty common when people start using NSUrlConnection delegates,
First thing you're setting the delegate of both views to the same object, which can work, but will need some hackery.
I recommend one of the following solutions:
Solution 1 (uses delegates, more work)
Create a new class, and give give it the NSURlconnection delegate protocol
and call it something like apiFetchDelegate
Then place your delegate methods in there -(void) connectionDidFinishLoading, etc..
Now in your viewDidLoad method, change it to the following:
NSURL *headlineurl = [NSURL URLWithString:#"api1"];
headlinerequest = [NSURLRequest requestWithURL:headlineurl];
//Create a new instance of the delegate
apiFetchDelegate* headlineDelegate = [[apiFetchDelegate alloc] init];
[[NSURLConnection alloc] initWithRequest:headlinerequest delegate:headlineDelegate];
And the second delegate:
NSURL *mostnewsurl = [NSURL URLWithString:#"api2"];
NSURLRequest *mostnewsrequest = [NSURLRequest requestWithURL:mostnewsurl];
//Create second delegate
apiFetchDelegate* mostnewsDelegate = [[apiFetchDelegate alloc] init];
[[NSURLConnection alloc] initWithRequest:mostnewsrequest delegate:mostnewsDelegate];
now as you see, each one will get its own delegate, and data won't be mixed anymore !
Solution 2 (without delegates, less work to do)
This is probably a better solution for your need , I'm not sure why you need delegates for such a simple call, but if you don't , better go with this simple way !.
We will make async calls to avoid freezing the UI while data is being fetched, this will require an NSOperationQueue, here's how it'll work:
In your viewDidLoad method, change the code to this:
//Create your Queue here
NSOperationQueue *apiCallsQueue = [NSOperationQueue alloc] init];
[apiCallsQueue setMaxConcurrentOperations:2];
NSURL *headlineurl = [NSURL URLWithString:#"api1"];
headlinerequest = [NSURLRequest requestWithURL:headlineurl];
[NSURLConnection sendAsynchronousRequest:headlinerequest queue:apiCallsQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
//Here is your data for the first view
//
NSLog(#"Data for headline view: %#", [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding]);
}];
And for the second view:
NSURL *mostnewsurl = [NSURL URLWithString:#"api2"];
NSURLRequest *mostnewsrequest = [NSURLRequest requestWithURL:mostnewsurl];
[NSURLConnection sendAsynchronousRequest:mostnewsrequest queue:apiCallsQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
//Here is your data, for the second view
//
NSLog(#"Date for latest news: %#", [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding]);
}];
Let me know if this works for you or if you need further assistance.

iOS - Performing multiple AFJSONRequestOperations that write to Core Data in background

I am having trouble determining the best way to manage updates to my apps SQLite database (using Core Data)
When my app launches, it hits a server to determine which tables need updating. I then do a service call for each of those tables. Once I get the JSON back for each, it creates/updates the corresponding objects in my SQLite DB.
What I am doing works, as it performs each request and updates each table that needs to be-- but I don't think I am doing this correctly.
Doing this still locks my UI Thread and I need to be able to run this code asynchronously in the background every 10 minutes or so.
AFJSONRequestOperation* operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
NSMutableArray *operations = [NSMutableArray new];
//for each of the tables that need updating, create a AFJSONRequestOperation
for(NSString *str in [JSON valueForKey:#"Views"])
{
NSString* path = [NSString stringWithFormat:#"cache/%#/?deviceUID=%#&token=%#", str, #"00000-00000-0000-00001", [_globals getToken]];
NSURLRequest* request = [client requestWithMethod:#"GET" path:path parameters:nil];
AFJSONRequestOperation* operation2 = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON)
{
[self updateTable:str withJSON:JSON];
}
failure:nil];
[operations addObject:operation2];
}
//AFHTTPClient
[client enqueueBatchOfHTTPRequestOperations:operations progressBlock:nil completionBlock:^(NSArray *operations) {
//this gets called way before the objects are done updating to the DB
NSLog(#"DONE ALL REQUESTS");
[_HUD hide:YES]; // getting called after getting all of the JSON not after all tables are updated
}];
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
[_HUD hide:YES];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Failed" message:[error localizedDescription] delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[alert show];
}];
[operation start];
Heres my updateTable function with only 1 condition
- (void)updateTable:(NSString *)tblName withJSON:(id)JSON
{
for(NSDictionary *record in [JSON valueForKey:#"Records"])
{
NSString *viewName = [[record valueForKey:#"ViewName"] lowercaseString];
//Worker
if([viewName isEqualToString:[[NSString stringWithFormat:#"Worker_vw_iSales"] lowercaseString]])
{
if([Worker doesWorkerExist:[record valueForKey:#"JSONData"]])
{
NSLog(#"deleting old worker");
[ad.managedObjectContext deleteObject:[Worker doesWorkerExist:[record valueForKey:#"JSONData"]]];
}
NSEntityDescription *desc = [NSEntityDescription entityForName:NSStringFromClass([Worker class]) inManagedObjectContext:ad.managedObjectContext];
Worker *worker = [[Worker alloc] initWithEntity:desc insertIntoManagedObjectContext:ad.managedObjectContext];
[worker initWithJSONSting:[record valueForKey:#"JSONData"]];
NSLog(#"Creating Worker: %#", worker.firstName);
}
}
}
I hope this is not all too confusing-- if so I can try to explain more.
I may be doing this completely wrong, and if I am just let me know. I have tried a few other things, including using an NSOperationQueue instead of AFHHTTPs enqueueBatchOfHTTPRequestOperations:requests but I cannot get the behavior I am looking for.
Thanks!
What you are looking for now is setSuccessCallbackQueue: on AFJSONRequestOperation. AFNetworking set all their success blocks to run on the main queue unless otherwise specified.
What I've done is
#implementation myClass {
dispatch_queue_t backgroundQueue;
}
- (id)init
{
if (self = [super init]){
backgroundQueue = dispatch_queue_create("com.proj.myClass", 0);
}
return self;
}
- (void)doSomeStuff
{
NSMutableURLRequest *request = [[myAFAPIClient sharedClient] requestWithMethod:#"GET" path:path parameters:params];
AFJSONRequestOperation *myOperation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
//Success Block
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
}];
[myOperation setSuccessCallbackQueue:backgroundQueue];
[[myAFAPIClient sharedClient].operationQueue addOperation:myOperation];
}
So the different would be you are enqueue'ing operations, where I'm just adding them straight to the client.
Also, where I have //Success Block I do all sorts of things such as dispatching other methods onto backgroundQueue, which those methods make more JSON requests and I don't have any issues.

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);