Search code examples
iosswiftparse-platformpfquery

Swift- Creating a profile page like Twitter or Instagram through PFQueryTableViewController


I have a TableView subclassed as a PFQueryTableViewController and I want to know how I can retrieve an inventory of users post from Parse. I'm having trouble receiving the users data and displaying with my code. Is there something I need to edit in my queryForTable, change in parse or change in my code?

Here is my ProfileViewController:

import UIKit
import Parse
import ParseUI

class ProfileViewController: PFQueryTableViewController{

    var profilePosts:NSMutableArray! = NSMutableArray()

    override init(style: UITableViewStyle, className: String!) {
        super.init(style: style, className: className)
    }


    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        self.parseClassName = "Test"
        self.textKey = "postMessage"
        self.pullToRefreshEnabled = true
        self.objectsPerPage = 200

    }


    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    override func queryForTable() -> PFQuery {
        let query = PFQuery(className: "Test")
        query.whereKey("username", equalTo: PFUser.currentUser()!)
        query.limit = 200;

        return query
    }

    // MARK: - Table view data source

    override func objectAtIndexPath(indexPath: NSIndexPath!) -> PFObject? {
        var obj : PFObject? = nil
        if(indexPath.row < self.objects!.count){
            obj = self.objects![indexPath.row] as? PFObject
        }

        return obj
    }

    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        // #warning Incomplete implementation, return the number of sections
        return 1
    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // #warning Incomplete implementation, return the number of rows
        return profilePosts.count
    }


    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath?, object:PFObject!) -> PFTableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("PCell", forIndexPath: indexPath!) as! ProfileCell

        if let profilePost : PFObject = self.profilePosts.objectAtIndex(indexPath!.row) as! PFObject {

            cell.NameProfile.text = object["account"] as? String
            cell.messageProfile.text = object["postMessage"] as? String
            let dateUpdated = object.createdAt! as NSDate
            let dateFormat = NSDateFormatter()
            dateFormat.dateFormat = "MMM d, h:mm a"

            cell.timeProfile.text =  NSString(format: "%@", dateFormat.stringFromDate(dateUpdated)) as String
            cell.messageProfile.numberOfLines = 0
            let likeScore = object[("count")] as! Int
            cell.likeScoreProfile.text = "\(likeScore)"
            let replyscore = object["replies"] as! Int
            cell.replyScoreProfile.text = "\(replyscore) replies"
            cell.messageProfile.text = profilePost.objectForKey("postMessage") as? String
            if let profilePic = object["profilePhoto"] as? PFFile {
                let imageView = cell.profilePhoto as PFImageView

                imageView.image = UIImage(named: "profileImg")
                imageView.file = profilePic

                imageView.loadInBackground(nil) 
            }


        return cell
    }

Solution

  • You never assign your profilePosts to the objects that are returned from parse. You have two options for fixing this. One is to just stick with the default parse implementation of objects which allows you to use objects whenever you need one of your database objects. The other is to stick with your profilePosts array and assign it in objectsDidLoad which would prevent you from having to change any code except for that. Otherwise in your methods like numberOfRowsInSection you would need to return objects!.count instead.

    In the case that you decide to use your own array of profilePosts you'll want to do this:

    import UIKit
    import Parse
    import ParseUI
    
    class ProfileViewController: PFQueryTableViewController{
    
    var profilePosts:NSMutableArray! = NSMutableArray()
    
    override init(style: UITableViewStyle, className: String!) {
        super.init(style: style, className: className)
    }
    
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    
        self.parseClassName = "Test"
        self.textKey = "postMessage"
        self.pullToRefreshEnabled = true
        self.objectsPerPage = 200
    
    }
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    override func objectsDidLoad(error: NSError?) {
        super.objectsDidLoad(error)
        profilePosts = NSMutableArray(array: objects!)
        self.tableView.reloadData()
    }
    
    override func queryForTable() -> PFQuery {
        let query = PFQuery(className: "Test")
        query.whereKey("username", equalTo: PFUser.currentUser()!.username)
        query.limit = 200;
    
        return query
    }
    
    // MARK: - Table view data source
    
    override func objectAtIndexPath(indexPath: NSIndexPath!) -> PFObject? {
        var obj : PFObject? = nil
        if(indexPath.row < self.profilePosts.count){
            obj = self.profilePosts[indexPath.row] as? PFObject
        }
    
        return obj
    }
    
    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        // #warning Incomplete implementation, return the number of sections
        return 1
    }
    
    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // #warning Incomplete implementation, return the number of rows
        return profilePosts.count
    }
    
    
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath?) -> PFTableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("PCell", forIndexPath: indexPath!) as! ProfileCell
    
        if let object : PFObject = self.profilePosts.objectAtIndex(indexPath!.row) as? PFObject {
    
            cell.NameProfile.text = object["account"] as? String
            cell.messageProfile.text = object["postMessage"] as? String
            let dateUpdated = object.createdAt! as NSDate
            let dateFormat = NSDateFormatter()
            dateFormat.dateFormat = "MMM d, h:mm a"
    
            cell.timeProfile.text =  NSString(format: "%@", dateFormat.stringFromDate(dateUpdated)) as String
            cell.messageProfile.numberOfLines = 0
            let likeScore = object[("count")] as! Int
            cell.likeScoreProfile.text = "\(likeScore)"
            let replyscore = object["replies"] as! Int
            cell.replyScoreProfile.text = "\(replyscore) replies"
            cell.messageProfile.text = profilePost.objectForKey("postMessage") as? String
            if let profilePic = object["profilePhoto"] as? PFFile {
                let imageView = cell.profilePhoto as PFImageView
    
                imageView.image = UIImage(named: "profileImg")
                imageView.file = profilePic
    
                imageView.loadInBackground(nil) 
            }
    
    
        return cell
    }
    

    Notice how I changed cellForRowAtIndexPath to use the object you were getting from profilePosts and then how I overrode objectsDidLoad and assigned the PFQueryTableView objects property to your profilePosts array. One reason you may want to do this is because by having your own managed copy of the Parse objects you can perform things like insertion or deletion with animation, which the regular objects property does not allow you to do. Hopefully this helps you out, let me know if you have more questions.

    EDIT 2 In light of your comment about your more specific question, I would suggest that you edit your original question to specify that you are asking about how to get just the current user's objects, and not about which implementation to use. I am leaving that code above in there however, because the queryForTable method has been modified to show what you need to do to meet your query demands. It also does show those that come looking how to properly retrieve objects if they are having issues.