Saving image from a URL asynchronously - ios

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.");
}
});
});

Related

Get Image from server

I am getting responseObject like this
responseObject =
{
"img": "images/ProfileImage/defaultImg.jpg",
}
I have to store this image in app folder. Is there any way to do this.
The path is incomplete
You need to append base URL before "images/ProfileImage/defaultImg.jpg"
Then
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSString *urlToDownload = BaseURL + "images/ProfileImage/defaultImg.jpg";
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,#"defaultImg.png"];
//saving is done on main thread
dispatch_async(dispatch_get_main_queue(), ^{
[urlData writeToFile:filePath atomically:YES];
NSLog(#"File Saved !");
});
}
});
Here is the code for downloading image and store into a local i.e "Document" directory in iOS app folder
In below code urlString is the url of image.
dispatch_queue_t downloadThumbNail = dispatch_queue_create("com.download.thumbnail", NULL);
dispatch_async(downloadThumbNail, ^
{
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:urlString]];
UIImage *imageThumb = [UIImage imageWithData:imageData];
dispatch_async(dispatch_get_main_queue(),
^{
NSString *str = #“filename.png”;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
NSString *filePath = [documentsPath stringByAppendingPathComponent:str];
NSData *pngData = UIImagePNGRepresentation(imageThumb);
[pngData writeToFile:filePath atomically:YES];
});
});
Enjoy codeing.

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];
}
}

save image to subfolder of /library/Application Support/bundleID_name/... iOS

The following code works without error or exception - but still, it does not do what it should ! I wanted to save an image into the iOS library/Application Support folder. More precisely, the image should be placed into a /library/Application Support/bundleID_name/subfolder/ (and the subfolder being called "location1").
If I check the functionality with the iOS-Simulator, I can see the creation of the subfolder (i.e. .../library/Application Support/bundleID_name/location1/). Also the function "saveImage" works without exception. But there is no image being saved !!!! (i.e.the image-file is missing and the folder remains empty) !!
What could be the mistake ??
Here is my code with the call of two functions (see code below):
UIImage *in_image = [UIImage imageNamed:#"template009c.jpg"];
NSString *locDirectoryName = #"location1";
NSURL *LocationDirectory = [self appendLocationToApplicationDirectory:locDirectoryName];
[self saveImage:in_image :#"image1" :LocationDirectory];
With the corresponding function-Nr1:
- (NSURL*)appendLocationToApplicationDirectory:(NSString*)locationDirName
{
NSString* appBundleID = [[NSBundle mainBundle] bundleIdentifier];
NSFileManager*fm = [NSFileManager defaultManager];
NSURL* dirPath = nil;
// Find the application support directory in the home directory.
NSArray* appSupportDir = [fm URLsForDirectory:NSApplicationSupportDirectory
inDomains:NSUserDomainMask];
if ([appSupportDir count] > 0) {
// Append the bundle ID and the location-Foldername to the URL for the Application Support directory
dirPath = [[[appSupportDir objectAtIndex:0] URLByAppendingPathComponent:appBundleID] URLByAppendingPathComponent:locationDirName];
// If the directory does not exist, this method creates it.
// This method call works in OS X 10.7 and later only.
NSError* theError = nil;
if (![fm createDirectoryAtURL:dirPath withIntermediateDirectories:YES attributes:nil error:&theError]) {
// Handle the error.
NSLog(#"%#", theError.localizedDescription);
return nil;
}
else {
// Mark the directory as excluded from iCloud backups
if (![dirPath setResourceValue:#YES
forKey:NSURLIsExcludedFromBackupKey
error:&theError]) {
NSLog(#"Error excluding %# from iCloud backup %#", [dirPath lastPathComponent], theError.localizedDescription);
}
else {
NSLog(#"Location Directory excluded from iClud backups");
}
}
}
return dirPath;
}
And function Nr2:
//saving an image
- (void)saveImage:(UIImage*)image :(NSString*)imageName :(NSURL*)pathName {
NSData *imageData = UIImagePNGRepresentation(image); //convert image into .png format.
NSFileManager *fileManager = [NSFileManager defaultManager];
// NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
// NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *LocationDirectory = [pathName absoluteString];
NSString *fullPath = [LocationDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.png", imageName]];
/***** THE FOLLOWING LINE DOES NOT SEEM TO DO WHAT IT IS SUPPOSED TO *******/
[fileManager createFileAtPath:fullPath contents:imageData attributes:nil];
/**** I also tried without the FileManager, but same problem - no file written... ***/
// [imageData writeToFile:fullPath atomically:NO];
NSLog(#"image saved");
}
By the way, getting the "fullPath" with the XCode-Debugger, I get:
"fullPath NSPathStore2 * #"file:/Users/username/Library/Application%20Support/iPhone%20Simulator/7.1/Applications/2BCC3345-9M55F-4580-A1E7-6694E33456777/Library/Application%20Support/bundleID_name/image1.png" 0x09d50950
Doesn't that also seem correct ?? But why is [fileManager createFileAtPath:fullPath contents:imageData attributes:nil]; not performing ???
This:
"fullPath NSPathStore2 * #"file:/Users/username/Library/Application%20Support/iPhone%20Simulator/7.1/Applications/2BCC3345-9M55F-4580-A1E7-6694E33456777/Library/Application%20Support/bundleID_name/image1.png" 0x09d50950
is not a valid path, it's a URL path but stored in a string. If you are going to use URL's then use ULRs rather than trying to convert to a string:
[imageData writeToURL:pathName atomically:YES];
(preferably naming the parameter as pathURL) and if you want to use paths then don't use a URL at any stage.
Also, where an API method returns an error or status flag, check it in the code as standard.
I'm pretty sure you can't save an image on a path you specify. You can save images on the gallery or in DocumentDirectory. This should be the code to save an image on the DocumentDirectory:
NSString *imgName=[#"imgname.png"];
[[NSUserDefaults standardUserDefaults]setValue:imgName forKey:#"imageName"];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *savedImagePath = [documentsDirectory stringByAppendingPathComponent:imgName];
UIImage *image = imageView.image; // imageView is my image from camera
NSData *imageData = UIImagePNGRepresentation(image);
[imageData writeToFile:savedImagePath atomically:NO];

How to store a UIImage locally to avoid an unnecessary server request?

I'm downloading an image and then displaying it with UIImageView. Using this approach the image is downloaded every time the view is loaded. How would I go about storing it locally to avoid an unnecessary server request?
[self requestData ] ;
UIImage *userImage = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:userProfileImageUrl ]]] ;
[self.profileImageView setImage:userImage ] ;
I would suggest using a library like SDWebImage that handles caching and asynchronous download.
Very first time, you have to save the image into NSDocumentsDirectory. For that you have to take the path of directory and append imageName like this
NSString * documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString * documentsPathAppend = [documentsPath stringByAppendingFormat:#"/%#",[userProfileImageUrl lastPathComponent]];
And you have to write image with folowing condition
if(![[NSFileManager defaultManager]fileExistsAtPath:documentsPathAppend])
{
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:userProfileImageUrl]]];
[data writeToFile:documentsPathAppend atomically:YES];
}
After that you have to add this path into your local storage like core data. From next time you have to check whether image is there or not for particular URL in your core data.If it is there, then fetch the path from your core data and show like this
[self.profileImageView setImage:[UIImage imageWithContentsOfFile:imageEntity.imagePath]];
try this method to store UIImage in Local Directory..
- (void)addImage:(UIImage *)image toCacheWithIdentifier:(NSString *)identifier {
NSString *folderPath = #"LOCAL DIRECTORY PATH ";
if(![[NSFileManager defaultManager] fileExistsAtPath:folderPath isDirectory:nil])
{
[[NSFileManager defaultManager] createDirectoryAtPath:folderPath withIntermediateDirectories:YES attributes:nil error:nil];
}
NSString *fileName = [NSString stringWithFormat:#"%#.png",identifier];
fileName = [folderPath stringByAppendingPathComponent:fileName];
NSData *imageData = UIImagePNGRepresentation(image);
[imageData writeToFile:fileName atomically:YES];
}
to retrieve image from Local Directory try this method...
- (UIImage *)imageFromCacheWithIdentifier:(NSString *)identifier
{
NSString *folderPath = #"LOCAL DIRECTORY PATH ";
NSString *fileName = [NSString stringWithFormat:#"%#.png",identifier];
fileName = [folderPath stringByAppendingPathComponent:fileName];
if([UIImage imageWithContentsOfFile:fileName])
{
return [UIImage imageWithContentsOfFile:fileName];
}
return nil;
}
One other viable approach is to utilize NSURLCache (see also: Understanding Cache Access) in the official documentation.
The HTTP Cache was invented exactly for this requirement: Caching in HTTP in RFC 2616.
Unfortunately, HTTP Caching is an advanced topic, and the exact behavior of NSURLCache is quite obscure.

NSFileManager Not Saving

I've been trying to use NSFileManager to cache images to the Cache directory of my sandboxed app. Unfortunately, my code doesn't seem to work.
- (BOOL)saveImageToCacheFolder
{
NSFileManager *filemgr = [NSFileManager defaultManager];
NSString *cachePath = [[[filemgr URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask] lastObject] absoluteString];
NSString *fullpath = [cachePath stringByAppendingPathComponent:#"test.jpg"];
NSURL *imageURL = [FlickrFetcher urlForPhoto:self.photoData format:FlickrPhotoFormatLarge];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
BOOL success = false;
if (imageData != nil) {
success = [imageData writeToURL:[NSURL URLWithString:fullpath] atomically:true];
if (!success) NSLog(#"Did Not Managed to save the image");
}
return success;
}
Here is what I am trying to do. First, get the default NSFileManager. Second, get the relative path to the cache directory of my app. Third, append the name of my file (here i just wrote test for now). Fourth, convert my image to NSData. Fourth, write the NSData object to the specified path.
edit: added if statement for checking if the imageData is nil
Try this:
NSArray *myPathList = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cachePath = [myPathList objectAtIndex:0];
NSString *fullpath = [cachePath stringByAppendingPathComponent:#"test.jpg"];
NSURL *imageURL = [FlickrFetcher urlForPhoto:self.photoData format:FlickrPhotoFormatLarge];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
if (imageData != nil) {
success = [imageData writeToURL:[NSURL fileURLWithPath:fullpath] atomically:YES];
if (!success) NSLog(#"Did Not Managed to save the image");
}

Resources