Parse orQueryWithSubqueries not working correctly (swift) - ios

I'm working on implementing messaging functionality into my swift app, which runs on Parse, and querying for Conversation objects using the orQueryWithSubqueries method isn't working out like it should. Here is how I set up my query:
var user1 = PFQuery.getUserObjectWithId("someUserObjectID")
var user2 = PFQuery.getUserObjectWithId("otherUserObjectID")
var query1 = PFQuery(className: "Conversation")
query1.whereKey("user1", equalTo: user1!)
query1.whereKey("user2", equalTo: user2!)
var query2 = PFQuery(className: "Conversation")
query2.whereKey("user1", equalTo: user2!)
query1.whereKey("user2", equalTo: user1!)
var query = PFQuery.orQueryWithSubqueries([query1, query2])
query.includeKey("user1")
query.includeKey("user2")
query.getFirstObjectInBackgroundWithBlock {
(object: PFObject?, error: NSError?) -> Void in
if (error == nil) {
println("Found conversation object!")
} else {
var errorCode = error!.code
if (errorCode == 101) {
println("Did not find conversation object!")
} else {
println("Unknown error occurred.")
}
}
}
Essentially, in my scheme a Conversation object is a PFObject with two relevant fields: user1 and user2... which simply defines the two users that the conversation is between. The reason that I need this query to work is that when a user wants to start a new Conversation with a friend... I first check if that Conversation exists, and this query is how I do that. Because I don't know who started the Conversation, I need to check for both users in both user fields (my app automatically fills the user1 field with whoever started the conversation).
Anyway, here's where the weird behavior is happening. I have a conversation object in the cloud where:
user1 = someUser
user2 = otherUser
If I run my query the way that it is typed above... the Conversation object is not found by the orQueryWithSubqueries (even though query1 should find the Conversation object!). However, if I remove query2 from the orQueryWithSubqueries, or even just comment out either whereKey constraint in query2... the Conversation object is found. This is weird because query2 shouldn't even have anything to do with finding the Conversation object! Because query1 is the one that matches the object, and orQueryWithSubqueries are supposed to return object that match either query. Can any one shed any light on what I'm doing wrong?

In case anyone else encounters this issue, I never did find a way to get the orQueryWithSubqueries to work properly (may be a bug in the Parse iOS SDK), but this was my workaround:
var query = PFQuery(className: "Conversation")
query.whereKey("user1", containedIn: [user1, user2])
query.whereKey("user2", containedIn: [user1, user2])
query.getFirstObjectInBackgroundWithBlock {
(object: PFObject?, error: NSError?) -> Void in
if (error == nil) {
} else {
var errorCode = error!.code
if (errorCode == 101) {
} else {
// Handle error here.
}
}
}

Related

Back4App - Parse Query not returning any PFObects in iOS

In this scenario back4app initialised successfully, user login and signup also working, but when I am fetching all user list by using the below code, its returning blank array, without any error, there are three users in the User class in back4app. Can anyone help?
let query = PFQuery(className: "User")
query.findObjectsInBackground { (result, err) in
print(result)
print(err)
}
The User class was created by the Parse server. And that is the reason why is it a protected class. To reach these kinds of classes you need to use underscore character before the class name.
I mean don't use User for className, use it: _User
// let query = PFQuery(className: "User")
let query = PFQuery(className: "_User")
query.findObjectsInBackground { (result, err) in
print(result)
print(err)
}

How to check if PFRelation in Parse contains exactly same array in Swift

I am making a chat Application in ios through parse server. I have made a MessageRoom collection which has many to many relationship with users through PFRelation. Now i am struck . Whenever a user starts a new conversation , I add new entry in MessageRoom collection and use its id in the messages of that group. But when i want to fetch a previous conversation , let say a conversation between 5 users , how will i query the messageRoom which has exactly the same 5 users (not more or less) in its relation ?
This is the code i am using to create or get Message Room . It is not working correctly. What it does is instead of making a new messageRoom first time and fetching the same for latter user , it makes a new messaga room every time.
class func createOrGetMessageRoom(users:[PFUser], description:String)->PFObject{
var returnMessageRoom:PFObject = PFObject(className: PF_MESSAGE_ROOM_CLASS_NAME);
let users = users.sort(increasingIDs)
let query:PFQuery = PFQuery(className: PF_MESSAGE_ROOM_CLASS_NAME)
query.whereKey(PF_MESSAGE_ROOM_USERS, containsAllObjectsInArray : users)
query.findObjectsInBackgroundWithBlock{(objects, error )->Void in
if error == nil {
if objects?.count == 0 {
let messageRoom = PFObject(className: PF_MESSAGE_ROOM_CLASS_NAME)
messageRoom[PF_MESSAGE_ROOM_DESCRIPTION] = description
messageRoom[PF_MESSAGE_ROOM_LAST_USER] = PFUser.currentUser()
messageRoom[PF_MESSAGE_ROOM_LAST_MESSAGE] = ""
messageRoom[PF_MESSAGE_ROOM_COUNTER] = 0
messageRoom[PF_MESSAGE_ROOM_UPDATE_TIME] = NSDate()
let messageUsers = messageRoom.relationForKey(PF_MESSAGE_ROOM_USERS)
for user in users {
messageUsers.addObject(user)
}
messageRoom.saveInBackgroundWithBlock{(success,error)->Void in
if error == nil {
returnMessageRoom = messageRoom
}
}
}else{
returnMessageRoom = objects![0]
}
}else{
print("Message.createMessage Erorr");
print(error)
}
}
return returnMessageRoom
}
class func increasingIDs(user1: PFUser, user2: PFUser) -> Bool {
return user1.objectId < user2.objectId
}
I have also checked this application . What it does is whenever it starts a new chat , it concatenates objectIds of users in ascending order and use it as a groupId which is used for future references and used in chat messages as a foreign key.
It'll work in private chat and in group chat , but what happens if a user has started a group chat , and wants to add new users to this chat ?? If we simple change the group id by concatenating this users id , the previous messages which have used the old group id will no longer appear in this message group.
Also tell me if this approach of making groupID through concatenation is better or many to many relationship is better?
One problem with your function createOrGetMessageRoom is that findObjectsInBackgroundWithBlock is asynchronous, and you're not taking that into account.
What this means is that the findObjectsInBackgroundWithBlock function gets a response a long time after createOrGetMessageRoom has returned.
So, the PFObject you create on the first line of your function is always returned - your function does not wait for findObjectsInBackgroundWithBlock to return a MessageRoom.
To fix this, make your code take a callback like this:
class func createOrGetMessageRoom(users:[PFUser], description:String, callback: (PFObject? -> Void)) {
let users = users.sort(increasingIDs)
let query:PFQuery = PFQuery(className: PF_MESSAGE_ROOM_CLASS_NAME)
query.whereKey(PF_MESSAGE_ROOM_USERS, containsAllObjectsInArray : users)
query.findObjectsInBackgroundWithBlock{(objects, error )->Void in
if error == nil {
if objects?.count == 0 {
let messageRoom = PFObject(className: PF_MESSAGE_ROOM_CLASS_NAME)
messageRoom[PF_MESSAGE_ROOM_DESCRIPTION] = description
messageRoom[PF_MESSAGE_ROOM_LAST_USER] = PFUser.currentUser()
messageRoom[PF_MESSAGE_ROOM_LAST_MESSAGE] = ""
messageRoom[PF_MESSAGE_ROOM_COUNTER] = 0
messageRoom[PF_MESSAGE_ROOM_UPDATE_TIME] = NSDate()
let messageUsers = messageRoom.relationForKey(PF_MESSAGE_ROOM_USERS)
for user in users {
messageUsers.addObject(user)
}
messageRoom.saveInBackgroundWithBlock{(success,error)->Void in
if error == nil {
callback(messageRoom)
}
callback(nil)
}
}else{
callback(objects![0])
}
}else{
print("Message.createMessage Erorr");
print(error)
callback(nil)
}
}
}
Usage:
YourClass.createOrGetMessageRoom([], description: "description") { messageRoom in
// Do something...
}
The db schema in my mind, you should have 3 collections, _User, MessageRoom, and Message.
MessageRoom: users, roomName and other infos.
Message: room(pointer of MessageRoom), msg(content), sender(pointer of _User)
below are pseudo code
In your app, query all current user involved messageRooms.
var query = new Parse.Query("MessageRoom")
query.equalTo("users", currentUser);
//other constraint, roomName, createdAt, limit ...
query.find(...)
Pick a messageRoom object, and then use it to getMessages.
var query2 = new Parse.Query("Message");
query2.eqaulTo("room", roomObj);
query2.include("sender");
query2.descending("createdAt");
query2.find(...)

Parse database is replacing objects when it should create a new one

I have been trying to figure out this problem for a while without success. I have a button works like this:
Query the videosTable class in Parse and if the value for videoID (that I get from youtube's API) is not existent, create a new PFObject with rating 0. Otherwise, increase the rating by one.
The result however, is that the first new object I try to create every time I launch the app has no problem, but if I create another new object during the same run/session, the object previously created gets replaced by this new one.
The objectID stays the same, so I'm guessing this has something to do with the query not getting closed (or something similar), resulting in me modifying the previous object, instead of creating a new one.
Can someone shed some light in this?
#IBAction func recomBtn(sender: AnyObject) {
let query = PFQuery(className: "VideosInfo")
query.whereKey("Video_ID", equalTo: videoID)
query.findObjectsInBackgroundWithBlock {
(objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
if objects?.count == 0 {
print(objects)
videosTable["Video_ID"] = self.videoID
videosTable["Rating"] = 1
videosTable.saveInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
if (success) {
print("Rating Created")
} else {
print(error?.description)
}}
} else {
objects![0].incrementKey("Rating")
print("Rating Increased")
videosTable.saveInBackground()
}} else {
print(error?.description)
}
}
}
After much reviewing, I found out I was making a very dumb mistake! I declared the PFObject outside of the function! So when I was done with my button, the PFObject remained! (Since I don't really close the view after I go to another video, the PFObject remained as a property of the class until I closed the app).
Thanks anyway for your help!

How to create pointer to Parse PFUser from facebook ID

I'm doing a Facebook graph call to get friends of the user that are using my app. I get the facebook ID of the user's friends back from the graph call. Below is what I'm attempting to obtain from Parse with that ID, but's it's not getting all the users back, I believe since its an async call. How can I save an array of pointers of the user's fb friends that are using the app? Thanks in advance!!
graphConnection.addRequest(requestFriends, completionHandler: { (connection: FBSDKGraphRequestConnection!, result: AnyObject!, error: NSError!) -> Void in
if result.objectForKey("friends") != nil {
// parsing dictionary to get results
let firstDict = result.objectForKey("friends")
let dataArray = firstDict!.objectForKey("data")
let myFriendsUsingTheAppCount = dataArray!.count
print("\(myFriendsUsingTheAppCount)")
let friendsArray:NSMutableArray = []
for var i = 0; i < dataArray!.count; i++ {
let friend = dataArray![i]
let friendFbObjectID = friend.objectForKey("id")!
let query = PFUser.query()
query!.whereKey("facebookID", equalTo: friendFbObjectID)
query!.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if error == nil {
// this is where I was going to save this specific user as a pointer
} else {
// some error
}
})
// this is saving just the friend's name/fb id to an array, but I want an array of pointers to the PFUser
friendsArray.addObject(friend)
}
} else {
// fb friends is nil
print("FB friends came back nil")
}
})
This could be simplified into a single query by using whereKey:containedIn:
let facebookIDs = dataArray.map { $0.objectForKey("id")! }
let query = PFUser.query()!
query.whereKey("facebookID", containedIn: facebookIDs)
The query will now contain all users whose facebook id is in the array passed to the query.
(swift syntax may be incorrect did not double check)

How to preset Parse fields?

https://www.flickr.com/photos/dom497/16118311866/
Based off the picture linked above, here's the scenario: A user signs-up in my app and their data is added to Parse. How can I get ElginSupAccess to be preset to false?
I've tried using the following to update the field but apparently I cannot update an undefined field. (using Swift)
var query = PFQuery(className:"_User")
query.getObjectInBackgroundWithId(nameID) {
(update: PFObject!, error: NSError!) -> Void in
if error == nil {
update["elginSupAccess"] = false
update.saveEventually()
Dom,
In Parse the correct syntax of querying the user class is below, you don't query it by class name:
PFQuery *query = [PFUser query];
or for swift
var query = PFUser.query()
query.getObjectInBackgroundWithId("xWMyZEGZ") {
(update: PFObject!, error: NSError!) -> Void in
if error != nil {
NSLog("%#", error)
} else {
update["ElginSupAccess"] = false
}

Resources