Parse Tutorial: Getting Started with Web Backends

Get started with Parse, and learn how to set up your iOS app with a backend that lets you store user accounts, posts, and attachments! By Ron Kliffer.

Leave a rating/review
Save for later
Share
You are currently viewing page 4 of 5 of this article. Click here to view the first page.

Showing Pictures on the Wall

Open WallPicturesViewController.swift. This view will show all of your users uploaded pictures.
When the view loads, it calls getWallImages() to retrieve all the objects. As you can see, it’s empty now.

Before you can make that work, you’ll need to add some code to place the images on the wall once you’ve retrieved them. To that end, add loadWallViews(_:) as follows:

func loadWallViews(objects: [WallPost]) {
  cleanWall()
  
  var originY: CGFloat = 0
  
  for wallPost in objects {
    //1
    let wallView = UIView(frame: CGRect(x: 0, y: originY,
      width: self.wallScroll.frame.size.width, height: 270))
  
    //2
    wallPost.image.getDataInBackgroundWithBlock { data, error in
      if let data = data, image = UIImage(data: data) {
        //3
        //Add the image
        let imageView = UIImageView(image: image)
        imageView.frame = CGRect(x: 10, y: 10, width: wallView.frame.size.width - 20, height: 200)
        imageView.contentMode = UIViewContentMode.ScaleAspectFit
        wallView.addSubview(imageView)
            
        //4
        //Add the info label (User and creation date)
        let creationDate = wallPost.createdAt
        let dateFormatter = NSDateFormatter()
        dateFormatter.dateFormat = "HH:mm dd/MM yyyy"
            
        let infoLabel = UILabel(frame: CGRect(x: 10, y: 220, width: 0, height: 0))
        let dateString = dateFormatter.stringFromDate(creationDate!)
            
        if let username = wallPost.user.username {
          infoLabel.text = "Uploaded by: \(username), \(dateString)"
        } else {
          infoLabel.text = "Uploaded by anonymous: , \(dateString)"
        }
            
        infoLabel.text = "Uploaded by: \(wallPost.user.username), \(dateString)"
        infoLabel.font = UIFont(name: "HelveticaNeue", size: 12)
        infoLabel.textColor = UIColor.whiteColor()
        infoLabel.backgroundColor = UIColor.clearColor()
        infoLabel.sizeToFit()
        wallView.addSubview(infoLabel)
            
        //5
        //Add the comment label (User and creation date)
        let commentLabel = UILabel(frame: CGRect(x: 10, y: CGRectGetMaxY(infoLabel.frame)+5, width:0, height: 0))
        commentLabel.text = wallPost.comment
        commentLabel.font = UIFont(name: "HelveticaNeue", size: 16)
        commentLabel.textColor = UIColor.whiteColor()
        commentLabel.backgroundColor = UIColor.clearColor()
        commentLabel.sizeToFit()
        wallView.addSubview(commentLabel)
      }
    }
  
    //6
    wallScroll.addSubview(wallView)
    originY += 270
  }
  //7
  wallScroll.contentSize.height = CGFloat(originY)
}

The first step is to clean the scroll view of any other UIView objects that may be there, for example, if you reload the screen. Then you use fast enumeration to go through the whole array of objects. For every object in the array:

  1. Create a view to display the image and post details.
  2. Download the image data.
  3. Add the image view to the wall view
  4. Get the user that uploaded the image, and put it in a label with the creation date.
  5. Add a label with the comment.
  6. Add it to the scroll view and increment the next post’s position.
  7. Set the ScrollView’s content size.

Now, replace the content of getWallImages() with the following code:

func getWallImages() {
  //1
  let query = WallPost.query()!
  query.findObjectsInBackgroundWithBlock { objects, error in
    if error == nil {
      //2
      if let objects = objects as? [WallPost] {
        self.loadWallViews(objects)
      }
    } else if let error = error {
      //3
      self.showErrorView(error)
    }
  }
}

Here’s what this code is doing:

  1. Create a simple query to retrieve WallPost objects, and sorts the query by creation date as we defined earlier in WallPost.swift.
  2. Find the object that matches the query. In this case, show all the objects of the type WallPost. If everything went fine, load the posts to the wall.
  3. If there was an error, then inform the user.

Build and run! You should see the image and the comment you previously uploaded. Take some time now to play with the app, uploading some more images and comments, and seeing them appear on your wall!

Feels good, doesn’t it? :]

ParseUI

As noted earlier there is another way to present the saved images. That’s the approach you will implement next.

In the previous section you presented the images in a simple UIScrollView, and calculated their sizes yourself. You may have thought that this job would be better done with a UITableView. Well, the smart people at Parse thought the same thing, so they wrote ParseUI.framework and filled it with some very handy objects to display Parse specific UI.

You’re going to focus on these three:

  • PFQueryTableViewController: This UITableViewController subclass makes is as simple as it can be to execute a PFQuery and display the result in a table.
  • PFTableViewCell: A UITableViewCell subclass to go along with the PFQueryTableViewController object.
  • PFImageView: This UIImageView subclass manages downloading and displaying images stored on Parse’s server.

Now, open WallPicturesTableViewController.swift and change its superclass from UITableViewController to PFQueryTableViewController.

Similarly, modify WallPostTableViewCell to make it a subclass of PFTableViewCell instead of UITableViewCell. In addition change the postImage property to be of type PFImageView instead of UIImageView.

Before you add code, you need to make some changes to your storyboard. Open Main.storyboard and locate the WallTableView scene:

TableView Screenshot

Open the Attributes Inspector and you’ll see you have an option to set parameters that are specific to PFQueryTableViewController:

Parse14

These parameters let you choose the object type to fetch and show in the table, and also let you define the the table’s behavior by adding pull to refresh, pagination, and a loading view. When using a simple UITableView to present the results, you could even set the PFObject keys you want shown in the table, without needing to fill it in code. Under Parse Class enter WallPost.

Now go back to WallPicturesTableViewController.swift and add the following methods:

override func viewWillAppear(animated: Bool) {
  loadObjects()
}

override func queryForTable() -> PFQuery {
  let query = WallPost.query()
  return query!
}

Every time the view appears, you want to reload the query and the table view. To specify which query to run, you override queryForTable() to return a query for a WallPost.

Finally, add the following table view delegate method to the class:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject!) -> PFTableViewCell? {
  // 1
  let cell = tableView.dequeueReusableCellWithIdentifier("WallPostCell", forIndexPath: indexPath) as! WallPostTableViewCell
    
  // 2           
  let wallPost = object as! WallPost
    
  // 3   
  cell.postImage.file = wallPost.image
  cell.postImage.loadInBackground(nil) { percent in
  cell.progressView.progress = Float(percent)*0.01
    println("\(percent)%")
  }
    
  // 4           
  let creationDate = wallPost.createdAt
  let dateFormatter = NSDateFormatter()
  dateFormatter.dateFormat = "HH:mm dd/MM yyyy"
  let dateString = dateFormatter.stringFromDate(creationDate!)

  if let username = wallPost.user.username {
    cell.createdByLabel.text = "Uploaded by: \(username), \(dateString)"
  } else {
    cell.createdByLabel.text = "Uploaded by anonymous: , \(dateString)"
  }

  cell.createdByLabel.text = "Uploaded by: \(wallPost.user.username), \(dateString)"
        
  cell.commentLabel.text = wallPost.comment
        
  return cell
}

This method replaces the UITableView data source method tableView(_:cellForRowAtIndexPath:) with a more suitable form. You get the returned PFObject as a parameter, without the need search it in a results array using and index path.

Let’s go over what this code does:

  1. Dequeue a cell from the table view, and cast it to a WallPostTableViewCell.
  2. Cast the given PFObject to a WallPost object
  3. Download the post image using PFImageView‘s loadInBackground method. In the completion closure you track the download’s progress. Here you fill a UIProgressBar as the image downloads.
  4. Add the creation date, the user’s name and the comment to the cell.

Before you can run this code, there’s one last thing you need to do. Go to LoginViewController.swift, and inside logInPressed(_:) replace the segues name from scrollViewWallSegue to tableViewWallSegue. Do the same in RegisterViewController.swift.

Build and run you code, and you’ll see the wall posts presented in a table view with a nice progress bar filling up as the images are downloading.