swift parse query not returning objects in ascending order - swift

My query aims to fetch the objects created by a certain number of users. The following are my codes:
var dates = [NSDate]()
var messages = [String]()
let getUsersQuery = PFQuery(className: "followings")
getUsersQuery.whereKey("follower", equalTo: PFUser.currentUser()!.objectId!)
getFollowedUsersQuery.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if let objects = objects {
for object in objects {
let followedUser = object["followedUser"] as! String
let getPostsQuery = PFQuery(className: "posts")
getPostsQuery.whereKey("userId", equalTo: followedUser)
getPostsQuery.orderByDescending("updatedAt")
getPostsQuery.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if let objects = objects {
for post in objects {
self.dates.append(post.createdAt as NSDate!)
self.messages.append(post["message"] as! String)
}
print(self.dates)
self.myTableView.reloadData()
})
}
}
}
})
Here's what come up when I print the dates in the log:
However, the content in my tableview is not in an ascending order. In fact the post created most recently is at the bottom of the tableview. One thing I noticed is that the posts of a particular user seems to be shown together. So all of one user's post would appear at the top of the tableview no matter another user has created the newest post. Any idea what is going wrong? thanks!

What you need is a nested query. The array you are trying to get is an of posts. So your query should be on the post table. (Also, your table should be called post not posts. Table names should always be singular. Each post is called a post not a posts.
let postsQuery = PFQuery(className: "post")
postsQuery.orderByDescending("updatedAt")
let usersQuery = PFQuery(className: "following")
usersQuery.whereKey("follower", equalTo:PFUser.currentUser()!.objectId!)
// this might need to change depending on what the followed user is called in the `followings` table.
postsQuery.whereKey("userId", matchesKey:"followed", inQuery:usersQuery)
getPostsQuery.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if let objects = objects {
for post in objects {
self.dates.append(post.createdAt as NSDate!)
self.messages.append(post["message"] as! String)
}
print(self.dates)
self.myTableView.reloadData()
})
Also, you are saving dates and messages in separate arrays. Create a struct or something that has a date and string and have a single array of Post objects.

Related

Parse display images in collectionView

I have a tableView with a CollectionView inside, in the TableView is the users details and I am trying to put the images belonging to the user in the collectionView.
So far I have everything working correctly apart from the images are not correct. Every image uploaded is displaying for each user, I need match the images to the user...I have been trying for a while now and have not found a solution so I'm hoping someone can help me here!
Here is my query for getting the user details and images:
func loadPosts() {
let followQuery = PFQuery(className: "Follows")
followQuery.whereKey("follower", equalTo: PFUser.currentUser()!.username!)
followQuery.findObjectsInBackgroundWithBlock ({ (objects:[PFObject]?, error:NSError?) -> Void in
if error == nil {
self.followArray.removeAll(keepCapacity: false)
for object in objects! {
self.followArray.append(object.valueForKey("following") as! String)
}
self.followArray.append(PFUser.currentUser()!.username!)
let query = PFQuery(className: "Posts")
query.whereKey("usernamee", containedIn: self.followArray)
query.addDescendingOrder("createdAt")
query.findObjectsInBackgroundWithBlock({ (objects:[PFObject]?, error:NSError?) -> Void in
if error == nil {
self.usernameArray.removeAll(keepCapacity: false)
self.imageArray.removeAll(keepCapacity: false)
self.uuidArray.removeAll(keepCapacity: false)
for object in objects! {
self.usernameArray.append(object.valueForKey("username") as! String)
self.imageArray.append(object.valueForKey("imageArray") as! PFFile)
self.uuidArray.append(object.valueForKey("uuid") as! String)
}
self.tableView.reloadData()
} else {
print(error!.localizedDescription)
}
})
} else {
print(error!.localizedDescription)
}
})
}
Best regards.
There are three issues I see in your code. First of all, in your Posts query, you use the whereKey() method: query.whereKey("usernamee", containedIn: self.followArray). username is spelled incorrectly, and this may be causing some issues. In addition, you remove all objects from your array every time you issue a query. There must be some way to add and remove data as necessary. (This only applies if you are refreshing data. If you have not added data to the arrays, there is no need to empty them.) Finally, you have a value called imageArray that is stored as a PFFile. An array would be multiple objects. It is hard to tell whether or not each user has multiple images or not. (For the solution, I assume that there is only one image per user. I would suggest changing the name of the key to be more precise. If this is not the case, write me a comment.)
Now, in regards to your actual problem, I believe that this is because you have only one array for all the images, and when you display it for each user, the entire array is used. In order to correct for this, I would suggest building a data structure in the following format: [(String, Image)], where String is the username and Image is file for the image. You can thus access the users and their images as an array of tuples.
To create the array of tuples, I would suggest: var usersAndImagesArray = [(username: String, imageFile: PFFile)]()
To add to the array:
for object in objects!{
usersAndImagesArray.append(object.valueForKey("username") as! String, object.valueForKey("image") as! PFFile)
}
To access the values when setting up the cells:
let username = usersAndImagesArray[index.row].username
let imageFile = usersAndImagesArray[index.row].imageFile

PFQuery categorizing results into multilevel arrays

I am trying to do a multiple PFQuery and storing the data properly.
My plan is to query for group members in my Group
I am looking for the key member. It gives me an array of objectIds. Then I want to query in my _User class for their picture with key firstImage. Displaying the data is not the problem, just getting it the right way.
The data needs to be stored considering it's groups. This is what i tried so far:
let fetchGroupsQuery = PFQuery(className: "Group")
fetchGroupsQuery.whereKey("member", equalTo: objectID!)
fetchGroupsQuery.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if error != nil
{
}
else {
if let objects = objects {
self.memberPhotos.removeAll()
for object in objects {
if let member = object["member"] as? [String]
{
// returns array of objectIds
for user in member
{
// returns single ids
self.groupIds.append(user)
let photoQuery = PFUser.query()
photoQuery?.whereKey("objectId", equalTo: user)
photoQuery?.findObjectsInBackgroundWithBlock({ (results, error) -> Void in
if error != nil {
// ....
}
else {
self.memberPhotos.removeAll()
if let results = results
{
for result in results
{
if result["firstImage"] != nil
{
self.memberPhotos.append(result["firstImage"])
}
}
}
}})
}
}
My idea was to store the data within an array of arrays.
So that I can later go into array[indexPath.row] to get the array I need to loop through in order to get the right group pictures.
Any ideas how to solve this?
I would use two separate queries function:
1) to get their ID's
2) for their pictures.
For the first query: what you need to change?
Your objectID array should be of type of NSMutableArray because in order to fetch their info you should use the constraint whereKey containedIn not whereKey equalTo
and for that constraint you should downcast your NSMutableArray to type AnyObject.
Also change the Type of your groupIds to NSMutableArray, then append your array.
For the Second query:
Since your groupIds Type is already NSMutableArray just cast it to AnyObject,then use the whereKey containedIn
Note: It would be better to download all images before appending your
array. So you have the option of using struct or class(easier to
group data to its owner) then create an array of that data
structure.Where it because easier to populate your UI.

The method does not enter for loop Parse Swift

I use parse to query current user's friend list and the friend request user and when user press each cell of the friend request, The app will add that friend back and delete the selected friend request so I query friend list and friend request and use "addedArray" as friend requests and "duplicate" as array of current user's friend list and use for loop to find the duplicate of friend list and friend request and delete that friend from addedArray so the current user will se the latest friend requests
Here's my code in swift
func queryAdded(){
let query = PFQuery(className: "Request")
let user = PFUser.currentUser()?.relationForKey("Friends")
let query2 = user?.query()
query.whereKey("To", equalTo: PFUser.currentUser()!)
query.findObjectsInBackgroundWithBlock {
(objects, error) -> Void in
if error == nil{
for object in objects! {
print("query")
let username = object.valueForKey("FromUsername") as! String
self.userCellAdded = username
self.addedArray.append(username)
print(username)
print(self.addedArray.count)
}
print("READY")
print(self.addedArray.count)
self.tableView.reloadData()
}
else{
/* dispatch_async(dispatch_get_main_queue()){
//reload the table view
query.cachePolicy = PFCachePolicy.NetworkElseCache
}*/
print("errorrrr")
}
}
query2!.findObjectsInBackgroundWithBlock{(objects,error) -> Void in
if error == nil {
for object in (objects)!{
if let username = object["username"] as? String {
self.duplicate.append(username)
print("duplicate")
print(username)
print("size")
print(self.duplicate.count)
}
}
}
}
for self.iIndex = 0 ; self.iIndex < self.addedArray.count ; ++self.iIndex {
for self.jIndex = 0 ; self.jIndex < self.duplicate.count ; ++self.jIndex {
print("in for loop")
if self.addedArray[self.iIndex] == self.duplicate[self.jIndex] {
self.addedArray.removeAtIndex(self.iIndex)
self.tableView.reloadData()
print("find")
}
}
}
}
The problem is The method queryAdded() does not run for loop for me and I don't understand why
The duplicate array and the addedArray have value and size but still it didn't go inside the for loop
Your problem is that your for loop is depending on the results of two asynchronous operations. What happens is that your app starts these two background queries and then immediately starts the for loop. Since there is no data yet from the queries, the for loop has no data to work on.
You can either solve this by creating a "pyramid hell" by nesting your operations (bad), or you can use a framework to achieve the same as Promises would provide for JavaScript (good).
Since you're using Parse, you have such a framework already; namely the Bolts Framework. You could then perform these operations sequentially using tasks (BFTask).
Example from the Bolts readme:
var query = PFQuery(className:"Student")
query.orderByDescending("gpa")
findAsync(query).continueWithSuccessBlock {
(task: BFTask!) -> BFTask in
let students = task.result() as NSArray
var valedictorian = students.objectAtIndex(0) as PFObject
valedictorian["valedictorian"] = true
return self.saveAsync(valedictorian)
}.continueWithSuccessBlock {
(task: BFTask!) -> BFTask in
var valedictorian = task.result() as PFObject
return self.findAsync(query)
}.continueWithSuccessBlock {
(task: BFTask!) -> BFTask in
let students = task.result() as NSArray
var salutatorian = students.objectAtIndex(1) as PFObject
salutatorian["salutatorian"] = true
return self.saveAsync(salutatorian)
}.continueWithSuccessBlock {
(task: BFTask!) -> AnyObject! in
// Everything is done!
return nil
}
You could then first prepare both your queries and then start the chain of tasks:
query1.findObjectsInBackground().continueWithSuccessBlock {
(task: BFTask!) -> BFTask in
var objects = task.result() as NSArray
for object in objects {
//collect your usernames
}
return query2.findObjectsInBackground()
}.continueWithSuccessBlock {
(task: BFTask!) -> AnyObject! in
var objects = task.result() as NSArray
for object in objects {
// collect your usernames from relation
}
// Call a function containing the for loop that is currently not running
return nil
}
The for loop is run
duplicate array and the addedArray have value and size - No they don't
findObjectsInBackgroundWithBlock runs the query in ... the background.
Therefore your program does the following:
start the first query
start the second query
run the for loop
the queries finish at some arbitrary point in time.
In particular when the program reaches point 3 the arrays do not contain anything, they are empty arrays, therefore the for-loop executes perfectly fine as it is supposed to be: it does nothing since there is nothing to loop over.
Solution:
Move the for loop into a function that you call after the first query and the second query finish.

Cannot Subscript A PFObject Error

I've attempted to solve this error, but I've had no luck in doing so. I'm getting the error: Cannot subscript a value of type '[PFObject]' with an index of type 'String' On this line of code: self.postDates.append(posts["createdAt"] as! String).
This is the portion of code I'm having trouble with:
var posts : [Post] = []
var postDates = [String]()
func loadData() {
var query = PFQuery(className: "Post")
query.orderByDescending("createdAt")
query.findObjectsInBackgroundWithBlock {(posts: [PFObject]?, error: NSError?)-> Void in
if error == nil {
if let posts = posts {
for post in posts {
self.postDates.append(posts["createdAt"] as! String)
}
self.tableView.reloadData()
}
} else {
// is an error
}
}
}
I'm trying to get the date and then display it every time the user create a new post utilizing Parse. Can anyone explain what is going on?
This is the tutorial I'm following along with: https://www.youtube.com/watch?v=L3VQ0TE_fjU
Because posts is an array of PFObject, how can you get an element inside from String? It's supposed to be an Int. It's just your typo, you already knew what you are doing. post is the PFObject you want.
for post in posts {
self.postDates.append(post["createdAt"] as! String)
}
You are trying to get (and add) the created at date of the PFObject,
instead you are getting the date of and array of PFObject (Which Posts is).
You should try to get the elements in the array, and get the date from the element instead of the array.
for post in posts{
postDates.append(post["createdAt"] as! String)
}

Parse includeKey works for an Array but not for a Pointer Object

I am currently working on an iOS app that connects to Parse and I am having difficulty in retrieving objects using the includeKey() function.
Lets say that I am adding a food item to my Food table:
var newFoodObject = PFObject(className: "Food")
//Here I am pointing to an existing object in my SubCategory table
newFoodObject["subCategory"] = PFObject(withoutDataWithClassName: "SubCategory", objectId: "oisii1pZSP")
newFoodObject.saveInBackgroundWithBlock { (success: Bool!, error:NSError!) -> Void in
println(success)
println(error)
}
Then lets say I want to retrieve that object:
var query = PFQuery(className: "Food")
query.includeKey("subCategory")
query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]!, error:NSError!) -> Void in
println(objects)
println(error)
}
For some reason when I do this the SubCategory object is not included in the query coming back unless I save the SubCategory in an array in my Food table.
Any thoughts why the includeKey() functions works with an array of pointers but not with a single pointer?