Using NSURLConnection sendAsynchronousRequest - objective-c

I have an application in Xcode 4.5.2. It sends a URL request to download an image and sets the image in an image view. I have the following code that works fine to accomplish this:
dispatch_queue_t downloadQueue = dispatch_queue_create("Get Facebook Friend", NULL);
dispatch_async(downloadQueue, ^{
self.firstFriendImage = [UIImage imageWithData:
[NSData dataWithContentsOfURL:
[NSURL URLWithString:
[[self.facebookPhotosAll objectAtIndex:self.randomIndex1]
objectForKey:#"pic_big"]]]];
dispatch_async(dispatch_get_main_queue(), ^{
[self postDownloadTasks:self.topView setLabel:self.firstFriendLabel
withFriendName:self.firstFriendName cropImage:self.firstFriendImage
inImageView:self.friendOneView atYPoint:22];
});
});
So although this code works fine, being new to objective C, I am trying to explore the language a bit to see how else I can do this same thing. So I tried to use the NSURLConnection sendAsynchronousRequest: method (after looking at some examples on here), but I can't seem to get this method to work. This is what I did:
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
NSString *string = [[self.facebookPhotosAll
objectAtIndex:self.randomIndex1]objectForKey:#"pic_big"];
[NSURLConnection sendAsynchronousRequest: [NSURL URLWithString:
string] queue:queue
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){
// Make sure eveything is ok
if(error){
// do something
}
self.firstFriendImage = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
[self postDownloadTasks:self.topView setLabel:self.firstFriendLabel
withFriendName:self.firstFriendName cropImage:self.firstFriendImage
inImageView:self.friendOneView atYPoint:22];
});
}];
So this code doesn't work at all (it's not returning any data). The app crashes when the method is called and I get the following message:
** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSURL _CFURLRequest]: unrecognized selector sent to instance 0xa39dd20'
Can anyone tell me how to accomplish what I did in the first code excerpt using the NSURLConnection sendAsynchronousRequest method?

In the send asynchronousRequest method, your URL string is missing a part. It should be like in your first method that worked:
[[self.facebookPhotosAll objectAtIndex:self.randomIndex1] objectForKey:#"pic_big"]

Related

how do you refresh ABUnknownPersonViewController?

My problem: I have a ABUnknownPersonViewController which needs to get an image from an online database. I have implemented an NSSession to download the image. The thread adds the image to the ABRecordRef then adds it to the ABUnknownPersonViewController. When the controller is pushed on the stack, it doesn't show the image...therfore sadness ensues.
NSString *imageUrl = dict[#"mug"];
__block NSURL *url = [NSURL URLWithString:imageUrl];
__block NSData *urlData = nil;
NSURLSessionConfiguration *sessionConfig =
[NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session =
[NSURLSession sessionWithConfiguration:sessionConfig
delegate:self
delegateQueue:nil];
NSURLSessionDownloadTask *getImageTask =
[session downloadTaskWithURL:[NSURL URLWithString:imageUrl]
completionHandler:^(NSURL *location,
NSURLResponse *response,
NSError *error) {
urlData = [NSData dataWithContentsOfURL:url];
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
newPerson = controller.displayedPerson;
ABPersonSetImageData(newPerson, (__bridge CFDataRef)(urlData), &anError);
controller.displayedPerson = newPerson;
if(urlData != nil) {
NSLog(#"I got here");
[self viewWillAppear:YES];
[[NSNotificationCenter defaultCenter] postNotificationName:#"com.razeware.imagegrabber.imageupdated" object:(__bridge id)(newPerson)];
}
});
}];
[getImageTask resume];
**Oddly enough, if I choose "Create New Contact" the image appears (So, the thread is working?). If I click cancel on iPhone then the image appears on the ABUnknownPersonViewController. So, it seems like the controller just needs to be refreshed. How?
I've tried [[self view] setNeedsDisplay]; //ain't working'
Help please!
OK - This may not be the best practice. However, since you already have [self viewWillAppear:YES]; you can try calling [self viewWillDisappear:NO]; before viewWillAppear. This is likely to refresh the content.
Sourced from here.
I had a similar problem recently and got this from somewhere else. Try adding a dispatch sync after the async to the main queue like this:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);
dispatch_async(queue, ^{
dispatch_sync(dispatch_get_main_queue(), ^{
// your image fetch code here
});
});
I found a solution:
ABRecordRef cannot be updated in a thread. It must be updated on the main thread.

Code execution with sendAsynchronousRequest

A variable is being set to (null) due to the sendAsynchronousRequest not completing before the request is complete. See code:
main.m:
GlobalSettings *globalsettings = [[GlobalSettings alloc] init];
NSString *url = [globalsettings facebookLink];
NSLog(#"URL: %#", url);
So, inside GlobalSettings:
-(NSString *)facebookLink
{
__block NSString *strReturn;
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://urlEditedOut/"]];
__block NSDictionary *json;
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
json = [NSJSONSerialization JSONObjectWithData:data
options:0
error:nil];
strReturn = json[#"FB"];
}];
return strReturn;
}
So this works fine, has been tested inside the completion block. However back in main.m the variable url is being set to (null) due to (i assume) the async request still connecting / processing request.
How do you combat this so that the variable is saved as the correct value?
The url is set to null because the method returns immediately due to the asynchronous request. The way to avoid this is a delegate. make main the delegate of the GobalSettings and call the delegate method from the completion block. This SO-Post isnt an exact duplicate, but its close enough to get you started.
How to return the ouput if method is using NSURLConnection Asynchronous calls with blocks
Avt's answer is what i would suggest, but returning a block works, too.

How to download image asynchronously using blocks?

I want to click a button to start downloading image and to update my UIImageView to new image once it is updated. The problem with my code is that it only downloads stuff, and not updates. It only updates if I click it again.
I want it to update the image some time in future, when the image is downloaded. How do I do that?
Edit: I have found the wrong code, changing it a bit helped and it all works.
Here comes another question - how do I simplify this code without turning it a mess? It looks excessive.
- (IBAction)getImage
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^{
NSURL *imageURL = [NSURL URLWithString:#"http://example.com/1.jpg"];
__block NSData *imageData;
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^{
imageData = [NSData dataWithContentsOfURL:imageURL];
dispatch_sync(dispatch_get_main_queue(), ^{
self.image = [UIImage imageWithData:imageData];
});
});
});
self.imageView.image = self.image;
}
You are setting the imageView before the image is done downloading, you need to move the logic into the block. Also there is no reason for you to do an extra dispatch_sync inside of your dispatch_async.
- (IBAction)getImage
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^{
NSURL *imageURL = [NSURL URLWithString:#"http://example.com/1.jpg"];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
//This is your completion handler
dispatch_sync(dispatch_get_main_queue(), ^{
//If self.image is atomic (not declared with nonatomic)
// you could have set it directly above
self.image = [UIImage imageWithData:imageData];
//This needs to be set here now that the image is downloaded
// and you are back on the main thread
self.imageView.image = self.image;
});
});
//Any code placed outside of the block will likely
// be executed before the block finishes.
}
Check out https://github.com/rs/SDWebImage
I use it to download images in the background with progress notification. It can be added simply to your project using Cocoapods (http://cocoapods.org).
There are several other async image loaders available on Cocoapods and GitHub if that doesn't work for you.
This is what I've been using although it doesn't provide any progress which I think is often useful. This is simple through.
- (void)downloadImageWithURL:(NSURL *)url completionBlock:(void (^)(BOOL succeeded, NSData *image))completionBlock
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if ( !error )
{
completionBlock(YES,data);
NSLog(#"downloaded FULL size %lu",(unsigned long)data.length);
} else{
completionBlock(NO,nil);
}
}];
}

dispatch_async confliction

Currently i am working on a chatting app, I am trying to upload the image, every thing is working fine except that when image is uploading the UI freeze, so async approach came into the scene, this is what i am trying to do:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
[self dismissModalViewControllerAnimated:YES];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);
dispatch_async(queue, ^{
UIImage *image = [info objectForKey: UIImagePickerControllerOriginalImage];
NSData *imgData = UIImageJPEGRepresentation(image, 1.0);
//[self performSelectorOnMainThread:#selector(send:) withObject:imgData waitUntilDone:YES];
[self send:imgData];
});
}
I am getting this error:
Tried to obtain the web lock from a thread other than the main thread
or the web thread. This may be a result of calling to UIKit from a
secondary thread. Crashing now...
WebThreadLock
-[UITextView setText:]
-[HPTextViewInternal setText:]
-[HPGrowingTextView setText:]
-[chatViewController send:]
__74-[chatViewController imagePickerController:didFinishPickingMediaWithInfo:]_block_invoke_0
_dispatch_call_block_and_release
_dispatch_worker_thread2
_pthread_wqthread
start_wqthread
I am using HPGrowingTextView to give a iMessage kind of expandable typing area for typing messages, but getting this problem.
I searched this error
Tried to obtain the web lock from a thread other than the main thread
or the web thread. This may be a result of calling to UIKit from a
secondary thread
peoples suggests using performSelectorOnMainThread but this approach again freeze the UI.
How to solve this conflict or is there any other approach.
Inside [self send:imageData]
...building a url and appending hFile(imageData)
[body appendData:[NSData dataWithData:hFile]];
[body appendData:[[NSString stringWithFormat:#"\r\n--%#--\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
// setting the body of the post to the reqeust
[request setHTTPBody:body];
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSString *returnString = [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];
NSString *imgUrl = [NSString stringWithFormat:#"http://www.www.www/uImages/thumbs/%#",returnString];
...
after uploading, thumbnail of image is returned, if I use [NSURLConnection sendAsynchronousRequest I get empty thumbnail which I am diplaying in uitableview.
When you want to change anything in the UI you should do it on the main thread.
So if you want to change the text of HPGrowingTextView control you have, you can do the following:
dispatch_async(dispatch_get_main_queue(), ^{
growingTextView.text = #"Some text";
})
You are getting the crash because you are calling send outside the main thread. The stack trace is obvious about this fact.
You need to make those calls on the main thread. When you do, however, your UI will of course hang because of this call...
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
From the method name, it's obvious that this call is synchronous, and will block until the result is returned.
Thus, you need to use the asynchronous form instead.
sendAsynchronousRequest:queue:completionHandler:

AFNetworking EXC_BAD_ACCESS in setCompletionBlock

I'm working on an iOS app and am having some trouble with making a http request using AFNetworking.
When I run the code I get the error: EXC_BAD_ACCESS(code=2 address=0x0).
The error is occurring when I attempt to setCompletionBlock.
I'm new to Objective-C and this has me stumped.
Thank you in advance. Everyone's help is appreciated!
#import "AFNetworking.h"
#import <Cordova/CDV.h>
#import "UploadImg.h"
#implementation UploadImg
- (void) uploadImg:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options{
NSURL *url = [NSURL URLWithString:#"http://test.com/mobile/"];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
NSData *imageData = [NSData dataFromBase64String:[arguments objectAtIndex:1]];
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
[params setObject:#"TEST_STYLE" forKey:#"styleType"];
NSMutableURLRequest *request = [httpClient multipartFormRequestWithMethod:#"POST" path:#"/upload.php" parameters:params constructingBodyWithBlock: ^(id <AFMultipartFormData>formData) {
[formData appendPartWithFileData:imageData name:#"imageName" fileName:#"image.png" mimeType:#"image/png"];
}];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"success");
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"error");
}];
[operation start];
}
#end
Thanks again!
I was able to solve this by changing the build settings.
Under the linking header change the "Other linker flags", click the entry for -weak_library and replace it with -weak-lSystem
From question: Use of Blocks crashes app in iPhone Simulator 4.3/XCode 4.2 and 4.0.2
Welcome to the fun world of Objective-C, #kev_addict!
When you get an EXC_BAD_ACCESS exception, it helps to have the details, to see what the stack trace looks like for the offending call.
Without any additional information, my gut tells me that your problems lie somewhere in the way you're getting the image data from an array. Is there any reason why you wouldn't have this method take a UIImage argument? It seems odd to expect that this method takes an array that expects image data--in the second position, no less.