dispatch async hangs on main thread while encoding image - ios

So I am taking this UIImage data and and converting to a string in base64. the problem is that it hangs on the UI thread whilst converting and I am not sure why.
- (void)processImage:(UIImage*)image{
dispatch_queue_t myQueue = dispatch_queue_create("My Queue",NULL);
[self.spinnerOutlet setAlpha:0.0f];
[self.spinnerOutlet startAnimating];
dispatch_async(myQueue, ^{
// Convert image
NSData *myData = [UIImagePNGRepresentation(image) base64EncodedDataWithOptions:NSDataBase64Encoding64CharacterLineLength];
NSString *myString = [NSString stringWithUTF8String:[myData bytes]];
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI
[self showSuccessAlertView:#"Success!" message:#"Submitting Image..."];
snapShotInBase64 = myString;
[self sendImagePostRequest];
});
});
}

Try this code:
- (void)processImage:(UIImage*)image{
dispatch_queue_t myQueue = dispatch_queue_create("My Queue",NULL);
[self.spinnerOutlet setAlpha:0.0f];
[self.spinnerOutlet startAnimating];
dispatch_async(myQueue, ^{
// Convert image
NSData *myData = [UIImagePNGRepresentation(image) base64EncodedDataWithOptions:NSDataBase64Encoding64CharacterLineLength];
NSString *myString = [NSString stringWithUTF8String:[myData bytes]];
snapShotInBase64 = myString;
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI
[self showSuccessAlertView:#"Success!" message:#"Submitting Image..."];
});
});
dispatch_barrier_async(myQueue, ^{
[self sendImagePostRequest];
});
}
or
- (void)processImage:(UIImage*)image{
dispatch_queue_t myQueue = dispatch_queue_create("My Queue",NULL);
[self.spinnerOutlet setAlpha:0.0f];
[self.spinnerOutlet startAnimating];
dispatch_async(myQueue, ^{
// Convert image
NSData *myData = [UIImagePNGRepresentation(image) base64EncodedDataWithOptions:NSDataBase64Encoding64CharacterLineLength];
NSString *myString = [NSString stringWithUTF8String:[myData bytes]];
snapShotInBase64 = myString;
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI
[self showSuccessAlertView:#"Success!" message:#"Submitting Image..."];
dispatch_async(myQueue, ^{
[self sendImagePostRequest];
});
});
});
}
hope will help. If you upload image in server, why you don`t use AFNetworking library

Related

dispatch_async with xml parser Not Working

i am using dispatch_async when parsing xml data in a uitable view here is my code:
- (void) Parse{
dispatch_async( dispatch_get_global_queue(0, 0), ^{
NSString *post =[[NSString alloc] initWithFormat:#"http://messages.xml"];
NSData *xmlData=[[NSData alloc]initWithContentsOfURL:[NSURL URLWithString:post]];
xmlParserObject =[[NSXMLParser alloc]initWithData:xmlData];
[xmlParserObject setDelegate:self];
dispatch_async( dispatch_get_main_queue(), ^{
[xmlParserObject parse];
});
});
[messageList reloadData];
}
now uitableview not showing any data in it. it was working perfectly before dispatch_async
I think you have problem in your code. Please reload table data in dispatch_get_main_queue()
- (void) Parse {
dispatch_async( dispatch_get_global_queue(0, 0), ^{
NSString *post =[[NSString alloc] initWithFormat:#"http://messages.xml"];
NSData *xmlData=[[NSData alloc]initWithContentsOfURL:[NSURL URLWithString:post]];
xmlParserObject =[[NSXMLParser alloc]initWithData:xmlData];
[xmlParserObject setDelegate:self];
[xmlParserObject parse];
dispatch_async( dispatch_get_main_queue(), ^{
[messageList reloadData];
});
});
}

How can I get my asynchronous url images loaded into NSMutableArray in order?

I am trying to load images by their URL and store them in NSMutableArray in order. My current code works properly if I were not to care about storing the images in order, however it stores them not in order. It currently stores the images in the articleImage array based on the speed at which the asynchronous requests are completed. I have tried playing around with insertObject:AtIndex but could not get anything to work. To clarify, the NSMutableArray that I am trying to store the images in (in orderly fashion) is articleImage.
Here is some code from my viewDidLoad:
dispatch_async(dispatch_get_main_queue(), ^{
if(articleInfoJSONArray.count > 0)
{
for(int i=0; i<articleInfoJSONArray.count; i++)
{
[issueID addObject:[[articleInfoJSONArray objectAtIndex:i] objectForKey:#"issueID"]];
[articleID addObject:[[articleInfoJSONArray objectAtIndex:i] objectForKey:#"articleID"]];
NSString *imageLink = [[articleInfoJSONArray objectAtIndex:i] objectForKey:#"articleImage"];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageLink]];
UIImage *image = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
[articleImage addObject:image];
if(articleImage.count == articleInfoJSONArray.count)
[self imagesLoaded];
});
});
}
}
});
Here is my imagesLoaded:
- (void)imagesLoaded
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle: nil];
ViewController * vc = [storyboard instantiateViewControllerWithIdentifier:#"MainView"];
[self presentViewController:vc animated:NO completion:nil];
}
Try to use dispatch_group. A dispatch group monitors work that has been added to it, and it will know when that work is done. :) http://commandshift.co.uk/blog/2014/03/19/using-dispatch-groups-to-wait-for-multiple-web-services/
One way i did an image download is with NSOperationQueue and NSOperation. You could define a NSOperationQueue in your header file:
#property (strong, nonatomic) NSOperationQueue *sequentialOperationQueue;
in your implementation do:
self.sequentialOperationQueue = [[NSOperationQueue alloc] init];
self.sequentialOperationQueue.maxConcurrentOperationCount = 1;
then you can add:
for (NSDictionary *imageDict in imagesToFetch) {
ImageDownloadOperation *imgDownloadOperation = [[ImageDownloadOperation alloc] initWithImageLocationDict:imageDict];
[self.sequentialOperationQueue addOperation:imgDownloadOperation];
}
LogoDownloadOperation is a subclass of NSOperation. this way you always have only one active download and process them in the order you want. For details on NSOperation check the apple doc.
in extract i did in ImageDownloadOperation:
- (void)start {
NSURL *imageUrl = [NSURL URLWithString:self.imageDict[#"imageUrl"]];
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig];
NSURLSessionDownloadTask *downloadPhotoTask = [session
downloadTaskWithURL:imageUrl
completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
if (error) {
self.sessionTask = nil;
[self done];
return;
}
NSData *imageData = [NSData dataWithContentsOfURL:location];
NSBlockOperation *saveImageBlockOperation = [NSBlockOperation blockOperationWithBlock:^{
[SharedAppDelegate.entityManager saveImage:imageData
imageDict:self.imageDict
inManagedObjectContext:SharedAppDelegate.managedObjectContext];
}];
saveImageBlockOperation.qualityOfService = NSQualityOfServiceBackground;
[[NSOperationQueue mainQueue] addOperation:saveImageBlockOperation];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
self.sessionTask = nil;
[self done];
}];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
}
As you can see, i store the imageData via my AppDelegate in CoreData. Instead of my way, you could give the ImageDownloadOperation a pointer to your NSMutableArray, then you can store the data direct in your array.
You could make an array of [UIImage new] then once the task is complete
replace the empty image images[i] = newImage
EDIT
NSMutableArray *imageArray = [NSMutableArray new];
for (int i=0; i<articleInfoJSONArray.count; i++) {
[imageArray addObject:[UIImage new]];
}
for (int i=0; i<articleInfoJSONArray.count; i++) {
dispatch_async(dispatch_get_main_queue(), ^{
//download image
imageArray[i] = downloadedImage;
});
}

Download images serially with async request

I want to download images from server asynchronously but they aren't coming serially. When I make a synchronous request then it downloads serially but creates other problems.
Code:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^(void){
NSString *UrlStr=urlString;
NSURL *imageURL=[NSURL URLWithString:[UrlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSData *tempData=[NSData dataWithContentsOfURL:imageURL];
UIImage *imgData=[UIImage imageWithData:tempData];
dispatch_async(dispatch_get_main_queue(), ^{
if (tempData!=nil) {
((FXImageView *)view).image = imgData;
[_images addObject:imgData];
}
else{
((FXImageView *)view).image = [UIImage imageNamed:#"NoImage.png"];
[_images addObject:[UIImage imageNamed:#"NoImage.png"]];
}
});
})
i also tried NSOperationQueue but wasn't successful.
dispatch_queue_t queue;
queue = dispatch_queue_create("com.example.MyQueue", NULL);
dispatch_async(queue, ^(void){
NSString *UrlStr=urlString;
NSURL *imageURL=[NSURL URLWithString:[UrlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSData *tempData=[NSData dataWithContentsOfURL:imageURL];
UIImage *imgData=[UIImage imageWithData:tempData];
dispatch_async(dispatch_get_main_queue(), ^{
if (tempData!=nil) {
((FXImageView *)view).image = imgData;
[_images addObject:imgData];
}
else{
((FXImageView *)view).image = [UIImage imageNamed:#"NoImage.png"];
[_images addObject:[UIImage imageNamed:#"NoImage.png"]];
}
});
})
https://developer.apple.com/library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html#//apple_ref/doc/uid/TP40008091-CH102-SW6
https://developer.apple.com/library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html#//apple_ref/doc/uid/TP40008091-CH102-SW5
Not the same... I don't have rep to comments. I created a serial queue, u used a concurrent queue.. Follow the link to understand the theory behind.
Try NSOperationQueue with maxConcurrentOperationCount set to 1, but note that in this case you should implement your image download as NSOperation subclass. Please check out Apple documentation on NSOperation to get a clue on how to implement asynchronous NSOperation.

downloading UIImage using grand central dispatch

I have an URL, which when copied into a browser, displays an image. My function is supposed to download the image asynchronously.
- (UIImage *)downloadImage:(NSString *)imageURL
{
NSError *error = nil;
NSURL *urlString = [NSURL URLWithString:imageURL];
NSData *data = [NSData dataWithContentsOfURL:urlString options:NSDataReadingUncached error:&error];
__block UIImage *image;
if (!error) {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
image = [UIImage imageWithData:data];
});
return image;
} else {
NSLog(#"%#", [error localizedDescription]);
}
return nil;
}
When I try to display the image in an UIImageView I get no errors, no nothing. I have NSLogged out both data and the imageURL passed in, and none of those are empty.
Any suggestions?
By calling dispatch_async, you're scheduling that work to happen later. Your function exits with nil before that work is done. You'll want to add a callback block to your function or make it block until you receive and process the image data.
Here is an example of a function with a block callback and how to use it.
- (void)downloadImageAtURL:(NSString *)imageURL withHandler:(void(^)(UIImage *image))handler
{
NSURL *urlString = [NSURL URLWithString:imageURL];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSError *error = nil;
NSData *data = [NSData dataWithContentsOfURL:urlString options:NSDataReadingUncached error:&error];
if (!error) {
UIImage *downloadedImage = [UIImage imageWithData:data];
handler(downloadedImage); // pass back the image in a block
} else {
NSLog(#"%#", [error localizedDescription]);
handler(nil); // pass back nil in the block
}
});
}
- (void)keyboardDidShow:(NSNotification *)aNotification {
[self downloadImageAtURL:#"" withHandler:^(UIImage *image) {
if (image) {
// display
} else {
// handle probelm
}
}];
}
The call to dataWithContentsOfURL:options:error: needs to be within the dispatch_queue block for it to be asynchronous. Any changes to the UI need to be in the mainThread. It should look something like this:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^(void) {
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage* image = [[UIImage alloc] initWithData:imageData];
if (image) {
dispatch_async(dispatch_get_main_queue(), ^{
//load image into UIImageView
}
});
}
});
You are not downloading the image asynchronously. Moreover, a method that is supposed to return a value in an async way cannot return that value through the method return value, but it should return it using a block.
You can try to do something like this:
- (void)downloadImage:(NSString *)imageURL onComplete:(void (^)(UIImage *, NSError * error))onComplete
{
NSURL *urlString = [NSURL URLWithString:imageURL];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSError *error = nil;
NSData *data = [NSData dataWithContentsOfURL:urlString options:NSDataReadingUncached error:&error];
image = [UIImage imageWithData:data];
if (onComplete) {
// Keep in mind that onComplete block will be called on a background thread.
// If you need to use it on UIImageView, you must set it on main thread.
onComplete(image, error);
}
});
}
Then, when you need to set the UIImageView image:
__weak typeof(self)selfB = self; // Better to use a weak reference inside blocks to avoid retain cycles
[self downloadImage:myURLString onComplete:^(UIImage * image, NSError * error) {
dispatch_async(dispatch_get_main_queue(), ^{ // As you can see, we use main thread for UI updates
selfB.imageView.image = image;
});
}];

Nothing happens after calling [table reloadData] when called with dispatch_async

- (void) setRooms:(NSArray *)newRooms
{
NSLog(#"Main thread(cp3)... %d", [rooms count]);
rooms = newRooms;
[table reloadData];
NSLog(#"Main thread(cp4)... %d", [rooms count]);
}
- (void) parseJSONWithURL:(NSURL *)jsonURL
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSLog(#"Main thread(cp1)...%d", [rooms count]);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
NSLog(#"Background thread(cp1)...%d", [rooms count]);
NSError *error = nil;
// Request the data and store in a string.
NSString *resp = [NSString stringWithContentsOfURL:jsonURL
encoding:NSASCIIStringEncoding
error:&error];
// Convert the String into an NSData object.
NSData *data = [resp dataUsingEncoding:NSASCIIStringEncoding];
// Parse that data object using NSJSONSerialization without options.
NSDictionary *json = [[NSDictionary alloc] init];
json = [NSJSONSerialization JSONObjectWithData:data
options:kNilOptions
error:&error];
// Return to the main thread to update the UI elements
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(#"Main thread(cp2)...%d", [rooms count]);
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[self setRooms:[json valueForKey:#"Rooms"]];
});
NSLog(#"Background thread(cp2)...%d", [rooms count]);
});
NSLog(#"Main thread(cp5)...%d", [rooms count]);
}
Try this
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
NSError *error = nil;
// Request the data and store in a string.
NSString *resp = [NSString stringWithContentsOfURL:jsonURL
encoding:NSASCIIStringEncoding
error:&error];
if (error == nil) {
// Convert the String into an NSData object.
NSData *data = [resp dataUsingEncoding:NSASCIIStringEncoding];
// Parse that data object using NSJSONSerialization without options.
NSDictionary *json = [[NSDictionary alloc] init];
json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
dispatch_sync(dispatch_get_main_queue(), ^{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
_rooms = [json valueForKey:#"Rooms"];
[_table reloadData];
});
}
});
or try
[self performSelectorOnMainThread:#selector(udpateAfterFetch:) withObject: [json valueForKey:#"Rooms"] waitUntilDone:YES];
-(void)udpateAfterFetch:(NSArray or whatever *) yourObject
{
_rooms = yourObject;
[_table reloadData];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
The table is not being reloaded because any UI update should happen in the main thread and it is not happening so in ur lines of code.

Resources