Struggling to append arrays across Parse classes - ios

Hi guys I'm trying to build a simple swipe app to like and dislike uploaded photos. I'm struggling with adding the likes/dislikes to Parse the way that I want them to. I've tried two ways so far:
adding the objectId of the posted image to the User who liked/disliked it but the problem is only one of the objectId's shows up in the array.
staying in the Parse class where the images are posted to (Post), add the userID of the liker/disliker to the image. This doesn't happen at all, new rows are created with new objectId's everytime an image is liked/disliked.
Ideally I want the users who have liked/disliked the photo in a single array so I can query this later. I don't have a great understanding of Parse, it's my first time using it so any help will be massively appreciated.
Here is the code I'm using when an image is swiped (adding to Post class):
if gesture.state == UIGestureRecognizerState.Ended {
var likedOrDisliked = ""
if label.center.x < 100 {
print("Dislike")
likedOrDisliked = "disliked"
} else if label.center.x > self.view.bounds.width - 100 {
print("Like")
likedOrDisliked = "liked"
}
if likedOrDisliked != ""{
var post = PFObject(className: "Post")
post.addUniqueObjectsFromArray([(PFUser.currentUser()?.objectId!)!], forKey: likedOrDisliked)
post.saveInBackground()
}
This is the snippet of when I try adding to User class:
PFUser.currentUser()?.addUniqueObjectsFromArray([displayedUserID], forKey: likedOrDisliked)
do {
try PFUser.currentUser()?.save()
} catch {
}
Here is what happens in the dashboard,
new rows created

What you wanted is to update the actual Post with the like/dislike user
Create a Post (This part you have not explained but i am show a simple assumption - pseuodo code)
var post = PFObject(class:"Post")
post["image"] = PFFile(image)
post.save()
Next you show the image on screen by getting the image from the post
When the user dislikes/likes
you add the current PFUser to the liked/disliked column and save back the object.
let arrayMut = NSMutableArray()
var array = NSArray()
if let arrayData = post.objectForKey("likedUser") as? NSArray {
array = arrayData
}
loop through now the array to find if current user is there.. if not find .. add current PFUser
arrayMut.addObject(PFUser.currentUser().objectId);
post.setObject(arrayMut, forKey: "likedUser")
post.save()

I've tried a lot of things and eventually something stuck, the desired effect was achieved through (added the current user to the liked or disliked fields) :
if likedOrDisliked != ""{
var post = PFQuery(className: "Post")
post.findObjectsInBackgroundWithBlock({ (objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
if let objects = objects {
for object in objects {
var objId = object["objectId"]
var query = PFQuery(className: "Post")
query.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if error == nil {
object.addUniqueObjectsFromArray([(PFUser.currentUser()?.objectId)!], forKey: likedOrDisliked)
object.saveInBackground()
}
})
}
}
}
})

Related

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(...)

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)

Entering Zip Code Switchies View Controller If Found In Parse (Swift and XCode)

So I'm trying to create a registration page with availability by Zip Code.
For instance, a user can only register if the service is available in their area (zip code).
So far I have a Text field for Zip Code and a button labeled "Check Availability".
I have a Parse Backend and I tested a connection to it using their setup guide and it works.
How can I go about adding Zip Codes to Parse and when a user types in that zip code that matches it'll open a new View Controller and they can register.
First method is to save zipCode that the user entered from the TextField:
var zipcodeFromUsers = customTextfield.text.toInt()
var savingObject = PFObject(className: "nameoftheclass")
savingObject["username"] = PFUser.currentUser()?.username
savingObject["zipcode"] = zipcodeFromUsers
savingObject.saveEventually { (success:Bool, error:NSError?) -> Void in
if error == nil
{
// data was saved
}
}
Second Method is to retrieve all the zipcodes from parse. So lets say that we want to query all 2344 zip codes
var needToFoundZipcode = 2344
var queryFromParse = PFQuery(className: "nameoftheclass")
queryFromParse.whereKey("zipcode", equalTo: needToFoundZipcode)
queryFromParse.findObjectsInBackgroundWithBlock { (objects:[AnyObject]?, error:NSError?) -> Void in
if error == nil
{
if let objects = objects as? [PFObject]
{
for SingleZipcode in objects
{
var singlezipcodeFound = SingleZipcode["zipcode"] as! Int
// now you could whatever you want
}
}
}
}

Parse: How can I update all rows with the same key value

How can I update all rows with the same key value in a Parse class?
As my code, I think Parse would cost a lot of my money if there are 100 objects per user.
Is there any other way to do that?
let username = PFUser.currentUser()?.username
let objectQuery = PFQuery(className: "FriendList")
objectQuery.whereKey("username", equalTo: username!)
objectQuery.findObjectsInBackgroundWithBlock { (friendList:[AnyObject]?, error:NSError?) -> Void in
if let friendList = friendList as? [PFObject] {
for myInfo in friendList {
myInfo["contact"] = contact
myInfo["portrait"] = PFFile(data: portraitNSData)
myInfo["company"] = company
myInfo["position"] = position
myInfo.save() // create a request?
}
}
}
Form array of needed objects and than just
PFObject.saveAllInBackground(objectsArray)
Parse has a functions to object see to save many objects (see documentation here)
PFObject.saveAllInBackground(array, block: {
(succeeded: Bool, error: NSError!) -> Void in
if (error != nil) {
println("Error saving: \(error)")
}
})
Unfortunately the documentation is not update to Swift but you can see a list of the function in objective-c
saveAll:
saveAll:error:
saveAllInBackground:
saveAllInBackground:block:
It would be best to create a CloudCode function. Then call that function from iOS. It's not difficult, see their documentation here: Parse.com

iOS/Swift/Parse: Local datastore via two view controllers query

Using the parse local data source
viewController1 - pinning the data to the local store.
let number = PFObject(className: "userNo")
number["phoneNumber"] = phoneNumber.text
number.pin()
viewController 2 - attempting to retrieve the data
var query = PFQuery(className:"userNo")
query.fromLocalDatastore()
query.getObjectInBackgroundWithId(string()) { //this line may be the issue?
(objects: PFObject!, error: NSError!) -> Void in
if error == nil {
var numberX = (PFObject()["phoneNumber"] as String)
self.phoneNumber.text = numberX
} else {
println("Error retreiving")
}
I am saving the users phone number in viewController1, via the parse local store method (*.pin()). This works fine.
In viewController2 I am attempting to show the user the locally stored data in a static cell 'detail'.
I have managed to make this work using PFUser.query and PFUser.Current user methods, but I don't think this is the right method to do this in. The parse doc actually state that we should retrieve data via PFObject, however how can we even do this without an ObjectId!?
viewController1
let number = PFUser.currentUser()
number["phoneNumber"] = phoneNumber.text
number.pin()
self.performSegueWithIdentifier("phoneDone", sender: self)
and then on viewController2
var query = PFUser.query()
query.fromLocalDatastore()
phoneNumber.text = (PFUser.currentUser()["phoneNumber"] as String)
If someone has a better solution, I would like to hear it. Thanks

Resources