Load Images from Parse.com to iOS App
Parse.com allows you to persist file objects, including images. In your iOS app, you'll likely want to load these in a sensible way that takes into account network lag.
In this case, we're going to use Parse.com's getDataInBackgroundWithBlock method, and we're going to pass a completion block to our caller when the download is completed (or if an error occurs). You can also pass an optional UIProgressView to the method, if you want to display a progress bar on screen.
The most reusable way to implement this is to create new category on PFObject and add this class method to your category's .h file:
+(void)loadParseImage:(PFObject *)parseObject forImageColumn:(NSString *)columnName withProgressBar:(UIProgressView *)progressBar andCompletionBlock:(void (^)(UIImage *imageFile, NSError *error))completionBlock;
Then in the category's .m file, add the following method:
+(void)loadParseImage:(PFObject *)parseObject forImageColumn:(NSString *)columnName withProgressBar:(UIProgressView *)progressBar andCompletionBlock:(void (^)(UIImage *imageFile, NSError *error))completionBlock
{
NSString *parseFileName = [NSString stringWithFormat:@"%@", [[parseObject objectForKey:columnName] name]];
// Get a path to the place in the local documents directory on the iOS device where the image file should be stored.
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
// You can change the path as you see fit by altering the stringByAppendingPathComponent call here.
NSString *imagesDirectory = [documentsDirectory stringByAppendingPathComponent:@"Images"];
NSString *storePath = [imagesDirectory stringByAppendingPathComponent:parseFileName];
if (progressBar)
{
// Reset and show the progress bar
[progressBar setProgress:0.0 animated:NO];
progressBar.hidden = NO;
}
// Image data from Parse.com is retrieved in the background.
[[parseObject objectForKey:columnName] getDataInBackgroundWithBlock:^(NSData *data, NSError *error)
{
if (!error)
{
NSData *fileData = [[NSData alloc] initWithData:data];
if (![[NSFileManager defaultManager] fileExistsAtPath:imagesDirectory])
{
// Create the folder if it doesn't already exist.
[[NSFileManager defaultManager] createDirectoryAtPath:imagesDirectory
withIntermediateDirectories:NO
attributes:nil
error:&error];
}
// Write the PFFile data to the local file.
[fileData writeToFile:storePath atomically:YES];
UIImage *showcaseImage;
if ([[NSFileManager defaultManager] fileExistsAtPath:imagesDirectory])
{
showcaseImage = [UIImage imageWithContentsOfFile:storePath];
}
else // No file exists at the expected path. Perhaps the disk is full, etc.?
{
NSLog(@"Unable to find image file where we expected it: %@", storePath);
}
completionBlock(showcaseImage, error);
// This may be a good place to clean up the target directory.
}
else // Unable to pull the image data from Parse.com. Consider more robust error handling.
{
NSLog(@"Error getting image data.");
completionBlock(nil, error);
}
}
progressBlock:^(int percentDone)
{
if (progressBar)
{
[progressBar setProgress:percentDone animated:YES];
}
}];
}
The class method expects you to pass a PFObject first, then an NSString representing the name of the column on Parse.com where your PFFile is stored, and an optional UIProgressView.
Now, assuming that you have a UIViewController with the following property declarations in its .h file:
@property (nonatomic, strong) UIImageView *imageView;
@property (nonatomic, strong) UIProgressView *progressBar;
You can load the images from any other part of your UIViewController's .m file, for example from viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
[PFObject loadParseImage:myPFObject forImageColumn:@"MainImage" withProgressBar:self.progressBar andCompletionBlock:^(UIImage *imageFile, NSError *error)
{
if (!error)
{
self.imageView.image = imageFile;
}
else
{
// Image file did not load correctly. Handle the error as you see fit.
}
}];
}
Remember: you'll need to #include your new PFObject category in any UIViewControllers or other objects that will access the class method.
Written by Sol Irvine
Related protips
3 Responses
Hi! Thanks for the tutorial.. I do get an error though:
"Use of undeclared identifier 'myPFObject'"
why do you think?
Adrienne: you probably need to replace 'myPFObject' with the actual instance of the PFObject that has the image as a property. Typically, you want to query for the PFObject first, then pass it into this loadParseImage:forImageColumn:withProgressBar:andCompletionBlock method to pull the image down. Note that you'll also want to change "MainImage" to match the name you gave the image column in Parse. HTH.
I'm having the same issue. I'm in parse and i don't see an option for image name. All i have is the objectid and the name of the pffile.