Add multiple query results to array - ios

I am trying to run 4 queries from my parse database, each pulling just 1 object. I am trying to add each of the objects to an array that can then be used in a collection view. The queries run successfully but the app crashes because it is pulling null values. Here is my code:
var query1desc = ""
var query2desc = ""
var descs = [String]()
fileprivate func fetchUsers() {
let query1 = PFQuery(className: "Messages")
query1.limit = 1
query1.findObjectsInBackground{
(objects, error) -> Void in
if error == nil {
if objects?.count == 0 {
} else {
let object = objects![0] as! PFObject
self.query1desc = object["message"] as! String
}
}
}
let query2 = PFQuery(className: "Messages")
query2.limit = 1
query2.findObjectsInBackground{
(objects, error) -> Void in
if error == nil {
if objects?.count == 0 {
} else {
let object = objects![0] as! PFObject
self.query2desc = object["message"] as! String
}
}
}
self.descs = [self.query1desc, self.query2desc]
self.collectionView.reloadData()
Does anyone know of a way to fix this so that self.descs does not just provide nil values? When I print within the query itself I know that objects are being pulled. Thanks in advance.

I think desc variable holds nil value because your program is executing an array assignment code part while completion handlers are still processing query requests. You need to wait until all your query requests finish and then can collect all the results.
let workGroup = DispatchGroup()
let queueForQuery1 = DispatchQueue(label: "firstQuery")
let query1 = PFQuery(className: "Messages")
query1.limit = 1
workGroup.enter()
queueForQuery1.async(group:workGroup) {
query1.findObjectsInBackground{ (objects, error) -> Void in
if error == nil {
if objects?.count == 0 {
} else {
let object = objects![0] as! PFObject
self.query1desc = object["message"] as! String
}
}
workGroup.leave()
}
}
let queueForQuery2 = DispatchQueue(label: "secondQuery")
let query2 = PFQuery(className: "Messages")
query2.limit = 1
workGroup.enter()
queueForQuery2.async(group:workGroup) {
query2.findObjectsInBackground{ (objects, error) -> Void in
if error == nil {
if objects?.count == 0 {
} else {
let object = objects![0] as! PFObject
self.query1desc = object["message"] as! String
}
}
workGroup.leave()
}
}
workGroup.notify(queue: DispatchQueue.main){
self.descs = [self.query1desc, self.query2desc]
self.collectionView.reloadData()
}

Related

Using parse API in swift 3, only some query results are saving?

I'm new to Swift and programming. I'm using Xcode 8.2.1 and the Parse AWS (free) server.
I'm using the following Syntax in order to save all query results for specific object IDs in to one row of a new parse class.
Most of the query results are saving (e.g. primarycityquery, secondcityquery, citydetailsquery) but not all (e.g. multicityquery).
I'm using the exact same syntax in another UIViewController (with just 3 queries, though) and it's working fine.
Is the issue here that I'm running too many queries? This is all within viewDidLoad. Please advise if there is a better way to do this, as well.
Thank you!
let primaryCityQuery = PFQuery(className: "primaryCityDetails")
primaryCityQuery.whereKey("objectId", equalTo: passedPrimaryCityDetailsObjectIdString)
primaryCityQuery.findObjectsInBackground(block: { (objects, error) in
if let objects = objects {
for primaryCityQuery in objects {
if let primaryCity = primaryCityQuery["primaryCity"] as? String {
self.primaryCity = String(primaryCity)
print(self.primaryCity) }
if let primaryCountry = primaryCityQuery["primaryCountry"] as? String {
self.primaryCountry = String(primaryCountry)
print(self.primaryCountry) }
if let primaryState = primaryCityQuery["primaryState"] as? String {
self.primaryState = String(primaryState)
print(self.primaryState) }
if let numberDays = primaryCityQuery["numberDays"] as? String {
self.numberDays = String(numberDays)
print(self.numberDays) }
if let numberNights = primaryCityQuery["numberNights"] as? String {
self.numberNights = String(numberNights)
print(self.numberNights) }
} } else {
print(error!)
} })
//second city info
let secondCityQuery = PFQuery(className: "secondCityDetails")
secondCityQuery.whereKey("objectId", equalTo: passedSecondCityDetailsObjectIDString)
secondCityQuery.findObjectsInBackground(block: { (objects, error) in
if let objects = objects {
for secondCityQuery in objects {
if let secondCity = secondCityQuery["secondCity"] as? String {
self.secondCity = String(secondCity)
print(self.secondCity) }
if let secondCountry = secondCityQuery["secondCountry"] as? String {
self.secondCountry = String(secondCountry)
print(self.secondCountry) }
if let secondState = secondCityQuery["secondState"] as? String {
self.secondState = String(secondState)
print(self.secondState) }
} } else {
print(error!)
} })
//
let multiCityQuery = PFQuery(className: "multiCityDetails")
multiCityQuery.whereKey("objectId", equalTo: passedCityBeforeDetailsObjectIdString)
multiCityQuery.findObjectsInBackground(block: { (objects, error) in
if let objects = objects {
for multiCityQuery in objects {
if let beforeCity = multiCityQuery["beforeCity"] as? String {
self.beforeCity = String(beforeCity)
print(self.beforeCity) }
if let beforeCountry = multiCityQuery["beforeCountry"] as? String {
self.beforeCountry = String(beforeCountry)
print(self.beforeCountry) }
if let beforeState = multiCityQuery["beforeState"] as? String {
self.beforeState = String(beforeState)
print(self.beforeState) }
} } else {
print(error!)
} })
let inputtedCityDetailsQuery = PFQuery(className: "primaryTripDetails")
inputtedCityDetailsQuery.whereKey("objectId", equalTo: primTripDetails)
inputtedCityDetailsQuery.findObjectsInBackground(block: { (objects, error) in
if let objects = objects {
for inputtedCityDetailsQuery in objects {
if let primaryTripWhen = inputtedCityDetailsQuery["primaryTripWhen"] as? String {
self.primaryTripWhen = String(primaryTripWhen)
print(self.primaryTripWhen) }
if let primaryTripWhatSort = inputtedCityDetailsQuery["primaryTripWhatSort"] as? String {
self.primaryTripWhatSort = String(primaryTripWhatSort)
print(self.primaryTripWhatSort) }
if let primaryTripWhoWith = inputtedCityDetailsQuery["primaryTripWhoWith"] as? String {
self.primaryTripWhoWith = String(primaryTripWhoWith)
print(self.primaryTripWhoWith) }
if let primaryTripBudget = inputtedCityDetailsQuery["primaryTripBudget"] as? String {
self.primaryTripBudget = String(primaryTripBudget)
print(self.primaryTripBudget)
let fullCityInput = PFObject(className: "fullCityInputRow")
fullCityInput["primaryCity"] = self.primaryCity
fullCityInput["primaryCountry"] = self.primaryCountry
fullCityInput["primaryState"] = self.primaryState
fullCityInput["secondCity"] = self.secondCity
fullCityInput["secondCountry"] = self.secondCountry
fullCityInput["secondState"] = self.secondState
fullCityInput["beforeCity"] = self.beforeCity
fullCityInput["beforeCountry"] = self.beforeCountry
fullCityInput["beforeState"] = self.beforeState
fullCityInput["primaryTripWhoWith"] = self.primaryTripWhoWith
fullCityInput["primaryTripBudget"] = self.primaryTripBudget
fullCityInput["primaryTripWhen"] = self.primaryTripWhen
fullCityInput["primaryTripWhatSort"] = self.primaryTripWhatSort
fullCityInput["numberDays"] = self.numberDays
fullCityInput["numberNights"] = self.numberNights
fullCityInput.saveInBackground(block: { (success, error) in
if success {
print("full city input row saved")
} else {
if error != nil {
print (error!)
} else {
print("blanks")
}
}
})
}
}
} else {
print(error!)
} })
Thank you. It does seem the magic number is 3. I've saved the values that were not saving in this view controller to another and then query them in one of my 3 queries here. All of my inputs are now successfully saved in one row of another class.

Parse - Query after a specific row

How can I query after a specific row where the objectId is equal to a objectId I have stored?
This is my query code:
func queryStory(){
let query = PFQuery(className: "myClassStory")
query.whereKey("isPending", equalTo: false)
query.limit = 1000
query.orderByDescending("createdAt")
query.findObjectsInBackgroundWithBlock { (posts: [PFObject]?, error: NSError?) -> Void in
if (error == nil){
// Success fetching objects
for post in posts! {
if let imagefile = post["userFile"] as? PFFile {
self.userFile.append(post["userFile"] as! PFFile)
self.objID.append(post.objectId!)
self.createdAt.append(post.createdAt!)
}
}
print("Done!")
}
else{
print(error)
}
}
}
This is my Parse database class:
What I want, is to only query the items that was createdAt after the objectId: woaVSFn89t. How can I do this?
Try filtering the objects once you have found them:
query.findObjectsInBackgroundWithBlock { (posts: [PFObject]?, error: NSError?) -> Void in
if (error == nil){
// Success fetching objects
var thePostTime = NSDate()
for post in posts! {
if post.objectId == "The Object Id You Were Trying To Find" {
thePostTime = post.createdAt!
}
}
for post in posts! {
if post.createdAt!.isGreaterThan(thePostTime) == true {
if let imagefile = post["userFile"] as? PFFile {
self.userFile.append(post["userFile"] as! PFFile)
self.objID.append(post.objectId!)
self.createdAt.append(post.createdAt!)
}
}
}
print("Done!")
}
else{
print(error)
}
}
You will notice that I compared the dates using this: NSDate Comparison using Swift
Before the for-loop make a variable:
var havePassedObjectId = false
Then inside the for-loop check if the current post is equal to the object id you want:
if post.objectid == "woaVSFn89t" {
self.userFile.append(post["userFile"] as! PFFile)
//Continue appending to arrays where needed
havePassedObjectId = true
} else if havePassedObjectId == true {
self.userFile.append(post["userFile"] as! PFFile)
//Continue appending to arrays where needed
}
This will check if you have already passed the object and append all the objects after.

Swift Dispatch Groups with Parse queries

I have 4 functions containing Parse query.findObjectsInBackgroundWithBlock. These are being called to grab data and then populate the table view. Using dispatch groups.
Here is two examples of my parse querys
func getEventImages() {
print("getEventImages enter")
dispatch_group_enter(self.group)
let query = PFQuery(className: "events")
query.orderByAscending("eventDate")
query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]!, error: NSError!) -> Void in
// Initialize your array to contain all nil objects as
// placeholders for your images
if error == nil {
self.eventMainImageArray = [UIImage?](count: objects.count, repeatedValue: nil)
for i in 0...objects.count - 1 {
let object: AnyObject = objects[i]
let mainImage = object["mainImage"] as! PFFile
//dispatch_group_enter(self.group)
mainImage.getDataInBackgroundWithBlock({
(imageData: NSData!, error: NSError!) -> Void in
if (error == nil) {
let mainImage = UIImage(data:imageData)
self.eventMainImageArray[i] = mainImage
print("getEventImages appended")
}
else {
print("error!!")
}
})
}
}
print("getEventImages leave")
dispatch_group_leave(self.group)
}
}
func getEventInfo() {
print("eventInfo enter")
dispatch_group_enter(group)
let query = PFQuery(className: "events")
query.orderByAscending("eventDate")
query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]!,error: NSError!) -> Void in
self.eventNameArray = [String?](count: objects.count, repeatedValue: nil)
self.eventInfoArray = [String?](count: objects.count, repeatedValue: nil)
self.eventDateArray = [NSDate?](count: objects.count, repeatedValue: nil)
self.eventTicketsArray = [String?](count: objects.count, repeatedValue: nil)
if error == nil {
for i in 0...objects.count - 1 {
let object: AnyObject = objects[i]
let eventName = object["eventName"] as! String
let eventInfo = object["eventInfo"] as! String
let eventDate = object["eventDate"] as! NSDate
let eventTicket = object["Tickets"] as! String
self.eventNameArray[i] = eventName
self.eventInfoArray[i] = eventInfo
self.eventDateArray[i] = eventDate
self.eventTicketsArray[i] = eventTicket
print("event info appended")
}
}
print("event info leave")
dispatch_group_leave(self.group)
}
}
And my dispatch_group_nofity
dispatch_group_notify(group, dispatch_get_main_queue()) { () -> Void in
print("Finished reloadDataFromServer()")
self.tableView.reloadData()
self.refreshControl?.finishingLoading()
}
}
The problem is that its hit and miss if the data gets retrieved quick enough before dispatch_group_leave(self.group) is called leading to reloading the tableview data too soon. I need to get this so the dispatch_group_leave gets called when the appending is completed.
There is no need for two methods to retrieve the data, no need to unpack the data into multiple arrays and no need to use dispatch groups.
All you need is a simple method to retrieve your event data
var events:[PFObject]=[PFObject]()
func getEventInfo() {
let query = PFQuery(className: "events")
query.orderByAscending("eventDate")
query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]!,error: NSError!) -> Void in
if error==nil {
self.events=objects as! [PFObject]
self.tableView.reloadData()
} else {
print("Something went wrong! - \(error)"
}
self.refreshControl?.finishingLoading()
}
}
Then, you haven't shown your cellForRowAtIndexPath but you would have something like
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = self.tableView.dequeueReusableCellWithIdentifier("cell") as! MyTableViewCell
let event=self.events[indexPath.row]
cell.eventName.text=event["eventName"] as? String
cell.eventInfo.text=event["eventInfo"] as? String
if let mainImageFile=event["mainImage"] as? PFFile {
mainImageFile.getDataInBackgroundWithBlock({
(imageData: NSData!, error: NSError!) -> Void in
if (error == nil) {
let mainImage = UIImage(data:imageData)
cell.mainImage= mainImage
}
else {
print("error!!")
}
}
return cell;
}
You can use a PFImageView or a framework like SDWebImage to handle image caching and putting a placeholder image in place while the image is loaded.
If you want to update an event is as easy as
var event=self.events[someindex];
event["eventName"]=newValue
event.saveInBackground()

Swift 2. using Parse I can't save results of a PFUser.query() in an username array

Well I'm trying to save the results of a query and when I try to save it in an array it just doesn't do it.
Here's my code:
let query = PFUser.query()
query?.orderByDescending("puntaje")
query?.limit = 50
query?.findObjectsInBackgroundWithBlock({
(users, error) -> Void in
if let objects = users {
for object in objects {
self.usernames.removeAll(keepCapacity: true)
self.scores.removeAll(keepCapacity: true)
if let user = object as? PFUser {
print(user.username)
self.usernames.append(user.username!)
self.scores.append((user["puntaje"] as? Int)!)
}
}
}
print(self.usernames.count)
})
while printing user.username appears all the usernames.
and in the print it shows that I have 0 usernames.
You need to move
self.usernames.removeAll(keepCapacity: true)
self.scores.removeAll(keepCapacity: true)
above the "for" loop. Right under
if let objects = users {
I found another way.
I used:
let query = PFUser.query()
query?.orderByDescending("puntaje")
query?.limit = 50
do {
if let users = try query?.findObjects() {
for user in users as! [PFUser] {
let name = user.username!
self.usernames.append(name)
self.scores.append((user["puntaje"] as? Int)!)
}
}
}catch {
print(error)
}

Parse query containedIn doesn't return any value

Several days I'm trying to crack why my code doesn't work and everything I've tried doesn't give me any result. Heres the deal:
There is a Booking class that contains userFrom who made booking
let query = PFQuery(className: "Booking")
query.whereKey("offer", equalTo: offer.pfObject!)
if self.typeOfUser == .COOK { //! If user is a Cook
query.findObjectsInBackgroundWithBlock({ (objects : [PFObject]?, error : NSError?) -> Void in
if let error = error {
print(error.localizedDescription)
} else {
if let objects = objects {
self.bookings = objects
self.usersIds = [String]()
for object in objects {
let userFrom = object.objectForKey("userFrom") as? PFObject
let userId = userFrom!.objectId! as String
self.usersIds.append(userId)
}
self.getUserInfoForBooking()
} else {
print("Something went wrong")
}
}
})
}
From every user I get objectId and append it to the [String] array. Then I query users with their IDs
private func getUserInfoForBooking() {
let userQuery = PFQuery(className: "User")
userQuery.whereKey("objectId", containedIn: self.usersIds)
userQuery.findObjectsInBackgroundWithBlock({ (objects : [PFObject]?, error : NSError?) -> Void in
if let error = error {
print(error.localizedDescription)
} else {
print(objects!)
if let objects = objects {
for object in objects {
self.users.append(object)
}
self.collectionView.reloadData()
}
}
})
}
In this query I always get an empty array.
Whatever I did, whatever I've changed always [] in response :(
This is the wrong way to query users
let userQuery = PFQuery(className: "User")
Because the class name is private. You should be creating the query as
let userQuery = PFUser.query()

Resources