how to handle online images in Parse - ios

so I'm using Parse in my app as backend cloud service. I looked into Parse documents and see that they have PFFile method which can upload local images to Parse, and PFImageView class to retrieve remote image from Parse. But in my app, we have a lot of online images with URLs, and how can I easily display these images in my app without worrying about caches and all? Or is there any way to download and upload online images to Parse easily so that I can just use Parse's service?

Since you aren't loading images from Parse, there's no reason to use PFFile or PFImageView. Downloading and uploading them would be redundant if you know they will remain available at their respective URL's. This will allow you to load images asynchronously (in the background) from a URL:
dispatch_async(dispatch_get_global_queue(0,0), ^{
NSData * data = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString: #"http://myurl/mypic.jpg"]];
if ( data == nil )
return;
dispatch_async(dispatch_get_main_queue(), ^{
UIImageView *imView = [[UIImageView alloc] initWithImage:[UIImage imageWithData: data]];
});
});
As far as cacheing goes, if your images are large you may notice a delay in their loading and may still need to cache. This is one of the most popular solutions:
https://github.com/path/FastImageCache

Related

PhotoKit iOS8 - Retrieve image using the "PHImageFileURLKey"

Is there anyway I can use the Path returned from the "PHImageFileURLKey" to go into the photo library and retrieve the image?
The path returned is in this format:
"file:///var/mobile/Media/DCIM/102APPLE/IMG_2607.JPG"
My plan is to store this path in the database and use it to fetch the image when I need to get it back.
Any help is appreciated. Thank you!
I think your solution of retrieving Photo Kit asset from the URL is wrong.
Here is what I would do (supposing you have access to PHAsset):
Store the localIdentifier:
PHAsset *asset = /*Your asset */
NSString *localIdentifier = asset.localIdentifier;
//Now save this local identifier, or an array of them
When it is time to retrieve them you simply do:
PHFetchResult *savedAssets = [PHAsset fetchAssetsWithLocalIdentifiers:savedLocalIdentifiers options:nil];
[savedAssets enumerateObjectsUsingBlock:^(PHAsset *asset, NSUInteger idx, BOOL *stop) {
//this gets called for every asset from its localIdentifier you saved
}];
If you only have access to “PHImageFileURLKey” then disregard this answer.
This isn't documented, so I'd strongly advise against using that URL for anything more than a prototype app. That said, this does appear to work:
dispatch_queue_t queue = dispatch_queue_create("photoLoadQueue", 0);
dispatch_async(queue, ^{
NSURL *privateUrl = [NSURL URLWithString:#"file:///var/mobile/Media/DCIM/102APPLE/IMG_2607.JPG";
NSData *imageData = [NSData dataWithContentsOfURL:privateUrl];
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = [UIImage imageWithData:imageData];
});
});
Naturally you'll need to replace the string used to initiate the url with one which is valid for your phone.
There are probably a load of issues with doing this - it's just not how the framework is meant to be used. Here are some off the top of my head:
When running in the simulator, the root path changes regularly between launches of the app, so if you store absoluteUrls like this your database will quickly become full of dead URLs. This will be inconvenient to say the least.
Worse, the URLs for the images may change on a real device - you don't have control over it, and once they change it's your app's fault for making the user reselect them or whatever.
You're not going to ever find out about changes to the PHAsset which the photo came from.
This may be circumventing user permission for photo access - what happens if your app's permission to access photos is revoked? This is probably an issue with lots of approaches to storing photos for later use, however.
You don't control the file - what if the user deletes it?
If I were you, I would retrieve the image properly from the photos framework, using PHImageManager requestImageForAsset: targetSize: contentMode: options: resultHandler:, and store it in a file within your app's directory, at a sensible resolution for whatever you're doing with it. This still doesn't give you asset changes, but is a pretty good solution.
If you want to store the assets themselves and only request the images when you actually need them, it might be worth investigating transient asset collections, though I've not used them so that might not work for what you need.

ios fast Image cache with server

I am trying to use Path's FastImageCache library to handle photos in my app. The sample they provide simply reads the images from disk. Does anyone know how I might modify it to read from a url? In the section about providing source images to the cache they have
- (void)imageCache:(FICImageCache *)imageCache wantsSourceImageForEntity:(id<FICEntity>)entity withFormatName:(NSString *)formatName completionBlock:(FICImageRequestCompletionBlock)completionBlock {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Fetch the desired source image by making a network request
NSURL *requestURL = [entity sourceImageURLWithFormatName:formatName];
UIImage *sourceImage = [self _sourceImageForURL:requestURL];
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock(sourceImage);
});
});
}
Has anyone used this api before and know how to get the source from the server to pass to the cache? Another example that still uses hard disk is
- (void)imageCache:(FICImageCache *)imageCache wantsSourceImageForEntity:(id<FICEntity>)entity withFormatName:(NSString *)formatName completionBlock:(FICImageRequestCompletionBlock)completionBlock {
// Images typically come from the Internet rather than from the app bundle directly, so this would be the place to fire off a network request to download the image.
// For the purposes of this demo app, we'll just access images stored locally on disk.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIImage *sourceImage = [(FICDPhoto *)entity sourceImage];
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock(sourceImage);
});
});
}
I worked on Fast Image Cache while I was at Path. The critical portion of Fast Image Cache is that it is the absolute fastest way to go from image data on disk to being rendered by Core Animation. No decoding happens, none of the image data is kept in memory by your app, and no image copies occur.
That said, the responsibility is yours to figure out how to download the images. There's nothing inherently special about downloading images. You can use NSURLConnection or one of many popular networking libraries (like AFNetworking) to actually download the image data from your server. Once you have that image data, you can call the relevant completion block for Fast Image Cache to have it optimize it for future rendering.
If you're looking for a simple way to download an image and display it when it's finished, then use something like SDWebImage. It's great for simple cases like that. If you are running into performance bottlenecks—especially with scrolling—as a result of your app needing to display tons of images quickly, then Fast Image Cache is perfect for you.
Your Approach Seems a Lot Like Lazy Loading Images from the URL, I had to do this once I had Used the following Library to do it, It dosent stores the Images in the disk, but uses cached Images..the below is its link..
https://github.com/nicklockwood/AsyncImageView
I added the networking logic to our fork > https://github.com/DZNS/FastImageCache#dezine-zync-additions-to-the-class
It utilizes NSURLSessionDownloadTasks, has a couple of configuration options (optional). All you need to do is create a new instance of DZFICNetworkController and set it as the delegate for FICImageCache's sharedCache instance object. It'll take care of downloading images with reference to the sourceImageURLWithFormatName: method on your objects conforming to <FICEntity>.
As I assume you'd use this in a UITableView or UICollectionView, calling cancelImageRetrievalForEntity:withFormatName: on the imageCache will cancel the download operation (if it's still in-flight or hasn't started).

Loading images from Images.xcassets

I am trying to load more than 100 images from the bundle, however recently I was just curious what is the best practice to load the images dynamically from the bundle. Will custom Images.xcassets allow me to do such task? How can I dynamically load NSArray?
To start with my answer I will recommend, DON'T do it.
Its really an overkill for your app to load 100 images and to store them in memory, at once, and the app would definitely go under some memory pressure.
In Xcode 5, .xcassets is still the best place to keep your images in app bundle. For further details you might be interested in Apple Asset Catalog documentation. However, rather than loading and storing images to NSArray, I would recommend you to store their identifiers (actually the image names as added to .xcassets) in the array. Also it would be helpful if you store the image identifiers to some NSString* const beforehand.
So your coding structure should look something like the following:
//image names
NSString* const kImage1 = #"image1";
NSString* const kImage2 = #"image2";
NSString* const kImage3 = #"image3";
//...etc
NSArray* imageIdentifiers = #[kImage1, kImage2, kImage3,...];
//...
And then to load individual image from bundle you could use:
UIImage* img = [UIImage imageNamed:kImage3]; //loads image3 from bundle and caches
Or to traverse through all images you might use:
for (NSString* identifier in imageIdentifiers) {
#autoreleasepool { //make sure your image data gets disposed after use
UIImage* img = [UIImage imageNamed:identifier];
//... use img, etc.
}
}
And finally, imageNamed: method of UIImage class, caches an image in system cache. So you won't have to worry about reloading it from file, if you reuse one.

UIImages NSURLs and Threads

I am trying to build a nice function to access the network for images, if they are found on the web, I store them in a cache system I made.
If the image was already stored on the cache, I return it.
The function is called getImageFromCache and returns an image if it is in the cache, else, it would go to the network and fetch.
The code might look like this:
UIImageView* backgroundTiles = [[UIImageView alloc] initWithImage[self getImageFromCache:#"http://www.example.com/1.jpg"]];
Now, I am moving on to using threads because of big latencies due to network traffic. So I want images to show a temp image before I get the result from the web.
What I want to know is how can I keep track of so many images being accessed sequentially, being added to UIImageViews by this function (getImageFromCache).
Something just won't work there:
-(UIImage*)getImageFromCache:(NSString*)forURL{
__block NSError* error = nil;
__block NSData *imageData;
__block UIImage* tmpImage;
if(forURL==nil) return nil;
if(![self.imagesCache objectForKey:forURL])
{
// Setting a temporary image until we start getting results
tmpImage = [UIImage imageNamed:#"noimage.png"];
NSURL *imageURL = [NSURL URLWithString:forURL];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
imageData = [NSData dataWithContentsOfURL:imageURL options:NSDataReadingUncached error:&error];
if(imageData)
{
NSLog(#"Thread fetching image URL:%#",imageURL);
dispatch_async(dispatch_get_main_queue(), ^{
tmpImage = [UIImage imageWithData:imageData];
if(tmpImage)
{
[imagesCache setObject:tmpImage forKey:forURL];
}
else
// Couldn't build an image of this data, probably bad URL
[imagesCache setObject:[UIImage imageNamed:#"imageNotFound.png"] forKey:forURL];
});
}
else
// Couldn't build an image of this data, probably bad URL
[imagesCache setObject:[UIImage imageNamed:#"imageNotFound.png"] forKey:forURL];
});
}
else
return [imagesCache objectForKey:forURL];
return tmpImage;
}
This is not a direct answer to your question, but are you aware that there is no need to use GCD to download things asynchronously (on a background thread)? Just use NSURLConnection and its delegate methods. All your code will be on the main thread but the actual connection and downloading will happen in the background.
(And in fact I have written a class, MyDownloader, that takes care of all this for you:
http://www.apeth.com/iOSBook/ch37.html#_http_requests
Scroll down to the part about MyDownloader and its subclass MyImageDownloader, which is doing exactly the sort of thing you need done here. Moreover, note the subsequent code in that chapter showing how to use a notification when a download completes, prompting the table view that need these images to reload the row that contains the image view whose image has just arrived.)
its good your building it from scratch but if you want to save the all the work, there's a drop in Replacement SDWebImage Library with support for remote images coming from the web, and has all the functionality Like Temp Image, Asychronous Loading, Caching etc, you said you need
In your background thread, once the download has completed and you've saved the image to the cache, I'd suggest you post a notification using the NSNotificationCenter to let other parts of your app know that the cache has been updated.
This assumes that whichever part of the app manages the image views has registered its interest in those notification with the addObserverForName method. When it receives such a notification, it can then attempt to retrieve the images from the cache again and update its image views if appropriate.
Depending on the number of image views, you may want to pass through the image url in the notification in some way (e.g. in the userInfo dictionary), and then based on that decide which image views should be refreshed rather than refreshing them all.
I should add that I would also recommend getting rid of the inner dispatch_async call. There's no need for that, although you may need to add synchronisation to your cache object so it can be safely accessed from the main thread as well as the download thread.

Curtail UIImage download size from WEB

I have an application that downloads images based on URL using the call:
img = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:urlToImage] options:0 error:&err]];
where URL is an image on the WEB.
I really want to curtail the amount of data transfer, so would like to reduce the file size of the download.
I was wondering if anyone has a magic way of reducing the file size of the download short of implementing a PHP server process to be the go between to take care of this.
Thanks in advance!

Resources