Last Updated: February 25, 2016
·
5.354K
· sirvine

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.

3 Responses
Add your response

Hi! Thanks for the tutorial.. I do get an error though:

"Use of undeclared identifier 'myPFObject'"

why do you think?

over 1 year ago ·

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.

over 1 year ago ·

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.

over 1 year ago ·