Upload UIImage in dispatch_async - ios

I have a problem. I call some method in dispatch_async. But in callMethod2 in different object I am uploading image with [NSURLConnection sendAsynchronousRequest. But after upload, It doesn't show me response. (However when I call callMethod2 without dispatch_async, it works great). Where can be the problem?
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
[offline callMethod1];
[offline callMethod2];
});
Upload image
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue currentQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
NSLog("Never show me me this log");
}];

You're calling NSLog on a thread that isn't the main thread (aka calling it asynchronously) so you won't see NSLog as it must run on the main thread.
You can have the completion block notify you some other way when it's done. The best way I've found is using https://github.com/kseebaldt/deferred which allows you to send a promise saying (I promise I'll do this thing and notify you when it's done).

Related

Downloading muliple files in background thread?

I was downloading files at a time or one by one or one only according to user requirements. After downloading files I am sending notification to another view as sucessfull message.
When I download a single file at a time it was successfully downloading the file. But when I was trying to download two or more files within time gap of 6 sec (for pressing another download button), first files are not downloading. It downloads only last file which I have send to download.
Any help would be appreciated.
url=[NSURL URLWithString:currentURL];
NSMutableURLRequest * request = [[NSMutableURLRequest alloc]initWithURL:url];
[request setHTTPMethod:#"GET"];
NSURLConnection *connection=[[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:YES];
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void)
{ //Background Thread
{
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *dataMain, NSError *error)
{
if ([dataMain length]/1024.0f > 600 && error == nil)
{
[dataMain writeToFile:pathOriginal atomically:YES];
NSLog(#"orginal file saved");
}
}];
}
dispatch_async(dispatch_get_main_queue(), ^(void){
[[NSNotificationCenter defaultCenter] postNotificationName:#"TestNotification" object:self];
}); });
Use dispatch async before each call. That way each call will run on a different thread, and will solve your issue.
Hope this helps!
NSURLConnection is deprecated. You should be using NSURLSession for new development. An NSURLSession will handle multiple downloads. (So would NSURLConnection, but it's not worth debugging that given that it's deprecated.

no callback for NSURLConnection sendAsynchronousRequest on auto restart for VoIP app

I am having an issue receiving any completion handler callback from either NSURLConnection sendAsynchronousRequest or AFHTTPRequestOperation. I am developing a VoIP app which works fine when running in the background or foreground. However this problem is present when the app is killed by the OS in which it is then automatically restarted since it contains the voip background mode.
my investigation has found that it could be due to the thread exiting or runloop has stopped in which the callback is not received - I have tested sendSynchronousRequest and this receives the response.
my test code is below - completion handler not called on app auto restart:
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:kAPIHost] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:kHostTimeout];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
NSInteger code = [httpResponse statusCode];
NSLog(#"code %d", code);
}];
Please shed some light on this issue as I may not be calling sendAsynchronousRequest correctly so that the callback is received?
It is weird as the network requests work if the user taps the app to open it.
Thanks for your help

Is NSURLConnection sendAsynchronousRequest completion block executed on the main thread?

I am wondering whether the completionHandler block is called on the main thread, when I use NSURLConnection sendAsynchronousRequest. My main concern is whether I need to dispatch UIKit calls on the main thread by myself.
The document for NSURLConnection did not mention this detail, unless I missed it.
I profiled my code, and there was no memory leak, which kind of indicating that the block was executed on the main thread.
Is there any document that gives definite answer?
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
[self.progress stopAnimating];
if (data != nil) {
...
} else {
[self showMessageIgnoreAcknowledgement:#"Error" message:#"blah"];
}
}];
From the documentation for the NSURLConnection sendAsynchronousRequest:queue:completionHandler: method:
Loads the data for a URL request and executes a handler block on an operation queue when the request completes or fails.
Also from the comment about the queue parameter:
The operation queue to which the handler block is dispatched when the request completes or failed.
Since you are passing in the main queue, the completion handler will be on the main thread (the main queue is on the main thread).

iOS: Why do I need to use sleep(1) after NSURLConnection sendAsynchronousRequest?

I want to display an image on the screen which I take from the internet. I have used
NSURLConnection to create an asynchronous call to take the data and, in the response block, I called the code to assign it to an UIImage object.
My question is why do I need to call sleep(1) after the block execution? If i'm not calling it, then my image is not drawn on the screen. Is it another, more elegant way to achive this?
-(void)loadImage:(NSString *)url
{
NSURL *imageURL = [NSURL URLWithString:url];
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
NSURLRequest *imageRequest = [NSURLRequest requestWithURL:imageURL cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:5.0f];
[NSURLConnection sendAsynchronousRequest:imageRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if(!connectionError) {
if(data) {
//there goes the main thingy
self.myView.wallpaperImage = [UIImage imageWithData:data];
[self.myView setNeedsDisplay];
} else {
NSLog(#"No data found at url:%#",url);
}
} else {
NSLog(#"Could not connect to %#",url);
}
}];
sleep(1);
}
This:
self.myView.wallpaperImage = [UIImage imageWithData:data];
[self.myView setNeedsDisplay];
Is happening on the thread managed by the NSOperationQueue passed to sendAsynchronousRequest. Those methods need to be called from the main thread.
Your sleep may be causing the main thread's runloop to iterate, after which those calls appear to have worked.
To fix this, and to avoid a whole bunch of other problems your current approach will have, do this:
[NSURLConnection sendAsynchronousRequest:imageRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if([data length] > 0) {
//there goes the main thingy
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.myView.wallpaperImage = [UIImage imageWithData:data];
[self.myView setNeedsDisplay];
}];
} else {
// Perform your error handling here.
}
}];
This will use [NSOperationQueue mainQueue] to perform those UIKit calls from the main queue - not libdispatch. libdispatch is a low level interface, it is a recommended best practice to always prefer the higher level interface - in this case, NSOperationQueue. UIKit is only safe when called from the main thread (or queue).
It also changes your error handling behavior to follow the best practices for the platform - check the result of your call (in this case, data) and THEN process any error returned.
Your code is actually a good example of why blocks retain captured objects (in this case self). If there was no retain cycle here, ARC could destroy queue as soon as it goes out of scope, and the block would never execute. Instead, because of the retain cycle, the queue stays around until the block has executed.

Cancel NSData initWithContentsOfURL in NSOperation

I currently have the following code in an NSOperation that has an observer for keyPath "isCancelled":
downloaded = FALSE;
NSURL *url = [NSURL URLWithString:requestString];
dataXML = [[NSData alloc] initWithContentsOfURL:url];
downloaded = TRUE;
I want to make it so that the observeValueForKeyPath function is able to cancel the dataXML continuing or just completely stop the NSOperation once the NSOperation is sent a cancel message. The NSOperation's cancelling operation cancel only notifies the operation that it should stop, but will not force my operation's code to stop.
You can't cancel it.
If you want to be able to cancel the load mid-way through, use NSURLConnection operating in asynchronous mode. It's a bit more work to set up but you can cancel at any point in the download process.
Alternatively, you could use this handy class I wrote that wraps an async NSURLConnection and its delegate in a single method call ;-)
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[[RequestQueue mainQueue] addRequest:request completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (data && error == nil)
{
//do something with your downloaded data
}
}];
//to cancel the download at any time, just say
[[RequestQueue mainQueue] cancelRequest:request];
Easy!
</shamelessSelfPromotion>
Note that the request above is already asynchronous, and the class already manages queuing of multiple requests, so you don't need to (and shouldn't) wrap it in an NSOperationQueue.

Resources