How to implement Parse query properly to get ordered data - ios

I have below code to build a query out of two queries.
Issue is result is not sorted by updatedAt.
What could be the issue?
let ownerQuery = PFQuery(className: "requests")
ownerQuery.whereKey("owner", equalTo: forUser)
ownerQuery.whereKey("stage", equalTo: "initiated")
let requestQuery = PFQuery(className: "requests")
requestQuery.whereKey("requested_by", equalTo: requestedBy)
requestQuery.whereKey("stage", equalTo: "accepted")
// fetch all request of current user as requestor or lender/seller
let query = PFQuery.orQuery(withSubqueries: [ownerQuery, requestQuery])
query.includeKey("requested_by")
query.includeKey("owner")
query.order(byDescending: "updatedAt")

issue turned out to be code where i was processing the retrieved data. Parse returned sorted result.
I have below code where ordering gets affected. it seems to be because of getDetails() returning in async.
So i have added array sort before returning. I retrieve 20 rows at a time so sorting every array should not be costly i guess.
I had also posted on GitHub. Awaiting confirmation.
query.findObjectsInBackground { (results, error) in
if error == nil {
for result in results! {
group.enter()
let stage = result["stage"] as! String
let requestedBy = result["requested_by"] as! PFUser
DBHelper.getDetails(result["requested_id"] as! String, callbackFunction: { (requestedItem) in
let owner = result["owner"] as! PFUser
let request = Request()
request.requestId = result.objectId
request.item = requestedItem
request.owner = owner
request.lastUpdatedDate = result.updatedAt
requestsFound.append(request)
group.leave()
})
}
group.notify(queue: DispatchQueue.global(qos: .background), execute: {
DispatchQueue.main.async {
requestsFound.sort {(request1:Request, request2:Request) -> Bool in
request1.lastUpdatedDate! < request2.lastUpdatedDate!
}
callbackFunction(requestsFound,nil)
}
})
} else {
// encountered error from Parse
DispatchQueue.main.async {
callbackFunction(requestsFound,error! as NSError) // to represent error at backend server
}
}
} //end of findObjectsInBackground

Try this one:
ownerQuery.order(byAscending: "updatedAt")

Related

Xcode Parse Query button Delete text and photo from a cell

each cell displays the according comments and photo of the user logged-on. They are loaded with parse.
Now you want to Löschen the button deletes the photo and the comments.
Unfortunately this does not work. Wen I click on the button nothing happens
Unfortunately I understand little of swift and can't get on the solution
The query works, and the app displays the photos and Commons.The query and post code:
override func viewDidLoad() {
super.viewDidLoad()
super.viewDidLoad()
let query = PFQuery(className: "Post")
query.whereKey("username", equalTo: PFUser.current()?.username)
query.findObjectsInBackground(block: { (object, error) in
if let posts = object {
for post in posts{
print(posts)
self.comments.append(post["message"] as! String)
self.imageFile.append(post["imageFile"] as! PFFile)
self.tableView.reloadData()
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
})
}
And here of the "delete"function code that I have tried:
#IBAction func remove(_ sender: Any) {
print("Entered remove")
let query = PFQuery(className: "Post")
query.whereKey("username", equalTo: PFUser.current()?.username)
query.findObjectsInBackground(block: { (object, error) in
if let posts = object {
print(posts)
for post in posts{
print(posts)
if let message = post["message"] as? String, let image = post["imageFile"] as? PFFile {
print("message and image read", message, image)
if let messageIndex = self.comments.index(of: message), let imageIndex = self.imageFile.index(of:image) {
self.comments.remove(at: messageIndex)
self.imageFile.remove(at: imageIndex)
}
}
self.tableView.reloadData()
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
})
}
The output:
I don't get an error message and nothing is deleted.
Thank you for your help
You do not have access to your current index where and object ids.
So based on that you can remove easy.
The more easy way to implement the delete function is to have an array of objectId for your messages:
self.ids.append(post.objectId)
And when you want to delete it:
let query = PFQuery(className: "Post")
query.whereKey("objectId", equalTo: self.ids.index(of: indexPath.row))
// Make a query in background to only get the object that you want to delete
query.getFirstObjectInBackgroundWithBlock {
(object: PFObject?, error: NSError?) -> Void in
if error != nil || object == nil {
print("The getFirstObject request failed.")
} else if let object = object {
print("Successfully retrieved the object.")
object.deleteInBackground()
}
}
Having different arrays representing the same object is not really good to do. So a better way to handle you problem is have only one array for your post
When you fetch it you can do something like that:
guard let user = PFUser.current() else { return }
let query = PFQuery(className: "Post")
query.whereKey("username", equalTo: user.username)
query.findObjectsInBackground(block: { (posts, error) in
if let posts = posts {
self.posts = posts
}
})
With this way when you want to delete it in the remove function:
if indexPath.row < self.posts.count {
self.posts[indexPath.row].deleteInBackground()
}

Only show friends posts on Parse

new programmer trying to learn Swift and I'm trying to set my app up to only show the current users post on a parse photosharing database. The first method here in theory should add the current user to the "followingWho" array.
override func viewDidLoad() {
super.viewDidLoad()
let userQuery = PFUser.query()
userQuery?.whereKey("username", equalTo: PFUser.currentUser()!.username!)
userQuery?.findObjectsInBackgroundWithBlock ({
(objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
//no error
if let objects = objects {
for object in objects {
let followingWho = object["followingWho"] as! NSArray!
self.loadData(followingWho)
}
}
} else {
//error
NSLog("Error")
}
})
self.tableView.reloadData()
}
then my second method here should display the posts and filter out all but the current users
func loadData(followingWho: NSArray) {
let query = PFQuery(className: "Posts")
query.whereKey("addedBy", containedIn: followingWho as! [PFObject])
query.orderByDescending("createdAt")
query.findObjectsInBackgroundWithBlock {
(posts: [PFObject]?, error: NSError?) -> Void in
if (error == nil) {
//No error
//let posts = posts as! [PFObject]
if let posts = posts {
for post in posts {
self.images.append(post["Image"] as! PFFile)
self.imageCaptions.append(post["Caption"] as! String)
self.imageDates.append(post["date"] as! String)
self.imageUsers.append(post["addedby"] as! String)
}
self.tableView.reloadData()
}
} else {
//error
NSLog("Error")
}
}
}
But nothing seems to be working. The code compiles and runs but it seems to only pull up a blank screen loading none of the posts.
This could be the problem, although if it is not the problem, it is certainly a problem: you are trying to update the UI from a background thread. findObjectsInBackgroundWithBlock calls its completion block in the background, and updating UI from the background is a big no-no. Instead, call your UI updates on the main thread:
dispatch_async(dispatch_get_main_queue(), {
//Do your UI updates here
})

Querying from Parse Classes

I am trying to query the user's from _User class, which I have managed successfully. But what I am trying to do now where I'm having a bit of difficulty, is query another class, Posts, and download the images that match the users downloaded from the first query?!
So I am just trying to assign the images from the Posts class to the correct users from the _User class...It sounds very simple, but it driving me mad!!
Here's my code for the query, I know it's probably not the best way but I'm newish to Swift! But I'm willing to try any tips or recommendations if you have any!
let userQuery = PFQuery(className: "_User")
userQuery.addDescendingOrder("createdAt")
userQuery.findObjectsInBackgroundWithBlock ({
(objects:[PFObject]?, error:NSError?) -> Void in
if error == nil {
self.profilePicArray.removeAll(keepCapacity: false)
self.usernameArray.removeAll(keepCapacity: false)
self.fullnameArray.removeAll(keepCapacity: false)
self.uuidArray.removeAll(keepCapacity: false)
for an object in objects! {
self.profilePicArray.append(object.valueForKey("profilePicture") as! PFFile)
self.usernameArray.append(object.valueForKey("username") as! String)
self.fullnameArray.append(object.valueForKey("firstname") as! String)
self.uuidArray.append(object.valueForKey("uuid") as! String)
}
let imageQuery = PFQuery(className: "Posts")
imageQuery.whereKey("username", containedIn: self.usernameArray)
imageQuery.findObjectsInBackgroundWithBlock({ (objects:[PFObject]?, error:NSError?) -> Void in
if error == nil {
self.lastPicArray.removeAll(keepCapacity: false)
for an object in objects! {
self.lastPicArray.append(object.valueForKey("image") as! PFFile)
}
self.collectionView.reloadData()
} else {
print(error!.localizedDescription)
}
})
} else {
print(error!.localizedDescription)
}
})
The result I am getting at the moment is that: all user's download and also all the posts, but the images are just assigned randomly to each user or all the posts appear for each single user!
Thanks in advance.
New Query
func uu() {
let query = PFQuery(className: "_User")
query.addDescendingOrder("createdAt")
query.includeKey("latestImage")
query.whereKey("username", notEqualTo: PFUser.currentUser()!.username!)
query.findObjectsInBackgroundWithBlock { (object:[PFObject]?, error:NSError?) -> Void in
if error == nil {
for object in object! {
if (object.objectForKey("latestImage") != nil)
{
self.lastPicArray.append(object.objectForKey("latestImage")!.valueForKey("image") as! PFFile)
self.profilePicArray.append(object.valueForKey("profilePicture") as! PFFile)
self.fullnameArray.append(object.valueForKey("firstname") as! String)
self.usernameArr.append(object.valueForKey("username") as! String)
}
}
self.collectionView.reloadData()
print(self.usernameArr)
print(self.lastPicArray)
}
}
}
You should be able to do this with just one query, by using the includeKey function of Parse Queries
Parse Queries
Take your 2nd query, and add something like this:
let imageQuery = PFQuery(className: "Posts")
imageQuery.whereKey("username", containedIn: self.usernameArray)
imageQuery.includeKey("fieldNameOfUserPointer")
Just change the 'fieldNameOfUserPointer' to the field name that references your _User object in the Posts entity.

Can't update Array within a function

Hey I'm working with Swift 2 and I'm trying to make a method that returns an array of strings of IDs downloaded from a database through a query. My problem is that within the function I cannot update my Array, meaning that I can access the downloaded information from the server but I cannot append it to my array for some reason. Or better, I can, but it doesn't really do anything. My array seems to stay empty.
func ATMsAroundMe(myLocation : PFGeoPoint) -> [String]{
var results = [String]()
let query = PFQuery(className: "ATMs")
query.whereKey("location", nearGeoPoint: myLocation, withinMiles: 5)
query.limit = 10
query.findObjectsInBackgroundWithBlock { (atms: [PFObject]?, error: NSError?) -> Void in
if (error == nil) {
for atm in atms! {
print(atm.objectId) //Works!
results.append(atm.objectId!) //Doesn't work
}
} else {
// Log details of the failure
}
}
print(results) //Prints "[]"
return results
}
So yeah if you have any suggestion or any idea on what am I doing wrong it'd be really helpful and appreciated if you could let me know.
Thanks.
The reason why results is not updated is because it is updated in another block scope. So updated values persists only in that block scope. To get the updated result you would need to use closures or __block in variable declaration in Objective-c.
which is quite nicely explained here here in BLOCKS VS CLOSURES
The issue here is that the call-
query.findObjectsInBackgroundWithBlock
This is an asynchronous call, and thus, your method simply returns as it does not wait for the results that will be returned by this asynchronous call. Thus you will need to think of an asynchronous API ATMsAroundMe in the form-
func ATMsAroundMe(myLocation : PFGeoPoint, completionHandler:(Bool,[String]?) ->Void){
let query = PFQuery(className: "ATMs")
query.whereKey("location", nearGeoPoint: myLocation, withinMiles: 5)
query.limit = 10
query.findObjectsInBackgroundWithBlock { (atms: [PFObject]?, error: NSError?) -> Void in
if (error == nil) {
for atm in atms! {
print(atm.objectId) //Works!
var results = [String]()
results.append(atm.objectId!)
completionHandler(true, results)
}
} else {
// Report the failure
completionHandler(false, nil)
}
}
}
You can now call this API like-
ATMsAroundMe(myLocation){(success :Bool, results:[String]?) in
if(success){
if let results = results {
//Process results
}
}
}
Synchronous solution:
func ATMsAroundMe(myLocation : PFGeoPoint) -> [String]{
var results = [String]()
let query = PFQuery(className: "ATMs")
query.whereKey("location", nearGeoPoint: myLocation, withinMiles: 5)
query.limit = 10
//Declare a semaphore to help us wait until the background task is completed.
let sem = dispatch_semaphore_create(0);
query.findObjectsInBackgroundWithBlock { (atms: [PFObject]?, error: NSError?) -> Void in
if (error == nil) {
for atm in atms! {
print(atm.objectId) //Works!
results.append(atm.objectId!)
dispatch_semaphore_signal(sem);
}
} else {
// Log details of the failure
dispatch_semaphore_signal(sem);
}
}
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
print(results) //Should print your results
return results
}
Note Be care full in calling this synchronous API from your main thread, it can potentially stall the main thread until the call returns like any other synchronous calls.

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