iOS 8.3+
Swift 1.2
Parse 1.7.2

2015/04/14 — Updated to cover Parse 1.7.2.

How to display parse.com images in a table view. iOS, Swift 1.2, Parse.com

The Parse documentation on this topic is disapointing, and, as I learned only provides some of the information you’ll need. Hitting up google I quickly realised that there is very little information out there that actually shows how to achieve this — I could not find a single question/post/article/tutorial to help. There are many people asking… but no one seems to present a complete answer.

In short — lots of frustration. Well read on my chums — help is at hand.

Our starting point is an XCode project that is already configured to use parse.com which you can find over in Github. (HowToDisplayImagesInTableViewFromParse — Snappy title eh?)

We’ll be using the same “Countries” demo data I have used before, augmented with some flag images.

Finally, don’t forget that I am all about Swift. My knowledge of ObjectiveC would fit in the tiny tiny space that exists between a stamp and it’s envelope.

Like all my posts, there is a shopping list of links at the end of the article. No leaving this page until you’ve read it all!

Goal

Strategy

The route to achieving images in our table view is very simple to describe….

  • Clear down the story board and delete the View Controller file
  • Add a temporary flag image to our project assets
  • Create a custom table cell class
  • Add a table view controller to our story board
  • Load table data and display a temporary image in our table
  • Load parse images in the background and display

…. but littered with little gotchas that will break the project leaving you frustrated.

Temporary flag image

We need to add an image asset to our project. This image will be displayed between loading the table view and fetching the images from Parse. You’ll find the file in the finished GitHub project.

Add this file to images.xcassets.

Custom Cell

We are going to start by creating the Custom Cell class file. Add a new file to the project.

  • OS = iOS
  • Type = Swift filed
  • File name = CustomCell

Into this new file add the follwing code.

There are 2 things to note about this code.

class CustomCell: PFTableViewCell {

We are subclassing the PFTableViewCell class.

@IBOutlet weak var customFlag: PFImageView!

The image outlet is subclassing the PFImageView class.

You should be able to tell that we’re going to be adding 3 components to our custom cell in the story board — so let’s get on and do that next.

Story Board

We shall start with a clean slate — delete the initial View Controller from the story board, and, delete the initial View Controller class file.

  • Drag a TableViewController on to the story board
  • Add a Prototype Cell to the Table View Controller
  • Drag 2 labels and 1 Image View into the prototype cell

Select the table view in the view outline and check the IS INITIAL VIEW CONTROLLER option

Select the prototype cell in the view outline and set the CUSTOM CLASS = CustomCell.

Your Story Board should look something like this.

There are a couple of properties we need to check/set. Ensure the cell STYLE = Custom and IDENTIFIER = Cell.

Now we can drag the IBOutlets to the story board and complete wiring up the elements. Swifth to assistent editor….

Table View Controller

Lets create a new Table View Controller class file. Add a new file to the project.

  • OS = iOS
  • Source = Cocoa Touch Class
  • Class = TableViewController
  • Subclass = PFQueryTableViewController
  • Language = Swift
  • File name = As suggested

We need to connect the Table View Controller within the story board to the newly created class file.

Update the file contents with the code below.

If you need help understanding this code, see the tutorial “iOS + Swift + parse.com — Part 2 (Table View)” where I walk through this code in detail.

At this point we have a TableViewController which has a connection to our Parse data.

Now to populate the table with some data — and flags!

Add the following method to the Table View Controller class file.

If you have used Table Views before you will recognise much in this code, but there are some crucial changes that if missed will not stop your project from building but will stop the data we desire being displayed. Lets walk through the code.

var cell = tableView.dequeueReusableCellWithIdentifier("CustomCell") as! CustomTableViewCell! if cell == nil { cell = CustomTableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "CustomCell") }

We’re initialising a cell or type “CustomCell” — the custom cell we defined which subclasses PFTableViewCell. The code double checks the cell was initialised becuase it is possible connectivity is lost at this point and the initialisation fails.

// Extract values from the PFObject to display in the table cell
if let nameEnglish = object?["nameEnglish"] as? String {
cell.customNameEnglish.text = nameEnglish
}
if let capital = object?["capital"] as? String {
cell.customCapital.text = capital
}

Push some data into our label elements.

// Display flag image
var initialThumbnail = UIImage(named: "question")
cell.customFlag.image = initialThumbnail
if let thumbnail = object?["flag"] as? PFFile {
cell.customFlag.file = thumbnail
cell.customFlag.loadInBackground()
}

This is the key block of code.

First — get a handle on the “question” image that is used if an image on Parse cannot be located, and we set this to display in the table view.

The PFFile class understands how to download files from a remote location and importantly how to work with a connection that might not be stable.

Second — check to see if we can locate the target image from Parse, if the image is located we call the loadInBackground() method. Depending on the device connection the images will load and replace the temporary images. On normal/fast connections it is likely you will only see the temporary holding images briefly — blink and you’ll miss them.

The PFFile class caches images on the device so that future table views do not require connectivity to successfully display the images.

Congratulations

We’ve created a table view that displays images which are hosted in the Parse cloud platform.


Links

Bonus — How to delete a row

I was asked how to add a row delete capability to this table view. I can see why using a PFQueryTableViewController controller might give the impression deleting rows, or inserting rows, might be different from a normal table — but it’s no different and very easy to do.

To add a row delete capability we need to add 2 more methods to our controller code. The first method will signal to the table view that editing a row is enabled. The second method will detail just what to do when the user edits a row — in our case delete the row.

Add the following method to your PFQuerytableViewController controller file.

This method enables the edit / swipe right capability. Right now we’re just returning TRUE. However you could perform logic testing here, maybe to check if the user has permission to edit this row.

The second method you need to add to your controller file is more complex, so we’ll step through the code to make sure it’s fully understood.

if editingStyle == .Delete {
…….
}
} else if editingStyle == .Insert {
…….
}

The object `editingStyle` tells us which action the user selected from the swipe right gesture. In our case the user can only select Delete so that is where all our logic must go.

let objectToDelete = objects?[indexPath.row] as PFObject

Get the target object out of our Parse objects collection.

objectToDelete.deleteInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
if (success) {
// Force a reload of the table — fetching fresh data from Parse platform
self.loadObjects()
} else {
// There was a problem, check error.description
}

This section is the meat and potatoes of our delete code. We call the deleteInBackgroundWithBlock method on the target object. We use a background call becuase we don’t know how long this action might take, depending on the user’s connectivity this might take a couple of seconds. Using a background call will ensure our app continues to function while the delete occurs. When the app receives a notification from Parse that the delete has completed we reload the table with fresh data from Parse — which will of course now be missing the previous target row.

Remember this app is not using any of the caching capabilites of Parse. You will need to adjust this code if you are using caching.

That’s it. You have added row deletion to your Parse table view.

Originally published at bizzi-body.com/blog on February 13, 2015.