I send an update to my server every 5 seconds with a timer. In response I receive an XML from the server which I parse using NSXMLParser. MyParser is set as the delegate and implements the needed callbacks to parse the document (including - (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError).
most of the time the parsing is successful but every few minutes initWithContentsOfURL gets stuck for about a minute (almost exactly one minute avery time), then [parser parse] returns false and after that i keep getting successful parsing till the next failure.
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:5
target:self
selector:#selector(parseDocument)
userInfo:nil
repeats:YES];
- (void) parseDocument {
dispatch_async(queue, ^{
NSLog(#"before NSXMLParser alloc");
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:[[NSURL alloc] initWithString:#"https://secure.xxxxxxxxxxxxxxxxxxxxxx.dbm?xxxxxxxxxxxxxxxxxxxx"]];
NSLog(#"after NSXMLParser alloc");
MyParser *myParser = [[MyParser alloc] initParser:4];
[parser setDelegate:myParser];
BOOL result = [parser parse];
if (result) {
NSLog(#"Successful parse");
} else {
NSLog(#"Failed parse");
}
});
}
My question is how can I check why the parse (which fails) is taking about a minute and than returns false???
parseErrorOccurred is never called.
I checked the server side and everything is working and running as it should.
Any ideas????
Thanks in advance
EDIT:
As it turns out, [parser parse] is working fine, the problem occurs in initWithContentsOfURL , every few minutes this line takes one minute (instead of 1-2 second) than the parse returns false.
i checked server side if everything is ok within that minute and it looks fine.
also checked if parser is not nil, which it isn't.
any suggestions on how to check whats causing the problem every few minutes in this line: NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:[[NSURL alloc] initWithString:#"https://secure.xxxxxxx.dbm?xxxxxxxxxx"]];, will be very appreciated.
Thanks
Switch to an asynchronous fetch that explicitly alerts you upon a communications error: NSURLSession if under 7 or later, the appropriate class method of NSURLConnection if support for 6 is required. Either way you'll end up with an NSError, an NSData and an NSURLResponse, which you can cast to NSHTTPURLResponse to check for a 200 response code.
If there's no error, a valid response code and greater-than-zero length data, proceed to hand the data to an NSXMLParser.
EDIT: so, I'm on a real computer now (rather than an iPad) and therefore can add that I'd gravitate towards code like:
[[[NSURLSession sharedSession]
dataTaskWithURL:[NSURL URLWithString:#"https://secure.xxxxxxxxxxxxxxxxxxxxxx.dbm?xxxxxxxxxxxxxxxxxxxx"]
completionHandler:
^(NSData *data, NSURLResponse *response, NSError *error)
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if(httpResponse.statusCode != 200 || error || ![data length])
{
NSLog(#"Some error condition");
return;
}
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
[parser setDelegate:myParser];
BOOL result = [parser parse];
if (result) {
NSLog(#"Successful parse");
} else {
NSLog(#"Failed parse");
}
}] resume];
I've favoured NSURLSession even though I'm just then telling the returned task to resume and never using any of the improved functionality over NSURLConnection's asynchronous mechanism, such as cancellation, because it is anecdotally much better at caching and because it'll automatically ensure an appropriate place is found to accumulate results and ultimately dispatch the completion handler asynchronously. So that's a few lines saved.
Related
I need to get image information from server, such image name, image id. Then use image id as one of parameters to make post, get image actual data. More specific, there are three images I should get.
First, I use getImageInfo to get image information.
- (void)getImageInfo {
// compose request
NSUserDefaults *getUserInfo = [NSUserDefaults standardUserDefaults];
NSString *uid = [getUserInfo objectForKey:#"uid"];
NSString *checkCode = [getUserInfo objectForKey:#"checkCode"];
NSString *data = [NSString stringWithFormat:#"uid=%#&yangzhengma=%#", uid, checkCode];
NSURL *url = [NSURL URLWithString:#"http://121.199.35.173:8080/xihuan22dcloud/services/Shibietupianservice/serviceGetallshibietu"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPBody = [data dataUsingEncoding:NSUTF8StringEncoding];
request.HTTPMethod = #"POST";
[[self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
if (!error) {
NSHTTPURLResponse *httpResp = (NSHTTPURLResponse*) response;
if (httpResp.statusCode == 200) {
// parse data in ram and put into images' imageInfos array
[self.images parseImageInfo:[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]];
[self getImageRawData];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
}
}] resume];}
Then I use getImageRawData to get three image data.
- (void)getImageRawData {
// compose request dynamically
NSUserDefaults *getUserInfo = [NSUserDefaults standardUserDefaults];
NSString *uid = [getUserInfo objectForKey:#"uid"];
NSString *checkCode = [getUserInfo objectForKey:#"checkCode"];
NSURL *url = [NSURL URLWithString:#"http://121.199.35.173:8080/xihuan22dcloud/services/Shibietupianservice/serviceGetthetupian"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = #"POST";
NSInteger count = 0;
for (ImageInformation *temp in self.images.imageInfos) {
NSString *data = [NSString stringWithFormat:#"uid=%#&yangzhengma=%#&tupianid=%#", uid, checkCode, temp.imageId];
request.HTTPBody = [data dataUsingEncoding:NSUTF8StringEncoding];[[self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
// if client side is no errors, continue
if (!error) {
// if server side is no errors, continue
NSHTTPURLResponse *httpResp = (NSHTTPURLResponse*) response;
if (httpResp.statusCode == 200) {
NSLog(#"图片内容:%#", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
// in ram and put into images' imageRawData array
[self.images parseImageRawData:[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] withImageId:temp.imageId withIndex:count];
// store data to disk
// NSString *path = [[NSString alloc] initWithFormat:#"image%#", temp.imageId];
// [FCFileManager writeFileAtPath:path content:data];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
}
}] resume];
count++;
}}
Here, it will loop three times, three responses come back, only the last one is complete, the others carry a error message, or incomplete raw data sometimes. Now I'm diving into concurrency programming guide, I guess serial queue likely can solve this problem.
Output like this:
2014-12-16 22:38:48.739 WeddingNewVersion[997:83366] 图片内容:<ns:serviceGetthetupianResponse xmlns:ns="http://serviceimpl.my.com"><ns:return>error</ns:return></ns:serviceGetthetupianResponse>
2014-12-16 22:38:48.749 WeddingNewVersion[997:83366] 图片内容:<ns:serviceGetthetupianResponse xmlns:ns="http://serviceimpl.my.com"><ns:return>error</ns:return></ns:serviceGetthetupianResponse>
2014-12-16 22:38:51.943 WeddingNewVersion[997:83366] 图片内容:<ns:serviceGetthetupianResponse xmlns:ns="http://serviceimpl.my.com"><ns:return>/9j/...(complete data)...9k=%%226654474.0</ns:return></ns:serviceGetthetupianResponse>
parameters of requests:
2014-12-17 14:59:25.364 WeddingNewVersion[1875:226651] uid=6&yangzhengma=odWoDXWcBv1jOrEhywkq7L&tupianid=41
2014-12-17 14:59:25.368 WeddingNewVersion[1875:226651] uid=6&yangzhengma=odWoDXWcBv1jOrEhywkq7L&tupianid=42
2014-12-17 14:59:25.368 WeddingNewVersion[1875:226651] uid=6&yangzhengma=odWoDXWcBv1jOrEhywkq7L&tupianid=43
the problem is likely not in composing request.
------------------------------------------------update1-----------------------------------------------
I have tried to put data task of session into a serial queue. Disappointed, this is not working.
dispatch_async(self.serialQueue, ^{
[[self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){...}] resume];
});
Meanwhile, I make delegateQueue of session as nil, reference says if nil, the session creates a serial operation queue for performing all delegate method calls and completion handler calls.
Now I am still confused how to make it right.
-----------------------------------------------update2------------------------------------------------
I add [NSThread sleepForTimeInterval:0.5] into the block dispatched to serial queue.
dispatch_async(self.serialQueue, ^{
[[self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){...}] resume];
[NSThread sleepForTimeInterval:0.5];
});
It does not work. The three responses are complete, but they are all the same.
Thank you in advance!
I'm just guessing as I've never tried it, but possibly your data tasks are all using the same TCP port on your end.
That would be OK if they were serialized - one after the other, in sequence - but if they overlap, then the server would receive garbled HTTP requests:
GET /foo
GET /bar
GET /baz
What the server would see might be something like:
GET /fGET /baroo
GET /baz
That your third requests actually works OK might be an accident of the timing.
If you absolutely require the three requests to be issued simultaneously, there are ways to open three different ports on your end. I don't know how to do it with Cocoa and Objective-C, but you can certainly do it with C and Berkeley Socket system calls. The Cocoa / Cocoa Touch networking methods are just wrappers around sockets.
A couple of thoughts:
Your technique of using a single NSMutableURLRequest instance, and repeatedly mutating it for each request (while the prior requests are still in progress) is curious.
In the spirit of thread safety, I would use a separate NSMutableURLRequest for each concurrent request. You don't want to risk having your thread issuing these requests mutate the request object while some background thread performing one of the prior requests. (See Apple's Thread Safety Summary in the Threading Programming Guide in which they point out that mutable classes are not generally thread safe.)
Having said that, the NSURLConnection documentation leaves us with the impression that this request object would be copied, mitigating this problem. I don't see this sort of assurance in the NSURLSession documentation (though I suspect it does the same thing).
I don't think this is the problem here (if this was the problem, the problem would likely be more erratic than what you report, and besides, I suspect that NSURLSession is handling this gracefully, anyway), but as a matter of good thread-safe coding habits, it would be prudent to let each concurrent request have its own NSMutableURLRequest object.
You have confirmed that the information being used in the requests looks valid.
If you wanted to take this to the next level, you might use Charles (or Wire Shark or whatever tool you prefer) to observe the actual requests as they go out. These sorts of tools are invaluable for debugging these sorts of problems.
If you observe the requests in Charles and confirm that they are valid, then this categorically eliminates client-side issues from the situation.
What is curious is that you are not receiving NSError object from dataTaskWithRequest. Nor are you receiving statusCode other than 200 from your server. That means that your requests were successfully sent to the server and received by the server.
Instead, the server is processing the request, but is having a problem fulfilling the request. This leads me to wonder about the server code, itself. I suspect that there is something in the server code that is preventing concurrent operations from taking place (e.g., locking some shared resource, such as temp file or SQL table, for the duration of the request). I would take a hard look at the server code and make sure there are no potential contention issues.
Furthermore, I would modify the server code to not simply report "error", but rather to produce a meaningful error message (e.g. system provided error messages, error codes, etc.). Your server is detecting an error, so you should have it tell you precisely what that error was.
Note, I am explicitly not advising you to make your requests run sequentially. That is inadvisable. While it might solve the immediate problem, you pay a huge performance penalty doing that, and it's not scalable. And remember, you really must handle concurrent requests gracefully, as you're likely to have multiple users of the app at some point.
I would take a hard look at the server code, adding further debugging information to the error messages in order to track down the problem.
I put request into for loop, it works. The first thought of rob about NSMutableRequest and NSURLSession seems right, I'm trying to catch the whole idea. Thanks for rob's answer. Anyway, this is code.
for (ImageInformation *temp in self.images.imageInfos) {
// compose request dynamically
NSURL *url = [NSURL URLWithString:#"http://121.199.35.173:8080/xihuan22dcloud/services/Shibietupianservice/serviceGetthetupian"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = #"POST";
NSString *data = [NSString stringWithFormat:#"uid=%#&yangzhengma=%#&tupianid=%#", uid, checkCode, temp.imageId];
request.HTTPBody = [data dataUsingEncoding:NSUTF8StringEncoding];
// data task
dispatch_async(self.serialQueue, ^{
[[self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
// if client side is no errors, continue
if (!error) {
// if server side is no errors, continue
NSHTTPURLResponse *httpResp = (NSHTTPURLResponse*) response;
if (httpResp.statusCode == 200) {
// in ram and put into images' imageRawData array
[self.images parseImageRawData:[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] withImageId:temp.imageId];
// store data to disk
// [FCFileManager writeFileAtPath:path content:data];
// dispatch display image task to main
dispatch_async(dispatch_get_main_queue(), ^{
if ([self.images.imageDrawDatasDic count] == [self.images.imageInfos count]) {
[self.tableView reloadData];
}
});
}
}
}] resume];
[NSThread sleepForTimeInterval:0.5];
});
}
}
I have video on my server and i want to know in how much time my video requires to download from server in the device. So that i can calculate the download time and check if server is slow or not. Any help would be appreciated.
Here is my code:
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:url] cachePolicy:NSURLCacheStorageNotAllowed timeoutInterval:[[Configuration sharedInstance] fatchTimeOut]];
[GMURLConnection sendAsynchronousRequest:request queue:self.operationQueue type:type withSubType:subType completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
//**********************Success Handling ********************
if (type == OperationTypeMeta) {
failCount = 0;
NSError *error = nil;
NSArray *serverResponse=[NSArray array];
if(data)
serverResponse = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
}];
You cannot use sendAsynchronousRequest and monitor connection status. As per apple docs:
To retrieve the contents of a URL using a completion handler block: If
you do not need to monitor the status of a request, but merely need to
perform some operation when the data has been fully received, you can
call sendAsynchronousRequest:queue:completionHandler:, passing a block
to handle the results.
So you have to implement NSURLConnection delegate. If you like block style(as I really really do), just create new class which implement delegate methods and takes NSURLRequest, status block and completion block as params and call them in appropriate delegate methods. This way you will have block-styled asynchronous requests with status updates.
If you do not know how to measure remaining time - each time didReceiveData: method is called append data length to class variable, and calculate how much time passed since last method receive. This way you have velocity(bytes per second). Knowing how big is your downloaded file just divide remaining size to your velocity and you'll get remaining time. There are some neat algoritms out there to get neat/steady remaining time instead of just jumping from 30secs to 2 mins(if we assume our internet connection is tricky). I've personally used #Ben Dolman's solution posted over here:
averageSpeed = SMOOTHING_FACTOR * lastSpeed + (1-SMOOTHING_FACTOR) * averageSpeed;
With some tweaking and playing with SMOOTHING_FACTOR I've accomplished great results.
I suggest you to use AFHTTPRequestOperation. Like this:
AFHTTPRequestOperation* operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead){
}];
You may use NSURLConnection with its delegate.
When download file first you get response from server which has total file size
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
_expectedContentLength = [response expectedContentLength];
if (_expectedContentLength==NSURLResponseUnknownLength)
_expectedContentLength = 0;
}
After in - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data you may calculate downloading time from time spent to download portion of file and whole file size
I'm using synchronous requests for the first time and would love some help. (The code I'm writing is solely for my own use, and given its purposes synchronous requests are not a problem.)
The code gets data from a web page in a series, manipulates the data, moves on to the next page in the series, manipulates THAT data, and so on. I'm using a synchronous request because I need the connection to finish loading and the data to be manipulated before the function loops to the next page.
Here's my looping code:
-(NSData *)myMethod {
NSString *string;
NSData *data;
for (int x = 1; x<100; x++) {
string = [NSString stringWithFormat:#"http://www.blahblah.com/%d",(x)];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:string]];
NSURLResponse *response = nil;
NSError *error = nil;
data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
}
return data;
}
When I was using connectionWithRequest, I just put the code to manipulate the data in connectionDidFinishLoading and it worked fine. But with sendSynchronousRequest, even though NSLog shows that the loop code is looping, the code in connectionDidFinishLoading never runs.
How can I fix this?
(Or am I taking the wrong approach completely?)
Here's how to take #nhgrif's good advice to perform asynch and preserve all of the results.
- (void)doRequest:(NSInteger)requestIndex gatheringResultsIn:(NSMutableArray *)array completion:(void (^)(void))completion {
if (requestIndex < 100) {
NSString *string = [NSString stringWithFormat:#"http://www.blahblah.com/%d",(requestIndex)];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:string]];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (data) [array addObject:data];
[self doRequest:requestIndex+1 gatheringResultsIn:array completion: completion];
}];
} else {
completion();
}
}
This will run 100 requests indexed 0..99 placing the results in a mutable array. Call it like this:
NSMutableArray *results = [NSMutableArray array];
[self doRequest:0 gatheringResultsIn:results completion:^{
NSLog(#"100 NSData objects should be here: %#", results);
}];
connectionDidFinishLoading is an NSURLConnection delegate method for when you've sent asynchronous requests. Normally, you'd implement this method to get the data that loaded, but you don't need to do this, as it's returned synchronously and assigned to your data variable.
I will note however, you are definitely taking a poor approach here.
First of all, if you'd use asynchronous requests here, you could query all 100 URLs as basically the same time and let them return in their own time.
But what's more problematic is what actually happens with your code.
We create a URL, send the synchronous request, and when it finishes, assign the return to data.
... then we loop. And do this 99 times. 99 times we make this synchronous request (to a different URL each time) and overwrite the data that the previous request loaded. And after the 100th time, we exit the loop and return the data we downloaded in the final request.
I want to call a unspecified number of URL requests which must be fired one after other. As the server can´t handle multiple requests with identical user-ID at the same time (only the last request is processed) i have to send my requests in an interval with about 1 seconds of gap. I did that within a dispatch_after block and increasing delays. But this is neither really secure nor elegant.
I´ve been just reading all day about GCD and want to try to change my code to send URL requests in a chain. My server connection class is build upon a NSURLConnection with asynchronuous request. That means it wouldn´t work with dispatch_async as the method call returns immediately back and the next request in the dispatch queue is called (which is probably immediately). But i have to wait for the response of the server until i may send the next request. My server connection class sends back via a delegate, but with dispatch_async it is never sending any deletate callbacks. Anyhow it wouldn´t work this way.
Probably it is better to put all requests into a NSArray and then call a method which will send requests from the array to the connection class and the delegate callback will pop the item from the array and sending the next request till all requests are done. Unfortunately i absolutely have no idea how i could store the requests and parameters in an array. Currently my call looks like that:
- (void)sendSettings
{
//NSLog(#"begins: %s", __FUNCTION__);
dataProtocol = [[BackgroundSoundConnection alloc] init];
[dataProtocol setDelegate:self];
//double delayInSeconds;
//dispatch_time_t popTime;
//delayInSeconds = 0.1f;
if (self.switch1.on)
{
if (![self.pinnedSettings.nextCall.globalId isEqualToString:self.sound.globalId]) {
[dataProtocol requestDataFromServer:[NSString stringWithFormat:#"setBackgroundSoundNextCall/%#", self.sound.globalId] httpMethod:#"PUT" sound:self.sound stickerType:#"nextCall" personMSISDN:nil];
}
} else {
if ([self.pinnedSettings.nextCall.globalId isEqualToString:self.sound.globalId]) {
[dataProtocol requestDataFromServer:[NSString stringWithFormat:#"disableBackgroundSoundNextcall"] httpMethod:#"PUT" sound:nil stickerType:nil personMSISDN:nil];
}
}
if (self.switch2.on)
{
if (![self.pinnedSettings.incomingCalls.globalId isEqualToString:self.sound.globalId]) {
[dataProtocol requestDataFromServer:[NSString stringWithFormat:#"setBackgroundSoundIncoming/%#", self.sound.globalId] httpMethod:#"PUT" sound:self.sound stickerType:#"incomingCalls" personMSISDN:nil];
}
} else {
if ([self.pinnedSettings.incomingCalls.globalId isEqualToString:self.sound.globalId]) {
[dataProtocol requestDataFromServer:[NSString stringWithFormat:#"disableBackgroundSoundIncoming"] httpMethod:#"PUT" sound:nil stickerType:nil personMSISDN:nil];
}
}
if (self.switch3.on)
{
if (![self.pinnedSettings.outgoingCalls.globalId isEqualToString:self.sound.globalId]) {
[dataProtocol requestDataFromServer:[NSString stringWithFormat:#"setBackgroundSoundOutgoing/%#", self.sound.globalId] httpMethod:#"PUT" sound:self.sound stickerType:#"outgoingCalls" personMSISDN:nil];
}
} else {
if ([self.pinnedSettings.outgoingCalls.globalId isEqualToString:self.sound.globalId]) {
[dataProtocol requestDataFromServer:[NSString stringWithFormat:#"disableBackgroundSoundOutgoing"] httpMethod:#"PUT" sound:nil stickerType:nil personMSISDN:nil];
}
}
for (int i = 0; i < [personArray count]; i++)
{
if (![personArray[i] connectedToServer])
{
NSLog(#"sound: %#", [personArray[i] soundId]);
NSLog(#"msisdn: %#", [personArray[i] personMSISDN]);
[dataProtocol requestDataFromServer:[NSString stringWithFormat:#"setBackgroundSoundContext/%#/%#", [personArray[i] soundId], [personArray[i] personMSISDN]] httpMethod:#"PUT" sound:self.sound stickerType:#"contextCalls" personMSISDN:[personArray[i] personMSISDN]];
}
}
[self animateViewAway:self.view];
}
A part of the request parameters is already in an array. I could use this array and push the other request parameters into it and then sending the first parameter. And after server responded send the next request triggered by the callback from the delegate. Probably this would work.
But i´m just wondering if there isn´t andy way to que the requests a dispatch queue. But how could i que the delegates as well? Or what do i have to do that the queue will wait until the server responds? I´d like to avoid rewriting my server connection class from asynchronous to synchronous URLConnection which would probably make the difference.
Can anybody point me to a solution with asynchronous URLConnection and dispatch_async?
I haven´t seen the possibilites of NSOperation and NSOperationQueue yet. In the podcast of Jeff Kelley i´ve heard that the advantage of GCD over NSOperation is the dependencies feature. http://iphreaksshow.com/042-iphreaks-show-concurrency-with-jeff-kelley/
Or did i mix up everything? What would you recommend?
A complete NSURLRequest represents a complete request by containing a path, query params or body, headers, etc. You can build several of these to represent your several server requests.
NSURLConnection provides an asynch send (sendAsynchronousRequest:queue:completionHandler:). A naive way to sequence a series of requests, is to nest the requests in completion blocks as follows...
[NSURLConnection sendAsynchronousRequest:request0 queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (!error) {
[NSURLConnection sendAsynchronousRequest:request1 queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (!error) {
// and so on... yikes, we'll have code in column 1000 pretty soon
But it should be clear that this is a weak idea. You can get the same effect for sequencing an arbitrary number of requests with pretty compact code as follows:
- (void)doManyRequests:(NSArray *)requests withResults:(NSMutableArray *)results completion:(void (^)(void))completion {
if (!requests.count) {
return completion();
}
NSURLRequest *nextRequest = requests[0];
NSArray *remainingRequests = [requests subarrayWithRange:NSMakeRange(1, requests.count-1)];
[NSURLConnection sendAsynchronousRequest:nextRequest queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
[results addObject:data];
[self doManyRequests:remainingRequests withResults:results completion:completion];
}];
}
Now, as you suggested, prepare several requests and place them in an array:
NSURLRequest *request0 = // however you build this for a given user id
NSURLRequest *request1 = // etc.
NSURLRequest *request2 = // etc.
NSArray *requests = #[request0, request1, request2];
NSMutableArray *results = [NSMutableArray array];
[self doManyRequests:requests withResults:results completion:^{
NSLog(#"this will be an array of NSData objects %#", results);
}];
Hi My problem is that i am getting a response from a web service and when i parse it and add to table and reload the table view it is not refreshing . Although if i call the [table reload] in keyboard is shown it gets updated there. Could someone tell me if im missing anything
This is what i am trying to do
- (void) longPoll {
//create an autorelease pool for the thread
#autoreleasepool {
NSLog(#"polling");
VSAppDelegate *var = (VSAppDelegate*)[[UIApplication sharedApplication] delegate];
//compose the request
NSError* error = nil;
NSHTTPURLResponse* response = nil;
//send the request (will block until a response comes back)
NSData* responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSLog(#"polling response is %d",response.statusCode);
//pass the response on to the handler (can also check for errors here, if you want)
[self performSelectorOnMainThread:#selector(dataReceived:) withObject:responseData waitUntilDone:YES];
}
[self performSelectorInBackground:#selector(longPoll) withObject: nil];
}
- (void) startPoll {
[self performSelectorInBackground:#selector(longPoll) withObject: nil];
}
- (void) dataReceived: (NSData*) theData
{
//process the response here
NSError *error = nil;
NSLog(#"polling data is %#",[[NSString alloc] initWithData:theData encoding:NSUTF8StringEncoding]);
NSLog(#"polling data is %#",[[theData base64EncodedString]base64DecodedString]);
NSDictionary *notifDic= [NSJSONSerialization JSONObjectWithData:theData options:kNilOptions error:&error];
//VSViewControllerSplit *split = [[VSViewControllerSplit alloc]init];
[self RecieveFunction:notifDic];
}
try it
dispatch_async(dispatch_get_main_queue(), ^{
[tablrView reloaddata];
});
The dataReceived method doesn't appear to be calling reloadData. I'll assume that RecieveFunction method does, though, but you should confirm that. It's hard to say without seeing RecieveFunction.
The more fundamental issue would appear to be that dataReceived method is creating a new instance of VSViewControllerSplit, calling its RecieveFunction method, and then letting this new VSViewControllerSplit instance fall out of scope (and if using ARC, get deallocated unless you pushed to it, presented it, etc.). You presumably don't want to create a new VSViewControllerSplit every time longPoll calls dataReceived, but rather just reference the existing instance.