Counting found objects in PFTableQueryViewController - ios

I am trying to count the number of the found objects in PFQueryTableViewController.
I have tried working around with
override func queryForTable() -> PFQuery {
let query = PFQuery(className: self.parseClassName!)
query.whereKey("member", equalTo: memberId!)
let count = query.countObjectsInBackground()
label.text = "\(count)"
return query
}
But my app will crash.
EDIT:
The issue is not to make a query and count it's objects. The problem is to use queryForTable passing my query to cellForRowAtIndexPath of my PFQueryTableViewController
the cellForRowAtIndexPath looks like this:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> PFTableViewCell? {
let cell:DetailApplicantCell = self.table.dequeueReusableCellWithIdentifier("reuseIdentifier") as! DetailApplicantCell
if let name = object?.objectForKey(self.textKey!) as? String{
cell.nameLbl.text = name
}
cell.groupImage.image = UIImage(named: "People.png")
if let imageFile = object?.objectForKey(self.imageKey!) as? PFFile{
cell.groupImage.file = imageFile
cell.groupImage.loadInBackground()
}
return cell
}
NOTE that this is not the default cellForRow

Try with query.findObjectsInBackgroundWithBlock method and get the size() of the response object
let query = PFQuery(className: self.parseClassName!)
query.whereKey("member", equalTo: memberId!)
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
let count = objects.size()
label.text = "\(count)"
if let object = objects as? [PFObject] {
}
} else {
// Log details of the failure
print("Error: \(error!)")
}
}

You are force unwrapping at 2 places, use if let:
func queryForTable() -> PFQuery? {
if let parseClass = self.parseClassName {
let query = PFQuery(className: parseClass)
if let id = memberId {
query.whereKey("member", equalTo: id)
}
let count = query.countObjectsInBackground()
label.text = "\(count)"
return query
}
return nil
}
Then you use your function like:
if let query = queryForTable() {
//your query related code here.
}

Rather than doing a second PFQuery I found a better way using a method of PFQueryTableViewController like this:
override func objectsDidLoad(error: NSError?) {
super.objectsDidLoad(error)
print("objectsDidLoad")
if let results = self.objects{
print("objectsFound")
self.groupsCountLbl.text = "\(results.count)"
self.groupsCountLbl.fadeIn()
}
}
The VC has a property objects an array of AnyObject?.
With the objectsDidLoad function you determine the time, everything is loaded.

Related

How to create an array of usersnames when working with Parse and Swift

I am trying to create an array of strings for all the usernames using the following code and populate a TableViewController.
class TableViewController: UITableViewController {
var randomUser = [String]()
override func viewDidLoad() {
super.viewDidLoad()
var query: PFQuery = PFUser.query()!
query.findObjectsInBackgroundWithBlock {(objects: [PFObject]?, error: NSError?) -> Void in
if error == nil{
if let objects = (objects as? [PFObject]!){
for object in objects{
self.randomUser.append(object.objectForKey("username") as! String)
print(object.objectForKey("username") as! String)
print(self.randomUser.count)
}
}
}
}
print(self.randomUser.count)
}
the output in the console:
0
username
1
username
2
username
3
But UItableview does not populate.. What could be causing this?
My guess is that query is delayed and view is created before it can return data. Thank you for any help!
Yes, you are right. You need to call self.tableView.reloadData() after you get the results of the query. Below is an example of where to call it.
private var usersArray = [PFUser]()
func fetchUsers() {
let userQuery: PFQuery = PFUser.query()!
userQuery.orderByAscending("username")
userQuery.whereKey("username", notEqualTo: (currentUser?.username)!)
userQuery.findObjectsInBackgroundWithBlock({
(users, error) -> Void in
if error == nil {
self.usersArray = users as! [PFUser]
self.tableView.reloadData()
} else {
print(error)
}
})
}
In this example, you can then access the username property by doing usersArray[i].username

UITableview reloading tableview and cell

I am trying to reload my table view using
self.tableView.reloadData()
It works properly if I'm loading static datasource using array. Everything work properly.
But when I try to use my query function with parse, it loads a new cell but the contents of the tableview cell doesn't change. If I re-open the app, the cells will update properly.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "EmpPostTVCellIdentifier"
let cell: EmpPostTVCell? = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as? EmpPostTVCell
//If datasource
if dataSource.isEmpty{
fetchDataFromParse()
print("no posts")
}
let itemArr:PFObject = self.dataSource[indexPath.row]
cell?.companyPostLabel.text = (PFUser.currentUser()?.objectForKey("companyName")!.capitalizedString)! as String
cell?.occupationPostLabel.text = itemArr["occupation"]!.capitalizedString as String
cell?.countryPostLabel.text = itemArr["country"]!.capitalizedString as String
let companyImage: PFFile?
companyImage = PFUser.currentUser()?.objectForKey("profileImageEmployer") as? PFFile
companyImage?.getDataInBackgroundWithBlock({ (data, error) -> Void in
if error == nil{
cell?.companyLogoImage.image = UIImage(data: data!)
}
})
let dateArr = createdByDate[indexPath.row]
let strDate = Settings.dateFormatter(dateArr)
cell?.closingDateLabel .text = strDate
return cell!
}
I am using pull to refresh my tableviews contents using this code
func refresh(sender:AnyObject)
{
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.fetchDataFromParse()
self.tableView.reloadData()
self.refreshControl?.endRefreshing()
})
}
with or without the dispatch_asynch function the results remains the same. It just add new tableviewcell but the contents in it does not change. Any ideas guys?
edit 1 :
func fetchDataFromParse() {
// MARK: - JOB POST QUERY
if PFUser.currentUser()?.objectId == nil{
PFUser.currentUser()?.saveInBackgroundWithBlock({ (success, error) -> Void in
let query = PFQuery(className: "JobPost")
//creating a pointer
let userPointer = PFUser.objectWithoutDataWithObjectId(PFUser.currentUser()?.objectId)
query.whereKey("postedBy", equalTo: userPointer)
query.orderByDescending("createdAt")
let objects = query.findObjects()
for object in (objects as? [PFObject])!{
//print(object.objectId)
self.dataSource.append(object)
self.createdByDate.append((object.objectForKey("closingDate") as? NSDate)!)
print(self.dataSource)
print(self.createdByDate)
}
})
} else {
let query = PFQuery(className: "JobPost")
//creating a pointer
let userPointer = PFUser.objectWithoutDataWithObjectId(PFUser.currentUser()?.objectId)
query.whereKey("postedBy", equalTo: userPointer)
query.orderByDescending("createdAt")
let objects = query.findObjects()
for object in (objects as? [PFObject])!{
//print(object.objectId)
self.dataSource.append(object)
self.createdByDate.append((object.objectForKey("closingDate") as? NSDate)!)
print(self.dataSource)
print(self.createdByDate)
}
}//end of PFUser objectID == nil else clause
}
Let's see the content of the fetchDataFromParse() function where I presume you're filling the self.dataSource array
Try to call self.tableview.reloadData() when fetchDataFromParse() is finished.
Check whether your dataSource array is empty at the end of your fetchDataFromParse method
PFUser.currentUser()?.saveInBackgroundWithBlock is an asynchronus method. So your tableView cell is having no data.

Error with Parse: PFObject does not have a member named 'createdAt'

I am stuck on that issue. I've read the Parse documentation (https://parse.com/docs/ios/guide#queries-query-constraints) but it doesn't helps me that much.
The error
When I try to get "createdAt" or "updatedAt" from a PFObject, I got the following error:
['PFObject'] does not have a member named 'createdAt'
The Code
Here is the (shortened) function:
func loadCheckInData() {
var query = PFQuery(className: "CheckIn")
query.orderByDescending("createdAt")
query.selectKeys(["firstName","lastName","updatedAt","createdAt"])
query.findObjectsInBackgroundWithBlock({
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
println(objects)
// Do something
if let object = objects as? [PFObject] {
println("\(object.createdAt)") <---- Error here
self.CheckedPatients = Array(object.generate())
}
} else {
// Log details of the failure
println("Error: \(error!) \(error!.userInfo!)")
}
})
}
Then I retrieve "firstName", "lastName" (and try to retrieve "createdAt") that are in my "CheckIn" Parse's class with the following code
func collectionView(cellView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
// Add parse data
if let value = CheckedPatients[indexPath.row]["lastName"] as? String {
cell.cellLabelTwo.text = value
}
if let value = CheckedPatients[indexPath.row]["firstName"] as? String {
cell.cellLabel.text = value
}
if let value = CheckedPatients[indexPath.row]["createdAt"] as? String {
cell.cellDay.text = value
}
return cell
}
And I call the function
override func viewDidAppear(animated: Bool) {
loadCheckInData()
}
In fact I tried differents method to get that "createdAt" value, and I can't make it works. Does anyone have an idea (and a quick explanation if possible) it could be nice.
Thank you !
It's simply because updatedAt/createdAt is a property on all PFObjects, no need to retrieve it using a key, just treat it as property.
I figured out how to get the property, thanks to #SanitLee answer. I post my code with the solution so that everybody could see it.
Solution
In my function named loadCheckInData() I added for object in objects { to the findObjectsInBackgroundWithBlock method. See below:
func loadCheckInData() {
var query = PFQuery(className: "CheckIn")
query.orderByDescending("createdAt")
query.findObjectsInBackgroundWithBlock({
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
// Do something
if let objects = objects as? [PFObject] {
for object in objects { // <--- Added
// data stored in the var named "CheckedPatient"
self.CheckedPatients = Array(objects.generate())
}
}
} else {
// ...
}
})
}
Then to retrieve the createdAt property inside the cellForItemAtIndexPath function, I changed:
if let value = CheckedPatients[indexPath.row]["createdAt"] as? String {
cell.cellDay.text = value
}
into:
var DateVar : PFObject = CheckedPatients[indexPath.row]
var dateFormatter:NSDateFormatter = NSDateFormatter() // Formating
dateFormatter.dateFormat = "EEEE dd MMM HH:mm"
cell.cellDay.text = dateFormatter.stringFromDate(DateVar.createdAt!)
Now it works as I want. If you have any advice, thorough explanations or improvement about the code, feel free to modify and explain it.
Thanks

Parse, iOS, includeKey query does not retrieve attribute of pointer object

I'm quite new to working with Parse and I'm building a todo list as part of a CRM. Each task in the table view shows the description, due date, and client name. The description and due date are in my Task class, as well as a pointer to the Deal class. Client is a string in the Deal class. I'm able to query the description and due date properly, but I am not able to retrieve the client attribute from within the Deal object by using includeKey. I followed the Parse documentation for includeKey.
The description and due date show up properly in the resulting table view, but not the client. The log shows client label: nil and the printed task details include <Deal: 0x7ff033d1ed40, objectId: HffKOiJrTq>, but nothing about the client attribute. How can I retrieve and assign the pointer object's attribute (client) to my label within the table view? My relevant code is below. Thank you in advance.
Edit: I've updated my code with func fetchClients() based on this SO answer, but I'm still not sure whether my function is complete or where to call it.
class TasksVC: UITableViewController {
var taskObjects:NSMutableArray! = NSMutableArray()
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
println("\(PFUser.currentUser())")
self.fetchAllObjects()
self.fetchClients()
}
func fetchAllObjects() {
var query:PFQuery = PFQuery(className: "Task")
query.whereKey("username", equalTo: PFUser.currentUser()!)
query.orderByAscending("dueDate")
query.addAscendingOrder("desc")
query.includeKey("deal")
query.findObjectsInBackgroundWithBlock { (tasks: [AnyObject]!, error:NSError!) -> Void in
if (error == nil) {
var temp:NSArray = tasks! as NSArray
self.taskObjects = temp.mutableCopy() as NSMutableArray
println(tasks)
self.tableView.reloadData()
} else {
println(error?.userInfo)
}
}
}
func fetchClients() {
var task:PFObject = PFObject(className: "Task")
var deal:PFObject = task["deal"] as PFObject
deal.fetchIfNeededInBackgroundWithBlock {
(deal: PFObject!, error: NSError!) -> Void in
let client = deal["client"] as NSString
}
}
//MARK: - Tasks table view
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.taskObjects.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("TaskCell", forIndexPath: indexPath) as TaskCell
var dateFormatter:NSDateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "M/dd/yy"
var task:PFObject = self.taskObjects.objectAtIndex(indexPath.row) as PFObject
cell.desc_Lbl?.text = task["desc"] as? String
cell.date_Lbl.text = dateFormatter.stringFromDate(task["dueDate"] as NSDate)
cell.client_Lbl?.text = task["client"] as? String
var clientLabel = cell.client_Lbl?.text
println("client label: \(clientLabel)")
return cell
}
}
If the deal column is a pointer then includeKey("deal") will get that object and populate it's properties for you. There is no need to perform a fetch of any type on top of that.
You really should be using Optionals properly though:
if let deal = task["deal"] as? PFObject {
// deal column has data
if let client = deal["client"] as? String {
// client has data
cell.client_Lbl?.text = client
}
}
Alternatively you can replace the last if let with a line like this, which handles empty values and uses a default:
cell.client_Lbl?.text = (deal["client"] as? String) ?? ""
In your posted cellForRowAtIndexPath code you are trying to read client from the task instead of from the deal: task["client"] as? String.

Parse Query Wrapping Error, works fine on simulator crashes on device

The code below runs fine on the simulator then crashes on two devices and works on one device.
I'm also getting this:
function signature specialization <Arg[0] = Exploded, Arg[1] = Exploded> of Swift.(_fatalErrorMessage (Swift.StaticString, Swift.StaticString, Swift.StaticString, Swift.UInt) -> ()).(closure #2)
Would it possibly have to do with bridging obj-C into my swift application?
Any suggestions
var query = PFUser.query()
query?.whereKey("username", notEqualTo: PFUser.currentUser()!.username!)
var users = query?.findObjects()
//Loop through users
if let users = users as? [PFUser]{
for user in users {
//println(user.username)
self.userArray.append(user.username!)
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! UITableViewCell
// Configure the cell...
cell.textLabel?.text = userArray[indexPath.row]
return cell
}
You want to perform the query in the background so the UI (main thread) stays responsive. Try the following:
if let currentUsername = PFUser.currentUser()?.username {
var query = PFUser.query()!
query.whereKey("username", notEqualTo: currentUsername)
query.findObjectsInBackgroundWithBlock { (objects: [AnyObject]?, error: NSError?) -> Void in
if (error == nil) {
if let users = objects as? [PFUser] {
for user in users {
self.userArray.append(user.username!)
}
}
} else {
// Log details of the failure
println("query error: \(error) \(error!.userInfo!)")
}
}
}
The first place I'd look is the forced unwrap of the optionals. Every one of those is asking for a crash -- when PFUser.currentUser() returns nil and user.username returns nil:
Try:
var query = PFUser.query()
if let query = query,
currentUser = PFUser.currentUser() as? PFUser,
currentUsername = currentUser.username {
query.whereKey("username", notEqualTo: currentUsername)
var users = query.findObjects()
//Loop through users
if let users = users as? [PFUser]{
for user in users {
//println(user.username)
if let username = user.username {
self.userArray.append(username)
}
}
}

Resources