my app hangs when i start downloading using NsurlConnection - iphone

I have an app that needs to download a sound file in order to work correctly.
I am using NSURLConnection asynchronously to download my file which is over 20Mb in size.
I placed a progressBarView in order to track the percentage of the downloading and I am using the delegate methods of NSUrlConnection as suggested by Apple.
NSURLRequest *theRequest=[NSURLRequest requestWithURL:soundFileURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20.0];
// create the connection with the request
// and start loading the data
NSURLConnection *theConnection;
theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
//[theConnection cancel];
//[theConnection start];
if (theConnection) {
// Create the NSMutableData that will hold
// the received data
// receivedData is declared as a method instance elsewhere
receivedData=[[NSMutableData data] retain];
} else {
// inform the user that the download could not be made
}
and delegate methods
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
[receivedData appendData:data];
}
and So ...
When I start downloading, the interface hangs from time to time and the progressView hangs as well.
One notable thing, and maybe another question:
I disabled the user interface so the user has to wait until the download finishes and, of course, I give him a message to tell him that.
Would Apple reject my app for that? I am terribly worried about that
Thank you for reading my question :)

NSUrlConnection send events of NSURLConnectionDelegate to main thread by default. You should create new pool and runloop for this connection and be sure that it processed in background. Here are the example of downloading images in background. It using modified NSOperationQueue and NSOperation but you can easy modify it for downloading your file. LinkedImageFetcher on developer.apple.com

//1 First allocate NSOperationQueue object and set number of concurrent operations to execute at a time
NSOperationQueue *thumbnailQueue = [[NSOperationQueue alloc] init];
thumbnailQueue.maxConcurrentOperationCount = 3;
// load photo images in the background
__weak BHCollectionViewController *weakSelf = self;
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
UIImage *image = [photo image];
dispatch_async(dispatch_get_main_queue(), ^{
// then set them via the main queue if the cell is still visible.
cell.imageView.image = image;
}
});
}];
operation.queuePriority = (indexPath.item == 0) ?
NSOperationQueuePriorityHigh : NSOperationQueuePriorityNormal;
[thumbnailQueue addOperation:operation];
Create Photo class of NSObject and add below method
- (UIImage *)image
{
if (!_image && self.imageURL) {
NSData *imageData = [NSData dataWithContentsOfURL:self.imageURL];
UIImage *image = [UIImage imageWithData:imageData scale:[UIScreen mainScreen].scale];
_image = image;
}
return _image;
}

Related

iphone memory issue while downloading images

I have a wierd problem. The requirement is to download image from a url on swipe and display it in image view .It is working all fine but I am getting memory warnings after 30 images and after few more swipe app crashes.
Implementation is pretty straight forward,but already spent almost 2 days to figure out the issue.
On each swipe i am calling A method :-
-(void)callDownloadImageAPI{
NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
[self loadIndicator:#"Please Wait.!!" :#"Please be patient while we are downloading image for you"];
#try{
DownloadImage *downLoadImge =[[DownloadImage alloc] init];
downLoadImge.delegate=self;
[downLoadImge getImage:[self.allUrlArray objectAtIndex:self.initialImageViewCounter]];
}
#catch (NSException *e) {
NSLog(#"callDownloadImageAPI exception %#",[e description]);
[HUD hide:YES];
}
[pool release];
}
This method download 1 image at a time and send UIImage to its delegate
//Implementation of DownloadImage.h and .m
#protocol DownloadImageDelegate
#required
- (void)messageFormServerWithImage:(UIImage*)imageFromSever;
- (void)gettingImageFailed :(NSString*)errorDesc;
#end
#interface DownloadImage : NSObject
#property(strong) NSURLConnection* connection;
#property(weak) id<DownloadImageDelegate> delegate;
#property(strong) NSMutableData* data;
-(void)getImage:(NSString *)imageUrl;
//DownloadImage.m
-(void)getImage:(NSString *)imageUrl{
#autoreleasepool {
[[NSURLCache sharedURLCache] removeAllCachedResponses];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:imageUrl]cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:60];
self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}
}
- (void)connectionDidFinishLoading:(NSURLConnection*)theConnection {
#autoreleasepool {
NSLog(#"connectionDidFinishLoading");
self.connection=nil;
if( self.data == nil) {
return;
}
// NSString* jsonStr = [[NSString alloc] initWithData:self.data encoding:NSASCIIStringEncoding];
UIImage *img=[UIImage imageWithData:self.data];
// NSArray *messages_json = [parser objectWithString:jsonStr error:nil];
[self.delegate messageFormServerWithImage:img];
self.data = nil;
img= nil;
}
}
Other delegates of NSUrlConnections are implemented but I am not putting it here .
Once this image is returned i am setting this image to scrollview and displaying it and deleting the previous image from scrollview.
More Info:-
Just to verify i commented out setting image to scrollview and just downloaded images on each swipe but still it crashes around 30 images
Surprisingly I am using same class DownloadImage.h and .m to download image at other places in the same work and it work awesome even with 500images .
I am testing in iPod Touch and I checked the memory utilised remain between 12-14mb(never exceed this)
Please help me out guys,let me know if you need more detail.
Its crashing because all the images are being stored in virtual memory, you need to be caching them and then loading them back into memory when the user actually views them.
Try also setting your image objects to nil after they have been cached or are not needed.
In your class I would also recommend using the didReceiveMemoryWarning method and release some of your images from memory when this is called.

Download image asynchronously

I need to download an image from the web and display it in an ImageView. Presently I am using SDWebImage (It is an asynchronous image downloader with cache support, with a UIImageView category).
But it crashes when I click the back button and forward button (when I try to go back and forth of the view repeatedly). Anyway this happens very rarely, but I need to get rid of this bug. Is there any other library (that does not use private API's) that I could use in my project?
Yes. You can user other libary. I've already implemented that using AsyncImageView which is inherited from UIImageView. What it does is it stores images in Cache memory fetched from a url and whenever you need to load image from the same URL again it will simply load it from the cache memory saving a lot of time.
Just follow the link for implementing that:
https://github.com/nicklockwood/AsyncImageView#readme
http://www.markj.net/iphone-asynchronous-table-image/
Please have a look at the image showing the technique I've implemented. It lets you do other activity while images are loading.:
NSURLConnection provides asynchronous downloading and is built into iOS.
I think that the bug that you describe may occur because when you "go back" release some objects that can be delegates of connections that are still running. For avoid crashes, you should cancel the connections before release or dealloc any object that could be a delegate of a running connection.
Another alternative for image async download is http://allseeing-i.com/ASIHTTPRequest/ .
I personally use the built in Grand Central Dispatch feature in iOS to download images from the server asynchronously.
Below is a code I used to fetch photos from Flickr in one of my apps.
In your image/photo class, have a function that is something like this:
- (void)processImageDataWithBlock:(void (^)(NSData *imageData))processImage
{
NSString *url = self.imageURL;
dispatch_queue_t callerQueue = dispatch_get_current_queue();
dispatch_queue_t downloadQueue = dispatch_queue_create("Photo Downloader", NULL);
dispatch_async(downloadQueue, ^{
NSData *imageData = *insert code that fetches photo from server*;
dispatch_async(callerQueue, ^{
processImage(imageData);
});
});
dispatch_release(downloadQueue);
}
In your Photo View Controller, you can call this function like this:
- (void)viewWillAppear:(BOOL)animated
{
[spinner startAnimating];
[self.photo processImageDataWithBlock:^(NSData *imageData) {
if (self.view.window) {
UIImage *image = [UIImage imageWithData:imageData];
imageView.image = image;
imageView.frame = CGRectMake(0, 0, image.size.width, image.size.height);
scrollView.contentSize = image.size;
[spinner stopAnimating];
}
}];
}
Check out EGOImageLoading by enormego for caching images.
It Works just like UIImageView and lets you download images from HTTP asynchronously and also its easy to integrate
//JImage.h
#import <Foundation/Foundation.h>
#interface JImage : UIImageView {
NSURLConnection *connection;
NSMutableData* data;
UIActivityIndicatorView *ai;
}
-(void)initWithImageAtURL:(NSURL*)url;
#property (nonatomic, retain) NSURLConnection *connection;
#property (nonatomic, retain) NSMutableData* data;
#property (nonatomic, retain) UIActivityIndicatorView *ai;
#end
//JImage.m
#import "JImage.h"
#implementation JImage
#synthesize ai,connection, data;
-(void)initWithImageAtURL:(NSURL*)url {
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[self setContentMode:UIViewContentModeScaleToFill];
if (!ai){
[self setAi:[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]];
[ai startAnimating];
[ai setFrame:CGRectMake(27.5, 27.5, 20, 20)];
[ai setColor:[UIColor blackColor]];
[self addSubview:ai];
}
NSURLRequest* request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60];
connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}
- (void)connection:(NSURLConnection *)theConnection didReceiveData:(NSData *)incrementalData {
if (data==nil) data = [[NSMutableData alloc] initWithCapacity:5000];
[data appendData:incrementalData];
NSNumber *resourceLength = [NSNumber numberWithUnsignedInteger:[data length]];
NSLog(#"resourceData length: %d", [resourceLength intValue]);
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"Connection error...");
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[ai removeFromSuperview];
}
- (void)connectionDidFinishLoading:(NSURLConnection*)theConnection
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[self setImage:[UIImage imageWithData: data]];
[ai removeFromSuperview];
}
#end
//Include the definition in your class where you want to use the image
-(UIImageView*)downloadImage:(NSURL*)url:(CGRect)frame {
JImage *photoImage=[[JImage alloc] init];
photoImage.backgroundColor = [UIColor clearColor];
[photoImage setFrame:frame];
[photoImage setContentMode:UIViewContentModeScaleToFill];
[photoImage initWithImageAtURL:url];
return photoImage;
}
//How to call the class
UIImageView *imagV=[self downloadImage:url :rect];
//you can call the downloadImage function in looping statement and subview the returned imageview.
//it will help you in lazy loading of images.
//Hope this will help
I know this is a very old thread but recently i had a lot of random crashes with SDWebImage, so i had to implement my own lazy loading and caching mechanism. It works pretty well, i just haven't tested it in heavy load cases. So here is the .h and .m files followed by the way i use it :
// UIImageView+CustomCache.h
#interface UIImageView(CustomCache)
-(void)startAsyncDownload:(UIImage*)placeHolderImage imageUrlString:(NSString*)imageUrlString;
#end
// UIImageView+CustomCache.m
#import "UIImageView+CustomCache.h"
#implementation UIImageView(CustomCache)
-(void)startAsyncDownload:(UIImage*)placeHolderImage imageUrlString:(NSString*)imageUrlString{
self.image = placeHolderImage;
[NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:
[NSURL URLWithString:imageUrlString]] queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionHandler){
#autoreleasepool {
if (connectionHandler != nil) {
NSLog(#"error in downloading description %#",connectionHandler.localizedDescription);
} else {
ImagesCacheHandler *ref = [ImagesCacheHandler new];
UIImage *imageFromData = [[UIImage alloc] initWithData:data];
if (imageFromData != NULL && imageFromData != nil && data.length > 0) {
self.image = imageFromData;
//custom store to sqlite
[ref archiveImage:imageUrlString imageData:data moc:[ref fetchContext]];
}
}
}
}];
}
#end
And in my table view i use (i import of course UIImageView+CustomCache.h)
UIImageView *imageViewToLazyLoad = (UIImageView*)[cell viewWithTag:1];
[imageViewToLazyLoad startAsyncDownload:[UIImage imageNamed#"palce_Holder_Image_name"] imageUrlString:imageUrl];
I personally prefer using NSURLConnection sendSynchronousRequest and putting a GCD wrapper around it. Keeps everything neat and tidy.

IPhone: File Upload using dispatch_async for background upload

I want to perform a image upload while the running application at the background. I am able to upload the image to the server using the code on this link.
How can I upload a photo to a server with the iPhone?
I heard the NSUrlConnection can be asynchronous and it was used in the EPUploader. In my code, I add some extra method that will create a file in the application directory used for the EPUploader. During this creation of the file, I don't want it to create on the main thread of the application so I wrap all the code including the EPUploader itself with the
dispatch_async on the global queue. That way I won't block the main thread while the file are creating.
It has no problem if I use dispatch_sync but dispatch_async I find something weird when I placed the breakpoint at NSUrlConnection connection :
- (void)upload
{
NSData *data = [NSData dataWithContentsOfFile:filePath];
//ASSERT(data);
if (!data) {
[self uploadSucceeded:NO];
return;
}
if ([data length] == 0) {
// There's no data, treat this the same as no file.
[self uploadSucceeded:YES];
return;
} /* blah blah */
NSURLConnection * connection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];
if (!connection) {
[self uploadSucceeded:NO];
return;
}
else
return;
I went to debug at the breakpoint and instead of going to if statement, the debugger jumps to the first return statement of this method. After that, the selectors I passed on to this class never gets called. This happen only on dispatch_async and it work on dispatch_sync on the global queue.
Does anybody know how to solve this issue?
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_async(queue, ^{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
self.uploadIndex = 0;
ALAsset *asset = [self.assets objectAtIndex:0];
[[FileUploader alloc] initWithAsset:[NSURL URLWithString:#"http://192.168.0.3:4159/default.aspx"]
asset:asset
delegate:self
doneSelector:#selector(onUploadDone:)
errorSelector:#selector(onUploadError:)];
//[self singleUpload:self.uploadIndex];
[pool release];
});
There are a couple of things that should be changed.
Remove the NSAutoreleasePool, it is not needed.
Copy the block to the heap because it's life will probably exceed that of the calling code.
Example:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_async(queue, [[^{
self.uploadIndex = 0;
ALAsset *asset = [self.assets objectAtIndex:0];
[[FileUploader alloc] initWithAsset:[NSURL URLWithString:#"http://192.168.0.3:4159/default.aspx"]
asset:asset
delegate:self
doneSelector:#selector(onUploadDone:)
errorSelector:#selector(onUploadError:)];
} copy] autorelease]);
If you are using ARC (which you certainly are since you should be) there is no need for the copy or autorelease.

NSOperationQueue and ASIHTTPRequest

I'm writing test cases for a wrapper class written around ASIHTTPRequest. For reasons I can't determine, my test cases complete with failure before the ASIHTTPRequest finishes.
Here's how the program flow works.
Start in my test case.
Init my http engine object, instruct it to create a new list
Create the new ASIHTTPRequest object and set it up.
Add the request to an operation queue.
Wait until that queue is empty
Check to see if my delegate methods were called and fail the test if they weren't.
Now, most of the time everything works fine and the test passes, but some of the time it fails because my delegate methods were called AFTER the operation queue returned control to my wait method.
Test Case
// Set my flags to 'NO'
- (void)setUp {
requestDidFinish = NO;
requestDidFail = NO;
}
- (void)testCreateList {
NSString *testList = #"{\"title\": \"This is a list\"}";
JKEngine *engine = [[JKEngine alloc] initWithDelegate:self];
NSString *requestIdentifier = [engine createList:jsonString];
[self waitUntilEngineDone:engine];
NSString *responseString = responseString_;
[engine release];
GHAssertNotNil(requestIdentifier, nil);
GHAssertTrue(requestDidFinish, nil);
GHAssertTrue([responseString hasPrefix:#"{\"CreateOrEditListResult\""], nil);
}
// Puts the test into a holding pattern until the http request is done
- (void)waitUntilEngineDone:(JKEngine *)engine {
[engine waitUntilFinishedRunning];
}
// The delegate method called on successful completion
- (void)requestFinished:(NSString *)requestIdentifier withResponse:(NSString *)response {
NSLog(#"request did finish");
requestDidFinish = YES;
responseIdentifier_ = [requestIdentifier retain];
responseString_ = [response retain];
}
Engine Code
- (NSString *)createList:(NSString *)list {
ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:[NSURL URLWithString:url]];
[request addRequestHeader:#"Content-Type" value:kContentType];
[request setRequestMethod:kPOST];
request.delegate = self;
[request appendPostData:[list dataUsingEncoding:NSUTF8StringEncoding]];
NSString *requestIdentifier = [NSString stringWithNewUUID];
[operationQueue_ addOperation:request];
[operationDictionary_ setObject:request forKey:requestIdentifier];
return requestIdentifier;
}
// This is the ASIHTTPRequest delegate method that's called on success
// but it sometimes isn't called until AFTER the operationQueue finishes running
- (void)requestFinished:(ASIHTTPRequest *)request {
DLog([request responseString]);
BOOL canNotifiyDelegate = [self.delegate respondsToSelector:#selector(requestFinished:withResponse:)];
if (canNotifiyDelegate) {
NSArray *keyArray = [operationDictionary_ allKeysForObject:request];
NSString *requestIdentifier = [keyArray objectAtIndex:0];
[operationDictionary_ removeObjectForKey:requestIdentifier];
if ([keyArray count] != 1) {
ALog(#"It looks like a request was added to the operation dictionary multiple times. There's a bug somewhere.", nil);
}
[self.delegate requestFinished:requestIdentifier withResponse:[request responseString]];
}
}
- (void)waitUntilFinishedRunning {
[operationQueue_ waitUntilAllOperationsAreFinished];
}
This is the way ASIHTTPRequest works. Delegate methods are called on the main thread, and calls to delegates do not block the request thread, so it's perfectly possible your delegates will be called after the queue finishes.
ASIHTTPRequest calls delegate methods on the main thread, by default GH-Unit runs its tests on a background thread. I'm still a little hazy on exactly what was going on, but forcing my network tests to run on the main thread fixed the problem.
I implemented the following method in my network test class.
- (BOOL)shouldRunOnMainThread {
return YES;
}

NSOperation for drawing UI while parsing data?

Hope you guys can help me :)
In the main thread, I create a NSOperation and add it to a queue.
What that operation do is connect to a data server with NSURLConnection, save the receivedData and parse it.
Operation.m
- (void)start
{
NSLog(#"opeartion for <%#> started.", [cmd description]);
[self willChangeValueForKey:#"isExecuting"];
_isExecuting = YES;
[self didChangeValueForKey:#"isExecuting"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:_url];
[request setHTTPMethod:#"POST"];
[request setValue:[NSString stringWithFormat:#"multipart/form-data; boundary=%#", m_BOUNDARY] forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:_postData];
_connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if (_connection == nil)
[self finish];
}
Then in this NSURL delegate method I parse the data I've just received from server.
Operation.m
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[self parseItems];
}
In the data, I can found items like, for instance, screenItem, CellItem, TextItem that I send to the main thread for drawing them while arriving. (I create a UITableView if an itemTable arrives, or I create a UIWebView if an itemWeb arrives)
Using this for sending item to main thread:
Operation.m
- (void) parseItems
{
while ([_data length] > 0)
{
NSInteger type = [self _readByte];
switch (type)
{
case SCREEN:
{
[self _send: [self _readScreen]];
break;
}
case CELL:
{
[self _send: [self _readCell]];
break;
}
// ... A lot of different items
}
}
}
- (void)_send:(CItem*)_item
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"newItem" object:_item];
}
Then in notification receiver:
AppDelegate.m
- (void) _newItemArrived:(NSNotification *) notification
{
[self performSelectorOnMainThread:#selector(processItem:) withObject:[notification object] waitUntilDone:NO];
}
My problem is that the UI is not painted until NSOperation finish. I thought that NSOpertion, being a different thread, would not block the main thread, but believe that is what is happening.
Some tips for this issue?
Thanks a lot for reading!
Are you using NSOperationQueue?
Check out this answer to the question NSOperation blocks UI painting? for a simple example of how to update the UI with a notification from an NSOperation running asynchronously on another thread.
UPDATE
NSURLConnection supports asynchronous connections by itself with a delegate. You should use this. If you have specific issue(s), you should describe those.
Check out the ASIHTTPRequest library.
If you really want to use this approach, you could trying running NSURLConnection synchronously (using the class method sendSynchronousRequest:returningResponse:error:). Your app would remain responsive since the connection is on a background thread. However, you would not be able to update anything until all the data is received.
So I know this is a pretty old question but I ran into the same issue and after hours of going through documentation and blogs I found a great solution in this post from Wim Haanstra http://www.depl0y.com/?p=345
Putting your NSOperation in an infinite loop until you get data back should do the trick!