NSURL download file, indicators shows too late - ios

Hi I got the following code and want do show an ActivityIndicator while downloading. But the indicator shows only when the download has finished?
_fanDownloadIndicator.hidden = NO;
NSLog(#"batzn");
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)objectAtIndex:0];
NSString *file = [documentPath stringByAppendingPathComponent:#"fan_new.mp3"];
BOOL fileExists = [[NSFileManager defaultManager]fileExistsAtPath:file];
if(fileExists == NO) {
NSURL *downurl = [NSURL URLWithString:url];
NSData *data = [NSData dataWithContentsOfURL:downurl];
if ([data writeToFile:file atomically:YES]) {
NSLog(#"downloaded fan");

Issue
You are doing the download task on main thread, and you wait until the thread ends before you show your loading view.
Solution
You need to start downloading task on background thread by using dispatch_async. Check out the code below
_fanDownloadIndicator.hidden = NO;
NSLog(#"batzn");
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)objectAtIndex:0];
NSString *file = [documentPath stringByAppendingPathComponent:#"fan_new.mp3"];
BOOL fileExists = [[NSFileManager defaultManager]fileExistsAtPath:file];
if(fileExists == NO) {
//Show your loading view here
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
//Do your downloading here on background thread
NSURL *downurl = [NSURL URLWithString:url];
NSData *data = [NSData dataWithContentsOfURL:downurl];
dispatch_async(dispatch_get_main_queue(), ^{
if ([data writeToFile:file atomically:YES]) {
//Hide your loading view
NSLog(#"downloaded fan");
}
});
});
}
Also I'll provide a suggestion for your loading view which is MBProgressHUD. Here is what you can do with it
//Show loading form here
[MBProgressHUD showHUDAddedTo:self.view animated:YES];
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
// Do something...
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView:self.view animated:YES];
});
});
Update
If you are dealing with downloading images, i should recommend this library:
SDWebImage
It also offers a third party to show activity indicator.

Related

UIDocumentInteractionController not working in iPad Real Device for iOS Objective c

My code is here:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(#"Downloading Started");
NSURL *url = [NSURL URLWithString:urlToDownload];
NSData *urlData = [NSData dataWithContentsOfURL:url];
if ( urlData )
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:#"%#/%#", documentsDirectory,strFileName];
//saving is done on main thread
dispatch_async(dispatch_get_main_queue(), ^{
[urlData writeToFile:filePath atomically:YES];
NSLog(#"File Saved !");
[self openDocumentIn:filePath];
});
}
});
- (void)openDocumentIn:(NSString*)path {
[self dismissViewControllerAnimated:YES completion:nil];
controller = [[UIDocumentInteractionController alloc] init];
controller.URL = [NSURL fileURLWithPath:path];
controller.delegate = self;
controller.UTI = #"com.adobe.pdf";
[controller presentOptionsMenuFromRect:self.view.bounds inView:self.view animated:YES];
}
The above code is working properly for iPhone, but it does not working in iPad.

Saving image from a URL asynchronously

So i have already gone through almost all questions on SO, and I have put them all together to create a method that accepts two paramaters, first is the URL of the image to download and display in UIImageView and second is the placeholder image for that UIImageView. I want to save the image so that it won't be downloaded every time. I have used SDWebImage to download the image, however i had some confusion when it came to saving the image in documents directory using SDWebImage, so i decided not to use it. I used dispatch_sync(dispatch_get_main_queue() and my method now looks like :
- (void)saveImageFromURL:(UIImage*)image:(NSURL*)imageURL {
NSString *url = [imageURL absoluteString];
NSArray *parts = [url componentsSeparatedByString:#"/"];
NSString *filename = [parts lastObject];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *fullPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.png", filename]];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:fullPath];
if (fileExists) {
NSLog(#"File already exists");
_myUIImage.image = [UIImage imageNamed:fullPath];
return;
}
else {
dispatch_async(dispatch_get_main_queue(), ^{
[self.myUIImage sd_setImageWithURL:imageURL placeholderImage:image];
UIImage *imageFromURL = [UIImage imageWithData:[NSData dataWithContentsOfURL:imageURL]];
NSData *imageDataNew = UIImagePNGRepresentation(imageFromURL);
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager createFileAtPath:fullPath contents:imageDataNew attributes:nil];
});
}
}
I have a couple of questions, is this implementation good enough since i am working on a app that will be on app store ? Will the downloading of the image from URL be done asynchronously ? (i know i am using dispatch_async but just need to confirm). If yes, then this wont block my UI, right ?
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
// *** Load Image from URL Asynchronously without blocking Main thread ***
UIImage *imageFromURL = [UIImage imageWithData:[NSData dataWithContentsOfURL:imageURL]];
dispatch_async(dispatch_get_main_queue(), ^(void){
// *** Create file path to DocumentDirectory ***
NSString * docDirPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *filePath = [docDirPath stringByAppendingPathComponent:#"myImage.jpg"]
// *** Write Image to disk ***
BOOL isSaved = [UIImageJPEGRepresentation(image, 1.0f) writeToFile:dirPath atomically:YES];
if(isSaved)
{
NSLog(#"Image write to disc successfully.");
}
});
});

Download images asynchronously

I am using the following code to download images. Can someone confirm the images are being downloaded asynchronously as they would appear to be? Normally, they download rapidly but every now and then the UI freezes for a minute while data comes through so something would appear to be awry:
#define kBgQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) //1
NSString *picURL = [NSString stringWithFormat:#"http://~/pics/%#",picname];
NSURL *urlPicUrl = [NSURL URLWithString:picURL];
dispatch_async(kBgQueue, ^{
NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:picURL]];
if (imgData) {
UIImage *imageCache = [[UIImage alloc] init];
imageCache = [UIImage imageWithData:imgData];
if (imageCache) {
[self saveImage:imageCache asPicName:picname];
dispatch_async(dispatch_get_main_queue(), ^{
});
}
}
});
EDIT:
Here is code to save image.
- (void)saveImage: (UIImage*)image asPicName: (NSString*)picname
{
if (image != nil)
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString* path = [documentsDirectory stringByAppendingPathComponent:
[NSString stringWithString: picname] ];
NSData* data = UIImagePNGRepresentation(image);
[data writeToFile:path atomically:YES];
}
}

Download folder from server to local iPad / iPhone

I want to create an application that automatically downloads a folder from my own server and store it locally on the iPad/iPhone. How can i accomplish this and where does the folder get stored on the local iDevice ? Therefore how will i access it afterwards? Many thanks for your help
The best way to do so was actually putting all the pictures for example in one zip file, downloading it and unzipping it on the real device using this code :
dispatch_queue_t queue = dispatch_get_global_queue(
DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSURL *url = [NSURL URLWithString:#"someDirectory/newPics.zip"];
NSError *error = nil;
// 2
NSData *data = [NSData dataWithContentsOfURL:url options:0 error:&error];
if(!error)
{
// 3
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *path = [paths objectAtIndex:0];
NSString *zipPath = [path stringByAppendingPathComponent:#"newPics.zip"];
[data writeToFile:zipPath options:0 error:&error];
if(!error)
{
ZipArchive *za = [[ZipArchive alloc] init];
// 1
if ([za UnzipOpenFile: zipPath]) {
// 2
BOOL ret = [za UnzipFileTo: path overWrite: YES];
if (NO == ret){} [za UnzipCloseFile];
// 3
NSString *imageFilePath = [path stringByAppendingPathComponent:#"newPics/pic1.png"];
//[self removeImage:zipPath];
[[NSFileManager defaultManager] removeItemAtPath:zipPath error: &error];
dispatch_async(dispatch_get_main_queue(), ^{
NSURL *fileURL = [NSURL fileURLWithPath:imageFilePath];
[_webV loadRequest:[NSURLRequest requestWithURL:fileURL]];
});
}
}
else
{
NSLog(#"Error saving file %#",error);
}
}
else
{
NSLog(#"Error downloading zip file: %#", error);
}
});
It's the best way to do it , fast and reliable.

show pragress bar while downloading file

Hi in my application I have a file in URL I want download to my document folder in my application while downloading the file i want to show the progress bar. So I have used the MBProgressHUD for that but its not working properly its showing late please tell me how to resolve this one.
My code:
- (IBAction)down:(id)sender {
UIButton *btn = (UIButton *)sender;
spinner = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
spinner.mode = MBProgressHUDModeCustomView;
[spinner setLabelText:#"downloading....."];
[spinner setLabelFont:[UIFont systemFontOfSize:15]];
[spinner show:YES];
NSURL *url = [NSURL URLWithString:fileurl];
NSData *urlData = [NSData dataWithContentsOfURL:url];
if ( urlData )
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:#"%#/%#", documentsDirectory,name];
[urlData writeToFile:filePath atomically:YES];
}
[activityIndicatorObject stopAnimating];
[spinner hide:YES];
[spinner removeFromSuperViewOnHide];
}
I have used the above code its taking too much time to show the MBProgressHUD Please tell me how to resolve this issue I have stuck here for time.
Thanks..
You have to change in your code as below..
- (IBAction)down:(id)sender {
UIButton *btn = (UIButton *)sender;
//spinner = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
[MBProgressHUD showHUDAddedTo:self.view animated:YES];
//spinner.mode = MBProgressHUDModeCustomView;
//[spinner setLabelText:#"downloading....."];
//[spinner setLabelFont:[UIFont systemFontOfSize:15]];
//[spinner show:YES];
NSURL *url = [NSURL URLWithString:fileurl];
NSData *urlData = [NSData dataWithContentsOfURL:url];
if ( urlData )
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:#"%#/%#", documentsDirectory,name];
[urlData writeToFile:filePath atomically:YES];
}
//[activityIndicatorObject stopAnimating];
//[spinner hide:YES];
//[spinner removeFromSuperViewOnHide];
[MBProgressHUD hideHUDForView:self.view animated:YES];
}
Enjoy Code..
I suspect what you are experiencing are concurrency issues with the above code. I'm assuming the URL you are fetching is from over a network. The NSData method dataWithContentsOfURL: is blocking the main thread before the operating system has a chance to call setNeedsDisplay on the spinner, which also happens on the main thread, and this could explain why it's showing up late.
The solution would be to make some sort of asynchronous request, to fetch the file, with a completion handler, so your application can do tasks etc upon completion, (like stopping the activity indicator, persisting the file to local storage etc).
This is what the documentation has has to say about the dataWithContentsOfURL: of NSData class :-
Important: Do not use this synchronous method to request network-based URLs. For network-based URLs, this method can block the current thread for tens of seconds on a slow network, resulting in a poor user experience, and in iOS, may cause your app to be terminated.
Instead, for non-file URLs, consider using the dataTaskWithURL:completionHandler: method of the NSSession class.

Resources