Swift + Parse.com : how to fetch user relation using a join table - ios

I have an app like instagram where i have to make relationship between users.
For that i decided to use the join table method.
It'' s just a table named "Activity" where i created rows "fromUser", "toUser" and a row for the activity type "type" which in this case is set to "followingAction".
Here how i set up the table :
var activity = PFObject(className:"Activity")
let currentUser = PFUser.currentUser()
let follow : String = "followingAction"
activity.setObject(currentUser, forKey: "fromUser")
activity.setObject(user, forKey: "toUser") // user is a another PFUser in my app
activity.setObject(follow, forKey: "type")
activity.saveEventually()
Ok, now i want to fetch all users that i m currently following (currentUser) and display them in a tableView
Following the doc https://parse.com/docs/relations_guide#manytomany-jointables
i made this but it only give me an array of users which only contain the user objectId, i can't have the email, name which are set up in the regular User table :
func loadUser () {
followingUserList.removeAllObjects()
let findUserObjectId = PFQuery(className: "Activity")
findUserObjectId.whereKey("fromUser", equalTo: userPassed)
findUserObjectId.whereKey("type", equalTo: "followingAction")
findUserObjectId.findObjectsInBackgroundWithBlock { (objects:[AnyObject]!, error:NSError!) -> Void in
if error == nil {
// The find succeeded.
println("succesfully loaded the fromUser in Activity class")
// Do something with the found objects
for object in objects {
let user : PFUser = object["toUser"] as PFUser
self.followingUserList.addObject(user)
println("User added to following user list : \(user)")
println("followingUserlist = \(self.followingUserList)")
self.tableView.reloadData()
} } else {
// Log details of the failure
println("error loadind user ")
println(error)
}
}
}
if i print the followingUserList which is the array where i put the fetched users, here is what i have :
followingUserlist = (
"<PFUser: 0x7d9b93c0, objectId: DQJihBpW5E, localId: (null)> {\n}"
)
When i make a regular query (PFUser.query()) for the regular User table, i have more detail :
ex :
<PFUser: 0x7db5af80, objectId: niwftRrB5x, localId: (null)> {
backgroundImage = "<PFFile: 0x7db5f080>";
email = "kiki#kiki.com";
emailVerified = 0;
profileImage = "<PFFile: 0x7db62910>";
username = kiki;
}
Here we can see i have the complete PFUser with it email, username etc etc.
Because the "fromRow" in my "Activity" table is a pointer to my regular User table, why the fetched result in my loadUser() method isn't complete ?

Ok i finally found the solution, i just used the getObjectInBackgroundWithId method like this :
func loadUser () {
followingUserList.removeAllObjects()
let findUserObjectId = PFQuery(className: "Activity")
findUserObjectId.whereKey("fromUser", equalTo: userPassed)
findUserObjectId.whereKey("type", equalTo: "followingAction")
findUserObjectId.findObjectsInBackgroundWithBlock { (objects:[AnyObject]!, error:NSError!) -> Void in
if error == nil {
// The find succeeded.
println("succesfully loaded the fromUser in Activity class")
// Do something with the found objects
for object in objects {
let user : PFUser = object["toUser"] as PFUser
let queryUsers = PFUser.query()
queryUsers.getObjectInBackgroundWithId(user.objectId, block: { (userGet :PFObject!,error : NSError!) -> Void in
self.followingUserList.addObject(userGet)
self.tableView.reloadData()
})
} } else {
// Log details of the failure
println("error loadind user ")
println(error)
}
}
}

Related

How to use the PFQuery for PFQueryTableViewController to retrieve an array of PFObjects?

I have a PFUser with a Skills column.
Typically I can use user.skills (or user["skills"]) to get the skills objects of the user.
But to use these with PFQueryTableViewController I need to construct some sort of PFQuery when overriding queryForTable to get the list of skills for a user.
How do I build this query? All it does is return the skills for a given user.
fetchSkills(){
var userQuery: PFQuery = PFUser.query()!
userQuery.wherekey("username", equalTo: (currentUser?.username)!)
userQuery.findObjectsInBackgroundWithBlock({
(users, error) -> Void in
var skills = users!
if error == nil {
if skills.count >= 1 {
for i in 0...users!.count-1 {
self.skillTypes.append(skills[i].valueForKey("UserSkills") as! String)
}
}
self.usersArray = users as! [PFUser]
self.tableView.reloadData()
} else {
print(error)
}
})
}

Swift Parse - local datastore and displaying objects in a tableview

I am building and app that saves an object in the local datastore with parse. I then run a query to retrieve the objects that are in the local datastore and it is working fine. however, I would like to grab the object, and the contents in it, and set some labels in a table view cell based on the items that are stored in the parse local data store object. for example, i make an object with attributes like "objectID", "name", "date", "location". what i'd like to do is to have a table view on the home screen that displays the name, date, location ...etc. of each item that was saved in local datastore in labels in each cell.
i know that im saving it correctly:
// parse location object
let parseLighthouse = PFObject(className: "ParseLighthouse")
parseLighthouse.setObject(PFUser.currentUser()!, forKey: "User")
parseLighthouse["Name"] = self.placeTitle.text
parseLighthouse["Note"] = self.placeNote.text
parseLighthouse["Locality"] = self.placeDisplay.text!
parseLighthouse["Latt"] = self.map.region.center.latitude
parseLighthouse["Longi"] = self.map.region.center.longitude
parseLighthouse["LattDelta"] = 0.5
parseLighthouse["LongiDelta"] = 0.5
parseLighthouse["Date"] = dateInFormat
parseLighthouse.pinInBackground()
parseLighthouse.saveInBackgroundWithBlock { (success: Bool, error: NSError?) -> Void in
println("Object has been saved. ID = \(parseLighthouse.objectId)")
}
and when i run the query, im able to access the attributes by running println(object.objectForKey("Name"))
func performQuery() {
let query = PFQuery(className: "ParseLighthouse")
query.fromLocalDatastore()
query.whereKey("User", equalTo: PFUser.currentUser()!)
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if error == nil {
// The find succeeded.
println("Successfully retrieved \(objects!.count) lighthouses.")
// Do something with the found objects
if let light = objects as? [PFObject] {
for object in light {
println(object.objectId)
println(object.objectForKey("Name"))
}
}
} else {
// Log details of the failure
println("Error: \(error!) \(error!.userInfo!)")
}
}
because when running the query, i get back the object id and name as expected.
Successfully retrieved 2 lighthouses.
Optional("A3OROVAMIj")
Optional(happy)
Optional("bbyqPZDg8W")
Optional(date test)
what I would like to do is grab the name field within the parse object local data store, and that be the name of the label on a cell in a table view controller.
i dont know how to access that info from the object, and set the label correctly.
does anyone know how this is possible?
It's always a good idea to avoid pointer lol ... so why not saving the userid or username with the specific object..
so change this line:
parseLighthouse.setObject(PFUser.currentUser()!, forKey: "User")
TO
parseLighthouse["username"] = PFUser.currentUser().username
Answer
NOW let's create a struct that contains the objectID and the Name outside of your Controller Class.
struct Data
{
var Name:String!
var id:String!
}
then inside of the Controller class, declare the following line of code globally
var ArrayToPopulateCells = [Data]()
Then your query function will look like :
func performQuery() {
let query = PFQuery(className: "ParseLighthouse")
query.fromLocalDatastore()
query.whereKey("User", equalTo: PFUser.currentUser()!)
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if error == nil {
// The find succeeded.
print("Successfully retrieved \(objects!.count) lighthouses.")
// Do something with the found objects
if let light = objects as? [PFObject] {
for object in light {
print(object.objectId)
print(object.objectForKey("Name"))
var singleData = Data()
singleData.id = object.objectId
singleData.Name = object["Name"] as! String
self.ArrayToPopulateCells.append(singleData)
}
}
} else {
// Log details of the failure
print("Error: \(error!) \(error!.userInfo)")
}
}
In the tableView numberOfRowinSection()
return ArrayToPopulateCells.count
In the cellForRowAtIndexPath()
var data = ArrayToPopulateCells[indexPath.row]
cell.textlabel.text = data.objectID
cell.detailLabel.text = data.Name
VOila that should be it

Retrieve the userInfo using Pointer. Parse

I have a class called Posts in which i've postedBy column where i am saving the PFUser.currentUser() (pointer). so i want to retrieve the username, profile picture and stuff from the _User class using postedBy in the Posts class. What is the shortest and efficient way to achieve this? i am not much familiar with relation queries.
I believe that instead of saving the user pointer, you should save the user's username then it comes easier for you to retrieve everything.
var query = PFQuery(className:"Posts")
var username = PFUser.currentUser()?.username
query.whereKey("username", equalTo: username!)
query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]?, error:NSError?) -> Void in
if error == nil
{
if let objects = objects as? [PFObject]
{
for one in objects {
var pictureImage = one["theFile"] as! PFFile
pictureImage.getDataInBackgroundWithBlock({ (dataToget:NSData?, error:NSError?) -> Void in
if error == nil {
if let Image = UIImage(data: dataToget!){
// then you have the image
// save the image to array
// reload the tableview
}
}
})
}
}
}
}

Save current user's message list in parse with swift

I use parse for my app. I want to let user able to type messages that they want to send via textField and save it to that user's messages column in parse with PFRelation via save button in view controller and the messages will be saved as an array and show it in tableView.
The problem is I don't know how to add text in textfield to an array and save it to parse.
Any help is appreciated and let me know if you need any additional information!
UPDATE:
These are screenshots of my parse's class "User"
This is my current user's friend list inside "Friends" column
I've not yet create Messages column because when run relationForKey code in Xcode it will automatically create for me
UPDATE 2:
This is my code:
#IBAction func addMessage(sender: AnyObject) {
var newMessage = addMessageText.text
let message = PFObject(className: "Messages")
var query = PFQuery(className: "Messages")
message["messageTextColumn"] = newMessage
message.saveInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
if (success) {
println("added to Message Class")
query.whereKey("messageTextColumn", equalTo: newMessage)
query.getFirstObjectInBackgroundWithBlock{(object:PFObject!, error: NSError!) -> Void in
if error == nil {
let relation = PFUser.currentUser().relationForKey("Messages")
var addMessageWithObject = object
if addMessageWithObject != nil {
relation.addObject(addMessageWithObject)
println("Added with getFirstObject")
}
else{
println("Error Added with getFirstObject")
}
}
}
} else {
println("added to Message class Error")
}
}
}
I save new message to the array first and then I save it with saveInBackgroundWithBlock.. and inside I query that message to add it to relation.
The messages that I've added appear on Messages class table but not in that user's relation but it shows log
"added to Message Class" and "Added with getFirstObject"
Which means that my code execute exactly like it should be. Probably about the method?
UPDATE 3 this is the object println
<Messages: 0x7fd4484f75f0, objectId: LFXoSaHfQl, localId: (null)> {
ACL = "<PFACL: 0x7fd4484d2e70>";
messageTextColumn = 9;
}
UPDATE 4
this is my code
#IBAction func addMessage(sender: AnyObject) {
var newMessage = addMessageText.text
let message = PFObject(className: "Messages")
var user = PFUser.currentUser()
var query = PFQuery(className: "Messages")
message["messageTextColumn"] = newMessage
message.saveInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
if (success) {
println("added to Message Class")
query.getFirstObjectInBackgroundWithBlock{(object:PFObject!, error: NSError!) -> Void in
if error == nil {
var addMessageWithObject = object
if addMessageWithObject != nil {
user.saveInBackground()
println("Added with getFirstObject")
}
else{
println("Error Added with getFirstObject")
}
}
}
}
}
}
user column is (undefined) as in screenshot here
and the error log can't add non pointer to relation is back
how do I fix this? Thanks!
Here's what you do:
Manually create your Message table on Parse
Add a messages column to your user table of type Relation with Target Class as your Message table.
In your code, in your buttons trigger:
// Get the message text from your textField
let messageText = textField.text
// Create your new Message object
let newMessage = PFObject(className: "Message")
// ... Add your data to your new message object
newMessage["messageTextColumn"] = messageText
newMessage.saveInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
if (success) {
// Add the new message to the user's relation
let relation = yourUser.relationForKey("messagesColumnName")
relation.addObject(newMessage)
// Save the user object
yourUser.saveInBackground()
} else {
// There was a problem, check error.description
}
}
Here's a link to Parse's Relation reference.
UPDATE:
Missing code to save your user object.
query.getFirstObjectInBackgroundWithBlock{(object:PFObject!, error: NSError!) -> Void in
if error == nil {
let relation = PFUser.currentUser().relationForKey("Messages")
var addMessageWithObject = object
if addMessageWithObject != nil {
relation.addObject(addMessageWithObject)
PFUser.currentUser().saveInBackground()
println("Added with getFirstObject")
}
else{
println("Error Added with getFirstObject")
}
}
}
UPDATE 2:
Messages without PFRelation:
Add a column (let's say user) of type Pointer with Target Class as _User to the Messages table to identify each message by their user.
Saving new messages: Save the new message object like above (just without adding the relation and it'e related code):
#IBAction func addMessage(sender: AnyObject) {
var newMessage = addMessageText.text
let message = PFObject(className: "Messages")
message["messageTextColumn"] = newMessage
message["user"] = PFUser.currentUser()
message.saveInBackgroundWithBlock {(success: Bool, error: NSError?) -> Void in
if (success) {
println("added to Message Class")
} else {
// Error saving message
}
}
}
Querying the messages for a user: You can query using the current user as a constraint so no matter which device a particular switches to, he/she will get only his messages.
var query = PFQuery(className:"Messages")
query.whereKey("user", equalTo:PFUser.currentUser())
query.findObjectsInBackgroundWithBlock {
...
}

Parse Query Profile Data for User Profile - Swift

I want to query the user data based on the profile you are on in my app. As of now my query just gets all the posts not just the user that the profile belongs too.
"Drives" is the class name of the user posts.
post.removeAll(keepCapacity: false)
var findTimelineData:PFQuery = PFQuery(className:"Drives")
findTimelineData.findObjectsInBackgroundWithBlock
{
(objects:[AnyObject]! , error:NSError!) -> Void in
if error == nil
{
self.post = objects.reverse() as [PFObject]
self.table.reloadData()
}
}
post.removeAll(keepCapacity: false)
var findTimelineData:PFQuery = PFQuery(className:"Drives")
//Add the next line
findTimelineData.whereKey("YOUR_COLUMN_NAME_WHERE_THE_USERS_ARE_STORED", equalTo: "THE_NAME_OF_THE_USER")
findTimelineData.findObjectsInBackgroundWithBlock
{
(objects:[AnyObject]! , error:NSError!) -> Void in
if error == nil
{
self.post = objects.reverse() as [PFObject]
self.table.reloadData()
}
}
Or instead you can choose any whereKey... function, listed as here: https://parse.com/docs/ios/api/Classes/PFQuery.html#//api/name/whereKey:equalTo:
UPDATED:
If you query a pointer field, then the whereKey is modified a bit, you have to use relational queries:
let userNameQuery = PFQuery(className: "THE_CLASSNAME_WHERE_THE_USERS_ARE_STORED")
userNameQuery.whereKey("YOUR_COLUMN_NAME_WHERE_THE_NAME_OF_THE_USERS_ARE_STORED", equalTo: "THE_NAME_OF_THE_USER")
let findTimelineData:PFQuery = PFQuery(className:"Drives")
findTimelineData.whereKey("POINTER_COLUMN_OF_USER", matchesQuery: userNameQuery)

Resources