dispatch_async NSURLConnection - download don't start - ios

I'm trying to download information in background from a url.
I've read about GCD, Runloops and threads and decided that dispatc_async is my way to go.
After receiving data I aalso want to update the gui.
But... the NSUrlConnection don't seem to start at all. The delegate don't receive any calls.
I'v used this NSUrlRequest and NSUrlConnection in a synchronous way and the delegate got the data excpected.
Here is my code, a method in a viewcontroller;
- (void)dispatch: (NSURLRequest *) pRequest respondTo: (VivaQuery *) pQuery {
dispatch_queue_t downloadQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(downloadQueue, ^{
NSURLConnection *tConnectionResponse =[[NSURLConnection alloc] initWithRequest: pRequest delegate: pQuery];
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Got to main thread.");
[pQuery requestEnd]; // Will update gui, i e aUIView setNeedsDisplay
});
});
}
Anyone got an idea?
Thanks in advance.
Kind regards,
Jan Gifvars
Stockholm

NSUrlConnection does its work asynchronously on it's own background thread so you do not need to create it in a background thread.

Related

NSURLConnection started in another thread. Delegate methods not called

I start a NSURLConnection in another thread:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),
^{
NSURLConnection *connection = [NSURLConnection connectionWithRequest:[request preparedURLRequest] delegate:self];
[connection start];
});
But my delegate method is not called:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData*)data;
When run on the main thread everything is fine. How can I run connection on another thread and get the delegate methods called at the same thread too?
GCD creates, destroys, reuses threads implicitly and there is a chance that the thread you call start from will stop existing immediately afterwards. This may result in the delegate not receiving any callbacks.
If you would like to receive callback in background thread, you can use setDelegateQueue or sendAsynchronousRequest:queue:completionHandler: method:
NSURLConnection* connection = [[NSURLConnection alloc] initWithRequest:request
delegate:self
startImmediately:NO];
[connection setDelegateQueue:[[NSOperationQueue alloc] init]];
[connection start];
The easiest way to start NSURLConnection in the background thread via GCD is:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^{
NSURLResponse* response = nil;
NSError* error = nil;
[NSURLConnection sendSynchronousRequest:request] returningResponse:&response error:&error];
NSLog(#"%#", response);
});
Yes, this is well known behavior of NSURLConnection because it needs a run loop to process the delegate events. The most common solution is (a) instantiate it with initWithRequest:delegate:startImmediately: where startImmediately is FALSE; (b) manually scheduleInRunLoop:forMode: to schedule it in the main run loop; and then (c) start the connection.
But, as you have it here, there's no point in dispatching this to a background queue, as it's already asynchronous so you should just initiate this from the main queue and none of the above is necessary. You use the above pattern in special cases (e.g. you were using NSOperation subclass to manage your requests), but generally it's not needed.
Also, FYI, effective iOS9, NSURLConnection is deprecated, so you should be using NSURLSession, anyway. And NSURLSession doesn’t suffer this limitation.
I had a similar issue. What I'm doing now is running NSURLConnection request in the main thread - it is running asynchronously so it won't slow down your application. In connectionDidFinishLoading, I run the following code to process the results of my calls. I perform the check because I have NSURLConnection call which may trigger other network calls. Since they are already running on a background thread I don't want to start a new one.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if ([NSThread isMainThread]) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
//Background Thread
[self processFinishLoading:connection];
});
}
else {
[self processFinishLoading:connection];
}
}

Why AsyncSocket can't connect to host when using dispatch_async() method?

I'm a little confused when using dispatch_async method to use AsyncSocket.
Here's my code
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
_socket = [[AsyncSocket alloc] initWithDelegate:self];
[_socket connectToHost:#"192.168.1.122" onPort:9501 error:nil];
[self sendCurrentLocation];
});
I use this code to connect, but It didn't work, event - (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err isn't invoked.
Anybody knows why? thanks.
p.s. forgive my English.
AsyncSocket data transmission is asynchronous, it is placed data in the queue, use threads to send, so we do not need to do it again asynchronously.

Equivalant in Swift performSelectorOnMainThread waitUntilDone

I wanted to translate this in swift :
dispatch_async(kBgQueue, ^{
NSData* data = [NSData dataWithContentsOfURL: googleRequestURL];
[self performSelectorOnMainThread:#selector(fetchedData:) withObject:data waitUntilDone:YES];
});
It's for using google places api.
I wondering about using a simple NSURLSession request but it seems that the dataWithContentsOfURL do the job of an NSURLSession request ?
Someone ?
dataWithContentsOfURL is discouraged. You should use NSURLSession for asynchronous downloads, or if you prefer the simpler NSURLConnection.
The delegate callbacks tell the main thread when the download is finished - so no need to engage with Great Central Dispatch APIs.
Mundi is right that the better tool here is NSURLSession. But your code can work; just need to use GCD correctly, and deal with the fact that it might fail:
dispatch_async(kBgQueue) {
if let data = NSData.dataWithContentsOfURL(googleRequestURL) {
dispatch_sync(dispatch_get_main_queue()) { self.fetchedData(data) }
} else {
// Here's the problem with dataWithContentsOfURL. You had an error, but you
// don't know what it was. I guess you'll do something here...
}
}

Run code in the background [duplicate]

This question already has answers here:
loading images from a background thread using blocks
(3 answers)
Closed 8 years ago.
I'm trying to download an image from a website and save it as a UIImage but if the user has low connection this can take forever... how can I download it in the background so the user can keep using the app in the meantime?
here is the code:
theIcon.image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://myWebsite.com/Icon.png"]]];
Use GCD.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
// do my background code
dispatch_async(dispatch_get_main_queue(), ^{
// do handling on main thread when done!
});
});
Use AFNetworking.
[imageView setImageWithURL:
[NSURL URLWithString:#"http://i.imgur.com/r4uwx.jpg"]
placeholderImage:[UIImage imageNamed:#"placeholder-avatar"]];
You can perform the selector in background thread while the main foreground thread runs.
[self performSelectorInBackground:#selector(downloadFile) withObject:nil];
- (void) downloadFile {
//download file
//you can show UIAlertView when done
}
In your - (void) downloadFile you can download this big file.
and have an activity indicator show (or not). You can have the activity Indicator become not hidden or hidden and have it startAnimating and stopAnimating will make it spin and stop. This can be referenced from the foreground and background processes.
The quick and dirty way:
NSMutableRequest* request = ... ;
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse* response, NSData* data, NSError* error) {
if (!error) {
// do something with the response data.
}
}];
This approach is sufficient for "a prove of concept", toy programs with simplistic insecure connections, Apple samples, and for hobbyists learning iOS for fun, and for samples which demonstrate anti-patterns ("How you should do it, not!").
If you want a solid approach you need to use NSURLConnection in asynchronous mode and implement the delegates - or use a third party library. ;)

AFNetworking happening on the main thread

I have a question with AFNetworking. I read the documentation here:
http://afnetworking.github.com/AFNetworking/Classes/AFImageRequestOperation.html
It says:
urlRequest:
The request object to be loaded asynchronously during execution of the operation.
Problem is when I execute the request for below, its happening on the main thread. I was able to confirm it by checking if it's in the main thread or not:
AFImageRequestOperation *operation = [AFImageRequestOperation imageRequestOperationWithRequest:urlrequest success:^(UIImage *image) {
if ([NSThread mainThread]){
NSLog(#"this is the main thread");
}
}];
[operation start];
Is there anything I am doing incorrectly? Was I interpreting the documentation wrong? Why is it not asynchronous following the documentation?
KVISH >
However, you can assign a different successCallbackQueue :
dispatch_queue_t backgroundQueue = dispatch_queue_create("com.name.bgqueue", NULL);
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.successCallbackQueue = backgroundQueue;
The image is being loaded in the background and then your success block is being called on the main thread.
The point of this is that the loading of the image which takes some time and would block your UI is done on the background, but once the image is loaded you probably want to do something like set it on a UIImageView and that requires being on the main thread.
Wow, can't believe I didn't notice this myself before asking. In the AFNetworking classes, it has the below:
dispatch_async(requestOperation.successCallbackQueue ?: dispatch_get_main_queue(), ^(void) {
success(operation.request, operation.response, processedImage);
});
So the success is called on the main queue. Case closed.

Resources