Need assistance regarding downloading data using NSURLConnection - objective-c

My app is a messaging app, it can also send image files. I am just uploading as image on web server and on other side just sending its url, with NSURLConnection i am trying to download an image with UIProgressView as a download indicator, here is my code:
This method is called when download button in uitableview is clicked, it removes the download button, add uiprogressview and start downloading
-(void)downloadImage:(UIButton *)link
{
UITableViewCell *cell = (UITableViewCell*)[link superview];
NSIndexPath *pathToCell = [tView indexPathForCell:cell];
NSMutableDictionary *checkItHasFile = [messages objectAtIndex:pathToCell.row];
NSString *str = [checkItHasFile objectForKey:#"hasFile"];
if([str isEqualToString:#"1"])
{
progress = [[UIProgressView alloc]initWithProgressViewStyle:UIProgressViewStyleBar];
progress.frame = CGRectMake(10, 50, 160, 30);
progress.progress = 0.0;
//progress.center = CGPointMake(23,21);
[cell addSubview:progress];
}
UIButton *view = [[UIButton alloc]init];
NSArray *subviews = [cell subviews];
for (view in subviews)
{
if([view isKindOfClass:[UIButton class]])
{
[view removeFromSuperview];
}
}
//
NSString *linkToPass = [NSString stringWithFormat:#"THE URL"];
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:linkToPass]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection)
{
nsmd = [[NSMutableData alloc]init];
}
else
{
NSLog(#"Connection to server failed!");
}
...
This method is a NSURLConnection delegate to indicate about response, in this I am calculating response size
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[self.resourceData setLength:0];
self.filesize = [NSNumber numberWithLongLong:[response expectedContentLength]];
}
This method is a NSURLConnection delegate to indicate about received data, I am updating progress bar by doing some calculation
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
resourceData = [[NSMutableData alloc]init];
[self.resourceData appendData:data];
NSNumber *resourceLength = [NSNumber numberWithUnsignedInteger:[self.resourceData length]];
[self.progress setProgress:[resourceLength floatValue] / [self.filesize floatValue] animated:YES];
}
I want to know when downloding is finished so i can also remove progress view for that the delegate method is connectionDidFinishLoading:connection of NSURLConnection.
Problem is it immediately triggers, so when progress view is animating the download progress this method also executes, if I am removing the progress view here, the progress view will disappear immediately with out indicating a complete download progress.
How to solve this issue?

Define a method that removes the progressbar, then in your - (void)connectionDidFinishLoading:(NSURLConnection *)connection implementation, if the resource length is less than a certain amount you decide, call the remove method with a delay:
[self performSelector:#selector(removeProgressBar) withObject:nil afterDelay:2];
otherwise call the same method without any delay.

Related

iOS: SDWebImageManager not caching image

I'm creating a slideshow using UIImageView, and the image links are in an array , so while I was at it, I learned that SDWebImageManager lets hit the URLs once only and then it caches the images for later use.
But what I'm monitoring in my app is that the 1st image is cached, I believe, but the 2nd image URL is always being hit.
Here's my code:
- (void)viewDidLoad {
[super viewDidLoad];
arry = [[NSMutableArray alloc] init];
[arry addObject:#"http://adjingo.2cimple.com/content/151/Image/6291.jpg"];
[arry addObject:#"http://adjingo.2cimple.com/content/151/Image/6290.jpg"];
NSURL *imageURL = [NSURL URLWithString:[arry objectAtIndex:0]];
__block UIActivityIndicatorView *activityIndicator;
__weak UIImageView *weakImageView = self.imageView;
SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager downloadImageWithURL:imageURL
options:0
progress:^(NSInteger receivedSize, NSInteger expectedSize) {
// progression tracking code
if (!activityIndicator) {
[weakImageView addSubview:activityIndicator = [UIActivityIndicatorView.alloc initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]];
//activityIndicator.center = self.imageView.center;
[activityIndicator startAnimating];
}
}
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
if (image) {
// do something with image
[activityIndicator removeFromSuperview];
activityIndicator = nil;
[self.imageView setImage:image];
}
}];
//Timer to do slideshow for images
timer = [NSTimer scheduledTimerWithTimeInterval: 5.0
target: self
selector: #selector(handleTimer:)
userInfo: nil
repeats: YES];
}
Here's the handleTimer code, to reload image in image view, every 5 seconds:
-(void) handleTimer: (NSTimer *) timer {
currentImage++;
if ( currentImage >= arry.count )
currentImage = 0;
__block UIActivityIndicatorView *activityIndicator;
__weak UIImageView *weakImageView = self.imageView;
SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager downloadImageWithURL:imageURL
options:0
progress:^(NSInteger receivedSize, NSInteger expectedSize) {
// progression tracking code
if (!activityIndicator) {
[weakImageView addSubview:activityIndicator = [UIActivityIndicatorView.alloc initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]];
//activityIndicator.center = self.imageView.center;
[activityIndicator startAnimating];
}
}
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
if (image) {
// do something with image
[activityIndicator removeFromSuperview];
activityIndicator = nil;
[self.imageView setImage:image];
}
}];
}
Here's where I monitor the network usage:
Please guide me if I have used the SDWebImageManagerwrongly.
Thanks
my mother language is Chinese,not English.Maybe I cannot express my thought clearly,while I will try my best to tell my idea.if I confuse you , I feel sorry.
I cannot pod SDWebimage because my country blocks google sometimes,so I cannot reproduce your scenarios.While I still give your some advice which may help you
First of all, your gave us little context.Maybe you can post more information about member variables and properties.When I copy your code to the Xcode.I need add them by myself.
Second,you mean when you use
NSURL *imageURL = [NSURL URLWithString:[arry objectAtIndex:1]];,
sdwebimage hits urls every time , not use cache URLs? you can get the image source by NSLog the cacheType.
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
if (image) {
NSLog(#"%d",cacheType);
[activityIndicator removeFromSuperview];
activityIndicator = nil;
[self.imageView setImage:image];
}
}];`
SDImageCacheTypeNone means image comes from network.
SDImageCacheTypeDisk means image comes from disk.
SDImageCacheTypeMemory means image comes from Memory.
Third,because the downloadWithURL:options:completed: is excuted not on the main thread. I doubt the sequence is the same with your thought.

svprogresshud not showing in xcode5 ios7

I am trying to use SVProgressHUD with cocoapods. I have install it in my xcode workspace following numerous tutorials online. It seems simple enough to use, but I cannot get it to work. Here my code to load data from rottentomatoes app. I want to show "loading" while the network request is doing its thing. I am wondering if this is ui thread issue ? I added a sleep inside the network request because the results were coming back too fast !
- (void) reload{
[SVProgressHUD showWithStatus:#"Updating" maskType:SVProgressHUDMaskTypeBlack];
NSString *url = #"http://api.rottentomatoes.com/api/public/v1.0/lists/dvds/top_rentals.json?";
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
[NSThread sleepForTimeInterval:3];
NSDictionary *object = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSMutableArray *array = [[NSMutableArray alloc]initWithArray:[object objectForKey:#"movies"]];
self.movies = [[NSMutableArray alloc]init];
for(NSDictionary *item in array){
SFMovie *movie = [[SFMovie alloc]initWithDictionary:item];
[self.movies addObject:movie];
}
[SVProgressHUD dismiss];
[self.tableView reloadData];
}];
}
EDIT for comment:
The reload is called on both init methods (since I am using storyboard for this project)
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
[self reload];
}
return self;
}
- (id) initWithCoder:(NSCoder *)aDecoder{
self = [super initWithCoder:aDecoder];
if(self){
[self reload];
}
return self;
}
Second EDIT:
I added a pull down to refresh and the "updating ..." shows up when I pull the tableview down. But it does not show up on init.
- (void)viewDidLoad
{
[super viewDidLoad];
UIRefreshControl *refreshControl = [UIRefreshControl new];
[refreshControl addTarget:self action:#selector(refresh:) forControlEvents:UIControlEventValueChanged];
refreshControl.attributedTitle = [[NSAttributedString alloc] initWithString:#"Pull to refresh..."];
self.refreshControl = refreshControl;
}
- (void)refresh:(UIRefreshControl *)sender{
[self reload];
[sender endRefreshing];
}
So I guess I cannot run UI stuff on init methods as the view is not ready. I am now calling the reload method on viewdidload and it works file. Thank you John !
In any init methods, messing with the view hierarchy is tricky business -- keep that logic in viewDidLoad to avoid any of those modifications from being wiped (especially from storyboard initialization).

Accurate progress displayed with UIProgressView for ASIHTTPRequest in an ASINetworkQueue

Summary: I want to track the progress of file downloads with progress bars inside cells of a tableview.
I'm using ASIHTTPRequest in an ASINetworkQueue to handle the downloads.
It works, but the progress bars stay at 0%, and jump directly at 100% at the end of each download.
Details:
I set up my ASIHTTPRequest requests and ASINetworkQueue this way:
[Only an extract of my code]
- (void) startDownloadOfFiles:(NSArray *) filesArray {
for (FileToDownload *aFile in filesArray) {
ASIHTTPRequest *downloadAFileRequest = [ASIHTTPRequest requestWithURL:aFile.url];
UIProgressView *theProgressView = [[UIProgressView alloc] initWithFrame:CGRectMake(20.0f, 34.0f, 280.0f, 9.0f)];
[downloadAFileRequest setDownloadProgressDelegate:theProgressView];
[downloadAFileRequest setUserInfo:
[NSDictionary dictionaryWithObjectsAndKeys:aFile.fileName, #"fileName",
theProgressView, #"progressView", nil]];
[theProgressView release];
[downloadAFileRequest setDelegate:self];
[downloadAFileRequest setDidFinishSelector:#selector(requestForDownloadOfFileFinished:)];
[downloadAFileRequest setDidFailSelector:#selector(requestForDownloadOfFileFailed:)];
[downloadAFileRequest setShowAccurateProgress:YES];
if (! [self filesToDownloadQueue]) {
// Setting up the queue if needed
[self setFilesToDownloadQueue:[[[ASINetworkQueue alloc] init] autorelease]];
[self filesToDownloadQueue].delegate = self;
[[self filesToDownloadQueue] setMaxConcurrentOperationCount:2];
[[self filesToDownloadQueue] setShouldCancelAllRequestsOnFailure:NO];
[[self filesToDownloadQueue] setShowAccurateProgress:YES];
}
[[self filesToDownloadQueue] addOperation:downloadAFileRequest];
}
[[self filesToDownloadQueue] go];
}
Then, in a UITableViewController, I create cells, and add the name of the file and the UIProgressView using the objects stored in the userInfo dictionary of the request.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"fileDownloadCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"FileDownloadTableViewCell" owner:self options:nil];
cell = downloadFileCell;
self.downloadFileCell = nil;
}
NSDictionary *userInfo = [self.fileBeingDownloadedUserInfos objectAtIndex:indexPath.row];
[(UILabel *)[cell viewWithTag:11] setText:[NSString stringWithFormat:#"%d: %#", indexPath.row, [userInfo valueForKey:#"fileName"]]];
// Here, I'm removing the previous progress view, and adding it to the cell
[[cell viewWithTag:12] removeFromSuperview];
UIProgressView *theProgressView = [userInfo valueForKey:#"progressView"];
if (theProgressView) {
theProgressView.tag = 12;
[cell.contentView addSubview:theProgressView];
}
return cell;
}
The progress bar are all added, with the progress set to 0%.
Then, at end of download, they instantly jump to 100%.
Some of the download are very big (more than 40Mb).
I do not do anything tricky with threads.
Reading the forums of the ASIHTTPRequest, it seems I'm not alone, but I couldn't find a solution.
Am I missing something obvious? Is this a bug in ASI* ?
ASIHTTPRequest can only report progress if the server is sending Content-Length: headers, as otherwise it doesn't know how big the response will be. (ASINetworkQueue also sends HEAD requests at the start to try to figure out document sizes.)
Try collecting all the network traffic with charlesproxy or wireshark, see if these headers are present and/or what is happening with the HEAD requests.

UIActivityIndicatorView when loading image from online?

I have a simple script that displays in a UIImageView from the internet.
Is there a way to display a UIActivityIndicatorView or something of that sort to show the user that it's loading?
Here's my minimal code:
NSString *imagePath = [NSString stringWithFormat:#"urlofimagehere.jpg"];
NSData *mydata = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:imagePath]];
UIImage *myimage = [[UIImage alloc] initWithData:mydata];
[imageView setImage:myimage];
[myimage release];
Thanks!
Coulton
EDIT: CODE REMOVED
-[NSData initWithContentsOfURL:]
is a synchronous method. This means it will block until the data is loaded over the network. If you want to be able to keep your UI responsive, you'll want to use an asynchronous approach. For more info, read this: http://akosma.com/2010/05/28/initwithcontentsofurl-methods-considered-harmful/
I'd recommend using NSURLRequest with an NSURLRequestDelegate to receive callbacks when the data is finished loading. Here's an example:
- (void)loadImageAtURL(NSString *)urlString {
[myUIActivityIndicatorView startAnimating];
// SHOW NETWORK INDICATOR
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
// SET UP THE REQUEST
NSURL *url = [[NSURL alloc] initWithString:urlString];
NSURLRequest *request = [[[NSURLRequest alloc] initWithURL:url] autorelease];
// SET UP THE CONNECTION
connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
mutData = [[[NSMutableData alloc] init] retain];
}
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error {
// Handle error
}
- (void)connection:(NSURLConnection *)connection
didReceiveData:(NSData *)data {
[mutData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)aConnection {
// ONCE LOADED HIDE NETWORK INDICATOR
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
// RELEASE THE CONNECTION
[connection release];
// CREATE NEW UIIMAGE FROM THE DATA
image = [[[UIImage alloc] initWithData:mutData] retain];
[mutData release];
// HIDE THE ACTIVITY INDICATOR
// Make sure hidesWhenStopped is set to YES on the activity indicator.
[myUIActivityIndicatorView stopAnimating];
[myUIImageView setImage:image];
[image release];
}
Hope this helps!

Iphone App crashing on launch

My simple iphone app is crashing on launch, it says "the application downloadText quit unexcpectedly" None of these windows that pop up when a mac app crashes and has a send to Apple button. My .h is below and I would greatly appreciate it if anyone could give me a hand as to what's wrong?
#import "downloadTextViewController.h"
#implementation downloadTextViewController
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
NSString *myPath = [self saveFilePath];
NSLog(myPath);
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:myPath];
if (fileExists)
{
NSArray *values = [[NSArray alloc] initWithContentsOfFile:myPath];
textView.text = [values objectAtIndex:0];
[values release];
}
// notification
UIApplication *myApp = [UIApplication sharedApplication];
// add yourself to the dispatch table
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationWillTerminate:)
name:UIApplicationWillTerminateNotification
object:myApp];
[super viewDidLoad];
}
- (IBAction)fetchData {
/// Show activityIndicator / progressView
NSURLRequest *downloadRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://simpsonatyapps.com/exampletext.txt"]
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:1.0];
NSURLConnection *downloadConnection = [[NSURLConnection alloc] initWithRequest:downloadRequest delegate:self];
if (downloadConnection)
downloadedData = [[NSMutableData data] retain];
else {
/// Error message
}
}
- (void)connection:(NSURLConnection *)downloadConnection didReceiveData:(NSData *)data {
[downloadedData appendData:data];
NSString *file = [[NSString alloc] initWithData:downloadedData encoding:NSUTF8StringEncoding];
textView.text = file;
/// Remove activityIndicator / progressView
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:1];
}
- (NSString *) saveFilePath
{
NSArray *pathArray =
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
return [[pathArray objectAtIndex:0] stringByAppendingPathComponent:#"savedddata.plist"];
}
- (void)applicationWillTerminate:(UIApplication *)application {
NSArray *values = [[NSArray alloc] initWithObjects:textView.text,nil];
[values writeToFile:[self saveFilePath] atomically:YES];
[values release];
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[super dealloc];
}
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse {
return nil;
}
#end
Edit:
The console comes up with:
21/03/10 2:32:19 PM downloadText[3548] Stack:
( 8307803, 2474450491, 8466881, 2787944, 2786485, 25429108, 8210735, 25423659,
25431927, 24117515, 24111079, 24110797, 8337, 23594443, 23632310, 23620404,
23602815, 23629921, 134489, 8092544, 8088648, 23596565, 23633839, 8252 )
Check the Console for exception reports. [Spotlight search for "Console" application if you're lazy. ;-] Any stack trace there to provide clues?
Run in the simulator in debug mode. Set a breakpoint at the start of viewDidLoad, plus anywhere in your code from any stack trace left in the Console.
Does putting the call to "[super viewDidLoad]" first (instead of last) help? If that's doing any work, it might be tripping up your viewDidLoad. Check the Console output first though -- I tend to make code changes only when I understand what's going wrong, or I can use it to rule out potential causes.