Asynchronous NSURLConnection with NSOperation - objective-c

I want to do NSURLConnection in background mode,because it response is having much data.Forums are telling to use Apple's finite length coding to use in didEnterBackground.
but I want to avoid it.instead of it I use following code through NSOperation with NSInvocation as, but it is not working.connectToServer is having NSURLConnection operation.any help please?didReceiveData,didReceiveResponse delegate methods are not called?
NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
selector:#selector(connectToServer)
object:nil];
[queue addOperation:operation];
[operation release];
[queue autorelease];
-(void)connectToServer
{
NSURL *url = [NSURL URLWithString:#"http://www.google.com"];
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url];
NSURLConnection *theConnection = [[[NSURLConnection alloc] initWithRequest:theRequest delegate:self] autorelease];
if( theConnection )
{
webData = [[NSMutableData data] retain];
}
else
{
NSLog(#"theConnection is NULL");
}
}

This kind of question was asked a zillion time. Delegates are not getting called because as of iOS 4 operations are started on a secondary thread. The thread probably exits before the delegates are called that's all.
Keep the connection on the main thread and handle the data in a background thread using GCD.
I'v wrote about all this stuff here : http://cocoaintheshell.com/2011/04/nsurlconnection-synchronous-asynchronous/
EDIT : Updated link.

Apple provides QHTTPOperation, which does just what you want, encapsulates an NSURLConnection within an NSOperation, for a single request. You can find it in Apple's sample code.
QHTTPOperation is actually a subclass of QRunLoopOperation which lets you encapsulate logic which depends on run-loop callbacks in an NSOperation.
The 3rd party ASIHTTPRequest is a similar popular solution, AFAIK it is no longer maintained though.

Look at the sendAsynchronousRequest:queue:completionHandler: method documented here in Apple's Documentation. It allows asynchronous requests with NSURLConnection.
Note this requires iOS5 or higher

Related

NSOperationQueue addOperationWithBlock with return in iOS

I have written method -(void) getStatus:(NSString*) url with return type,
-(NSString) getStatus:(NSString*) statusUrl
{
NSURL *urlObj = [NSURL URLWithString:[statusUrl
stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSError *err;
NSString *response = [NSString stringWithContentsOfURL: urlObj encoding:
NSUTF8StringEncoding error:&err];
return response;
}
But stringWithContentsOfURL is performing operation in main Thread, So while calling this method application struck for a second.
So I need to perform stringWithContentsOfURL function in background thread and after getting the response i want to return the response.
My current code:
-(NSString) getStatus:(NSString*) statusUrl
{
NSURL *urlObj = [NSURL URLWithString:[statusUrl
stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSError *err;
NSOperationQueue *rbQueue = [[[NSOperationQueue alloc] init] autorelease];
[rbQueue addOperationWithBlock:^{
NSString *response = [NSString stringWithContentsOfURL: urlObj
encoding: NSUTF8StringEncoding error:&err];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
return response;
}];
}];
return #"";
}
But now me receiving a empty string #"", I can not receive response i got from server. Is there any way to do the same task..
Have you noticed that there should be some changes with this approach to complete the task? You hardly can use getters this way because of the nature of asynchronous methods. Or the getters will block the main thread in their turn.
To avoid this I could recommend you to use NSNotification to update the UI after you complete the server request in the background thread;
or change your getter method definition to pass the result from the background thread to the main thread, but asynchronously:
- (void)getStatusAsychronously:(NSString *)statusUrl withCompletionBlock:(void(^)(NSString *result))completionBlock;
or even consider subclassing NSOperation object for server request. The NSOperation subclasses are really handy with NSOperationQueue instances and could provide some more useful features like the cancellation of operation.

Calling a http Request in background failing inside dispatch serial queue

I have a http request to make whenever a new location has been found asynchronously, for handling request, i have create a class called background requester which takes care of all these requests. The following code sample is as follows.
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
dispatch_queue_t queue;
queue = dispatch_queue_create("com.test.sample", NULL); //create a serial queue can either be null or DISPATCH_QUEUE_SERIAL
dispatch_async(queue,
^{
if (bgTask == UIBackgroundTaskInvalid)
{
bgTask=[[UIApplication sharedApplication]
beginBackgroundTaskWithExpirationHandler:
^{
DDLogInfo(#"Task =%d",bgTask);
DDLogInfo(#"Ending bground task due to time expiration");
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
}
BackgroundRequester *request = [[BackgroundRequester alloc] initwithLocation:self.currentLocation];
[request start];
DDLogInfo(#"Task =%d",bgTask);
DDLogInfo(#"bg Task remaining time=%f",[[UIApplication sharedApplication] backgroundTimeRemaining]);
});
}
//background requester class
//the start function will inturn calll the callAsynchrnously method.
-(void) callAsynchronously:(NSString *)url
{
DDLogInfo(#"Calling where am i from background");
DDLogInfo(#"Url =%#",reqURL);
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLCacheStorageNotAllowed timeoutInterval:20.0f];
responseData = [[NSMutableData alloc] init];
connect = [NSURLConnection connectionWithRequest:request delegate:self];
[connect start];
}
You cannot use connectionWithRequest from a background queue (without scheduling the connection in some runloop). Either
use sendSynchronousRequest (which is fine to do if you're using it from a background queue like this), or
schedule the connection in a run loop. If you dig through the AFNetworking code, you'll see they create a run loop on a dedicated thread, which strikes me as the most elegant solution if you really need the NSURLConnectionDataDelegate methods.
You can also use the main run loop (though I'm less crazy about that solution), e.g., something like:
connect = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
[connect scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
[connect start];
Also note, I'm not using connectionWithRequest (because that starts the connection immediately, which is incompatible with your calling of start; only use start if you use initWithRequest with a startImmediately of NO). If you try to do start in conjunction with connectionWithRequest, it can cause problems.
I think the sendSynchronousRequest is simplest (and saves you from having to write any of the NSURLConnectionDataDelegate methods, too). But if you need the NSURLConnectionDataDelegate methods (e.g. you need progress updates, you're using a streaming protocol, etc.), then use the scheduleInRunLoop method.

Object released within block

I'm pretty sure I know what is happening, however I want to know if there is a nice way of stopping it from happening.
Basically, I have a class method which looks something up from the core data store, and if nothing exists attempts to fetch it from a web server. The core data lookup and request are performed in the managed object contexts performBlock method.
I have the following block of code:
[context performBlock:^{
__block NSError *error;
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:NSStringFromClass([self class])];
[request setSortDescriptors:#[[[NSSortDescriptor alloc] initWithKey:key ascending:asc selector:#selector(caseInsensitiveCompare:)]]];
NSFetchedResultsController *controller = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:context
sectionNameKeyPath:keyPath
cacheName:nil];
[controller performFetch:&error];
if (!controller.fetchedObjects || controller.fetchedObjects.count == 0) {
// Nothing found or an error, query the server instead
NSString *url = [NSString stringWithFormat:#"%#%#", kMP_BASE_API_URL, [self baseURL]];
MPRequest *objRequest = [MPRequest requestWithURL:url];
[objRequest setRequestMethod:#"GET"];
[MPUser signRequest:objRequest];
[objRequest submit:^(MPResponse *resp, NSError *err) {
if (err) {
block(nil, err);
} else {
NSArray *objects = [self createListWithResponse:resp];
objects = [MPModel saveAllLocally:objects forEntityName:NSStringFromClass([self class])];
[controller performFetch:&error];
block(controller, nil);
}
}];
} else {
// Great, we found something :)
block (controller, nil);
}
}];
What is happening, is that the MPRequest object is created, and fired, however the submit method triggers an asynchronous request and thus returns almost instantly. I assume ARC is then releasing the MPRequest object. When the request is performed, the internal request object's delegate no longer exists, as ARC has released it (the MPRequest object is the delegate for the internal request it has). Because of this, the block that the submit method is provided with isn't called.
Is there any way I can prevent ARC for doing this without making the request synchronous?
Edit
The submit method of MPRequest looks like this
_completionBlock = block;
_responseData = [[NSMutableData alloc] init];
[self prepareRequest];
[self prepareRequestHeaders];
_connection = [[NSURLConnection alloc] initWithRequest:_urlRequest
delegate:self];
[self requestStarted];
Your MPRequest object needs to keep itself alive while the connection is running. The simplest way to do this is probably to retain itself when the connection is started, and then release itself after it calls the completion block. Under ARC, the simplest way to do this is
CFRetain((__bridge CFTypeRef)self);
and
CFRelease((__bridge CFTypeRef)self);
Conceptually, the run loop keeps the request alive. This is how NSURLConnection operates. But since MPRequest isn't actually attached to the run loop, it's just a wrapper around NSURLConnection, you need to do some work to keep it alive for the same period of time.
Also note that the NSURLConnection needs to be added to the appropriate runloop. The convenience method will add it to the current thread's runloop, but if you're in a background queue, that's not going to work. You can use something like
_connection = [[NSURLConnection alloc] initWithRequest:request delegate:delegate startImmediately:NO];
[_connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
set this MPRequest outside of the context performBlock:^{...} like so:
__strong MPRequest = // allocate or set the MPRequest.
[context performBlock:^{...}
Now once the performBlock is finished, the MPRequest will not have been released, and still be set to whatever variable you designated it to in the __strong clause.

NSURLConnection didReceiveData not called

I've read through tons of messages saying the same thing all over again : when you use a NSURLConnection, delegate methods are not called. I understand that Apple's doc are incomplete and reference deprecated methods, which is a shame, but I can't seem to find a solution.
Code for the request is there :
// Create request
NSURL *urlObj = [NSURL URLWithString:url];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:urlObj cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30];
[request setValue:#"gzip" forHTTPHeaderField:#"Accept-Encoding"];
if (![NSURLConnection canHandleRequest:request]) {
NSLog(#"Can't handle request...");
return;
}
// Start connection
dispatch_async(dispatch_get_main_queue(), ^{
self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES]; // Edited
});
...and code for the delegate methods is here :
- (void) connection:(NSURLConnection *)_connection didReceiveResponse:(NSURLResponse *)response {
NSLog(#"Receiving response: %#, status %d", [(NSHTTPURLResponse*)response allHeaderFields], [(NSHTTPURLResponse*) response statusCode]);
self.data = [NSMutableData data];
}
- (void) connection:(NSURLConnection *)_connection didFailWithError:(NSError *)error {
NSLog(#"Connection failed: %#", error);
[self _finish];
}
- (void) connection:(NSURLConnection *)_connection didReceiveData:(NSData *)_data {
[data appendData:_data];
}
- (void)connectionDidFinishDownloading:(NSURLConnection *)_connection destinationURL:(NSURL *) destinationURL {
NSLog(#"Connection done!");
[self _finish];
}
There's not a lot of error checking here, but I've made sure of a few things :
Whatever happens, didReceiveData is never called, so I don't get any data
...but the data is transfered (I checked using tcpdump)
...and the other methods are called successfully.
If I use the NSURLConnectionDownloadDelegate instead of NSURLConnectionDataDelegate, everything works but I can't get a hold on the downloaded file (this is a known bug)
The request is not deallocated before completion by bad memory management
Nothing changes if I use a standard HTML page somewhere on the internet as my URL
The request is kicked off from the main queue
I don't want to use a third-party library, as, ultimately, these requests are to be included in a library of my own, and I'd like to minimize the dependencies. If I have to, I'll use CFNetwork directly, but it will be a huge pain in the you-know-what.
If you have any idea, it would help greatly. Thanks!
I ran into the same problem. Very annoying, but it seems that if you implement this method:
- (void)connectionDidFinishDownloading:(NSURLConnection *)connection destinationURL:(NSURL *)destinationURL
Then connection:didReceiveData: will never be called. You have to use connectionDidFinishLoading: instead... Yes, the docs say it is deprecated, but I think thats only because this method moved from NSURLConnectionDelegate into NSURLConnectionDataDelegate.
I like to use the sendAsynchronousRequest method.. there's less information during the connection, but the code is a lot cleaner.
[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){
if (data){
//do something with data
}
else if (error)
NSLog(#"%#",error);
}];
From Apple:
By default, a connection is scheduled on the current thread in the
default mode when it is created. If you create a connection with the
initWithRequest:delegate:startImmediately: method and provide NO for
the startImmediately parameter, you can schedule the connection on a
different run loop or mode before starting it with the start method.
You can schedule a connection on multiple run loops and modes, or on
the same run loop in multiple modes.
Unless there is a reason to explicitly run it in [NSRunLoop currentRunLoop],
you can remove these two lines:
[connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[connection start];
or change the mode to NSDefaultRunLoopMode
NSURLConnection API says " ..delegate methods are called on the thread that started the asynchronous load operation for the associated NSURLConnection object."
Because dispatch_async will start new thread, and NSURLConnection will not pass to that other threat the call backs, so do not use dispatch_async with NSURLConnection.
You do not have to afraid about frozen user interface, NSURLConnection providing only the controls of asynchronous loads.
If you have more files to download, you can start some of connection in first turn, and later they finished, in the connectionDidFinishLoading: method you can start new connections.
int i=0;
for (RetrieveOneDocument *doc in self.documents) {
if (i<5) {
[[NSURLConnection alloc] initWithRequest:request delegate:self];
i++;
}
}
..
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
ii++;
if(ii == 5) {
[[NSURLConnection alloc] initWithRequest:request delegate:self];
ii=0;
}
}
One possible reason is that the outgoing NSURLRequest has been setup to have a -HTTPMethod of HEAD. Quite hard to do that by accident though!

NSArray is empty when I call it from another class (xcode)

I'm kind of a newbie in objective-c and I'm having issues when I call a NSArray from other class. I have a class which handles the parsing of a XML feed and another to manage the UItableview stuff. It's weird because when it's done synchronously (using NSXMLParser methods) all data is shown in the table, but when I use the NSURLConnection to make it asynchronous it parses all the data but the array is empty when it's called. If I call NSLog it shows all data contained the newsStories array when the data is being parsed, but somehow its deleted when I call it.
On the parser class I have and all the methods of the NSXMLParser:
- (void)parseXMLFileAtUrl:(NSString *)URL {
data = [[NSMutableData alloc] init];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:URL] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:request delegate:self];
if (connection) {
data = [[NSMutableData alloc]init];
}
[connection release];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
//Reset the data as this could be fired if a redirect or other response occurs
[data setLength:0];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)_data
{
//Append the received data each time this is called
[data appendData:_data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
//Start the XML parser with the delegate pointing at the current object
_parser = [[NSXMLParser alloc] initWithData:data];
newsStories = [[NSMutableArray alloc] init];
[_parser setDelegate:self];
[_parser setShouldProcessNamespaces:NO];
[_parser setShouldReportNamespacePrefixes:NO];
[_parser setShouldResolveExternalEntities:NO];
[_parser parse];
}
And this is how I call the array:
-(BOOL) loadData{
NSString *latestUrl = [[NSString alloc] initWithString:feed];
if ([latestArray count] == 0) {
news = [[news_parser alloc]init];
[news parseXMLFileAtUrl:latestUrl];
[self setArray:news.newsStories];--- here it says null for Array and for newsItems
}
[latestUrl release];
return YES;
}
- (void)viewDidLoad
{
[super viewDidLoad];
array = [[NSMutableArray alloc]init];
_News.rowHeight =85;
[self loadData];
[self._News reloadData];
}
Any help would be appreciated, thanks guys!
Regards.
... Do you understand what asynchronous means? It means that your function will return and the connection will continue, making the callbacks when it is ready. The way you have this coded up, you start the connection and then immediately try to use the data -- it isn't there yet! You need to wait until after connectionDidFinishLoading before you try to use the array.
Do some more research on what exactly it means to be asynchronous; it seems you're not understanding that.
Edit
Let me clarify, since you seem to have missed my point. Your viewDidLoad function finishes long before your connectionDidFinishLoading callback gets called, and so of course the newsStories array is not there yet. When you call:
[news parseXMLFileAtUrl:latestUrl];
in the loadData function, that doesn't stop and wait for the connection to return; if it were synchronous, it would, but asynchronous does not. (Hence I ask you to research what asynchronous actually means, which apparently you still have not done). Since that call returns and then you immediately try to use the loaded data (long before the connectionDidFinishLoading is called) you naturally don't have any data in there.
From Apple's docs:
Mutable objects are generally not thread-safe. To use mutable objects
in a threaded application, the application must synchronize access to
them using locks. (For more information, see “Atomic Operations”). In
general, the collection classes (for example, NSMutableArray,
NSMutableDictionary) are not thread-safe when mutations are concerned.
That is, if one or more threads are changing the same array, problems
can occur. You must lock around spots where reads and writes occur to
assure thread safety.
Reading this might help you out. Not totally sure if that is what is going on in your app but it looks like a good place to start.