How to Cancel all NSURLConnections in Background in iOS - iphone

I am downloading some Images (approx 200-300 thumb images) on Using NSURLCOnnections. I have array of urls of these images. The connection starts to download these images. But in certain case i want to cancel all these downloading. How it is possible. Can i cancel all these NSURLConnections.

Will be better to create NSOperationQueue and work with NSURLConnection through this queue.
For example:
NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(sendRequest)];
- (void) sendRequest {
NSURL *url = [NSURL URLWithString:#"http://www.google.com"];
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url];
NSURLConnection *theConnection = [[[NSURLConnection alloc] initWithRequest:theRequest delegate:self] autorelease];
...
}
NSOperationQueue has method cancelAllOperations

[connection cancel] is used to cancel an NSURLConnection.

I would use a synchronous NSURLConnection in combination with an NSOperationQueue to download the data:
[queue addOperationWithBlock:^{
NSData *data = [NSURLConnection
sendSynchronousRequest:…
returningResponse:… error:…];
}];
This way you can easily control the number of concurrent downloads and cancel the unfinished ones using [queue cancelAllOperations]. Note that cancelling the queue won’t stop the downloads that have already started, but that won’t be a problem if you’re downloading just small thumbnails and have a decent concurrent download limit.
Synchronous NSURLConnection mode has the advantage of being much easier to use than the asynchronous delegation API.

Related

NSURLConnection Bug when I run It in background.... how can I fix?

I've this code but I can't understand why I must run this on the main thread.
If I run this in the background It doesn't do the post request. Is it a bug?
How can I solve this problem?
- (void)setRead:(MWFeedItem *)entry
{ [self getToken:YES];
NSString *url=[NSString stringWithFormat:#"https://www.google.com/reader/api/0/edit-tag?a=user/-/state/com.google/read&i=%#&T=%#", entry.identifier, token];
[self postRequestWithURLState:url];
}
- (void)postRequestWithURLState:(NSString *)url
{
NSString *bodyRequest = nil;
NSURL *requestURL = [NSURL URLWithString:url];
NSMutableURLRequest *theRequest = [[NSMutableURLRequest alloc] init];
//NSLog(#"-------------- bodyRequest: %#", bodyRequest);
[theRequest setURL:requestURL];
[theRequest setTimeoutInterval:0.5];
[theRequest setHTTPMethod:#"POST"];
[theRequest setHTTPBody:[bodyRequest dataUsingEncoding:NSASCIIStringEncoding]];
[self.oauthAuthentication authorizeRequest:theRequest];
[NSURLConnection connectionWithRequest:theRequest delegate:self];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
this is my call:
-(void)segnaLettura:(MWFeedItem *)item{
[reader setRead:item];
}
- (void) segnaread:(MWFeedItem *)item{
[self performSelectorOnMainThread:#selector(segnaLettura:) withObject:item waitUntilDone:NO];
}
In order for an asynchronous NSURLConnection to function, it requires the thread's runloop to be processed. While new threads automatically get a runloop, it is up to you to run it.
You can learn how to do this in the Threading Programming Guide, and I can explain it more, but most of the time this isn't what you want. Most of the time in iOS, background threads should be managed with NSOperation or GCD. Generally, if you're manually spawning a thread on iOS 4+, you're doing it wrong. There are exceptions, but not often.
The first question here should be "why do I even have a background thread for this?"
If you really need a background thread, then the way you're doing segnaread is probably fine.

Refactoring my code for asynchronous NSURLConnection

There are problems with using synchronous requests. The problem is that I am not really sure how to refactor the code below to use an asynchronous request to accomplish the following two tasks:
Use a UIActivityIndicatorView subview to notify of active fetching;
Implement a Reachability check before the connection request to ensure that there is an Internet connection and that the host is available, i.e. reachabilityWithHostName:#"wwww.mywebhostingisterrible.com".
Currently I have a Fetcher class with a +(Fetcher *)sharedFetcher singleton that is used in all of my view controllers to fetch data. Currently all data is fetched using the following code:
- (NSString *)stringWithUrl:(NSURL *)url {
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30];
NSData *urlData;
NSURLResponse *response;
NSError *error;
urlData = [NSURLConnection sendSynchronousRequest:urlRequest
returningResponse:&response
error:&error];
return [[NSString alloc] initWithData:urlData encoding:NSUTF8StringEncoding];
}
A method that fetches looks like this:
-(NSArray *)fetchDogFoodListing {
NSString *urlString = [NSString stringWithFormat:#"%#query/dogFood/%#/%lli/%#/%#",self.connectionUrl,self.queryCriteria.dogFoodName,self.queryCriteria.dogFoodBrandId,self.queryCriteria.dogFoodIngredients,self.queryCriteria.dogFoodBrand];
NSString *json = [self stringWithUrl:[NSURL URLWithString:urlString]];
return [self.factory buildIKDogFoodArrayWithJSON:json];
}
and a call from the UIViewController in -(void)viewDidLoad looks like this:
self.dogFoods = [[IKFetcher sharedFetcher] fetchDogFoodListing];
I have thought about implementing a delegate in this situation but I am very shaky on how delegation ought to work. I'm not even sure which way it should go. Should the Fetcher have a FetcherDelegate that can notify the view controller of when it is done, so the UI can update? Should the view controller have a delegate so that the fetcher can update the view when stuff is done?
I'm sure based on my questions you can see my inexperience with delegation, so I would appreciate any help.
You could do something like this
dispatch_async(queue, ^{
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30];
NSData *urlData;
NSURLResponse *response;
NSError *error;
urlData = [NSURLConnection sendSynchronousRequest:urlRequest
returningResponse:&response
error:&error];
dispatch_async(dispatch_get_main_queue(), ^{
//.. do stuff with data here...
}
}
or you could just do the sendSynchronousRequest code inside a dispatch_sync if you want the connection to be synchronous, though you should really should be as asynchronous as possible.
you may want to look at the brilliant framework by Mugunth Kumar:
http://blog.mugunthkumar.com/products/ios-framework-introducing-mknetworkkit
I plan on using this for NSURLConnection in my future projects

How can I use NSURLConnection to make connection in background

In one of my previous post someone suggest me to use the
NSURLConnection *conn = [NSURLConnection connectionWithRequest:theRequest delegate:self];
To make connection in a new thread to keep GUI working without freezing during the connection.
But It now I can realize that this doesn't work.
This is the whole method.
- (void)postRequestWithURLState:(NSString *)url
{
NSString *bodyRequest = nil;
NSURL *requestURL = [NSURL URLWithString:url];
NSMutableURLRequest *theRequest = [[NSMutableURLRequest alloc] init];
//NSLog(#"-------------- bodyRequest: %#", bodyRequest);
[theRequest setURL:requestURL];
[theRequest setTimeoutInterval:2.0];
[theRequest setHTTPMethod:#"POST"];
[theRequest setHTTPBody:[bodyRequest dataUsingEncoding:NSASCIIStringEncoding]];
[self.oauthAuthentication authorizeRequest:theRequest];
NSURLConnection *conn = [NSURLConnection connectionWithRequest:theRequest delegate:self];
self.web = conn;
}
That is part of a class called greader. So I use this method in this mode.
reader = [[greader alloc] init];
[reader setDelegate:self];
Than I call it [reader postRequestWithURLState:url].
But in this way it still freeze my app for some seconds. How can I execute this action without freezing my app for some seconds?
This is strange. I cannot see why this method would freeze your UI.
One simple solution is to call the method in separate thread. Make the reader a property (or pass it to the method below):
-(void)postReaderRequest:(NSURL)url {
[self.reader postRequestWithURLState:url];
}
and then
[self performSelectorInBackground:#selector(postReaderRequest)
withObject:[NSArray arrayWithObject:url]];
If the result is the same, it must have been something else.
Try using NSThread to execute your NSURLConnection

How to abort a synchronized URL request in IPhone

I have an API call. It takes more than 2 minute to get a response from the server. how can i abort it manually while it is processing?
Here is my code:
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
[request setTimeoutInterval: 600.0];
[request setHTTPMethod:#"GET"];
NSMutableDictionary* headers = [[[NSMutableDictionary alloc] init] autorelease];
[headers setValue:#"application/json" forKey:#"Content-Type"];
[headers setValue:#"application/json" forKey:#"Accept"];
[headers setValue:val forKey:#"Authorization"];
[request setAllHTTPHeaderFields:headers];
NSError *Error;
NSURLResponse *response = [[NSURLResponse alloc] init];
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&Error];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
how can i abort the request while its processing.
Thanks
You can't. Use asynchronous connections if you want to be able to cancel.
Also, BTW, you have some errors in your code:
NSError *Error;
NSURLResponse *response = [[NSURLResponse alloc] init];
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&Error];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
For good measure, Error should be initialized to nil.
You are leaking response. Initialize it to nil, and sendSynchronousRequest:returningResponse:error: will fill it in (if the request succeeds, anyway).
The line [[NSURLConnection alloc] initWithRequest:request delegate:self] starts an asynchronous connection and releases it, after you already did a synchronous connection for the same request. And it leaks the NSURLConnection object. Did you even implement the necessary methods for the informal delegate protocol on your self object?
You can't, as the thread is being blocked by your synchronous request.
synchronous load is built on top of the asynchronous loading code made available by the class. The calling thread is blocked while the asynchronous (sic) loading system performs the URL load on a thread spawned specifically for this load request.
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/Reference/Reference.html

ASIHTTPRequest: Receive delegates from several requests within a network queue

I use ASINetworkQueue to send two requests, which are in a queue.
My problem is, that I'm not able to receive notifications when
a request fails/is done.
Code:
[networkQueue cancelAllOperations];
[networkQueue setShowAccurateProgress:YES];
[networkQueue setUploadProgressDelegate:self.progressIndicator];
[networkQueue setDelegate:self];
[networkQueue setQueueDidFinishSelector:#selector(queueDidFinish)];
NSURL *urlAttachment = [NSURL URLWithString:#"http://localhost/test1.xml"]];
ASIFormDataRequest *requestFile = [[[ASIFormDataRequest alloc] initWithURL:urlAttachment] autorelease];
[requestFile setFile:filePath forKey:#"attachment[test]"];
[requestFile setDidFailSelector:#selector(test1WentWrong)];
[requestFile setDidFinishSelector:#selector(test1Done)];
[networkQueue addOperation:requestFile]; //queue is an NSOperationQueue
NSURL *url = [NSURL URLWithString:#"http://localhost/test2.xml"]];
ASIFormDataRequest *request = [[[ASIFormDataRequest alloc] initWithURL:url] autorelease];
[request setPostValue:test.filename forKey:#"filename[test]" ];
[request setDidFailSelector:#selector(test2WentWrong)];
[request setDidFinishSelector:#selector(test2Done)];
[networkQueue addOperation:request]; //queue is an NSOperationQueue
[networkQueue go];
test1WentWrong, test1Done, test2WentWrong, test2Done aren't called.
Although the request runs fine and queueDidFinish gets called.
You need to set the delegate of the individual requests rather than the queue.
Basically, if you set the didFinish and didFail selectors on the queue, the queue's delegate is called. If you set them on the request, the request's delegate is called (You can also do both, in which case both get called).
In your case, where you want to use the same delegate for both requests, but different selectors for didFail / didFinish, I can see it would make sense for the queue's delegate to be called if you didn't set a delegate for the request. Perhaps I should add this... :)
Ben