Iterate through a custom Parse column in Swift app - ios

I want to know how I could store the entire custom column (the user Pointer<_User> column from a custom class) and put them all in an array variable so that I can see if a the user exists in that class or not. This is what I have:
Old Code
var objectUserIdArray = [String]()
let objectUserIdQuery : PFQuery = PFQuery(className: "Scores")
objectUserIdQuery.findObjectsInBackgroundWithBlock {
(objects : [PFObject]? , error : NSError?) -> Void in
var objectID = objects! as [PFObject]
for i in 0..<objectID.count {
objectUserIdArray.append(objectID[i].objectId!)
}
for _ in objectID {
print(objectUserIdArray)
}
New Code
func saveScoresOnParse() {
objectUserIdQuery.whereKey("User", equalTo: PFObject(withoutDataWithClassName: "_User", objectId: userID))
objectUserIdQuery.findObjectsInBackgroundWithBlock {
(objects : [PFObject]? , error : NSError?) -> Void in
if error == nil {
//var objectID = objects! as [PFObject]
/*for i in 0..<objectID.count {
self.objectUserIdArray.append( objectID[i].objectId! )
}*/
for _ in objects! {
print(objects)
}
// The score key has been incremented
for (var i = 0 ; i < self.objectUserIdArray.count ; i++) {
if self.userID != objects![i] {
print("New Scores")
print("R: \(self.rightAnswers)")
print("W: \(self.wrongAnswers)")
print("S: \(self.skippedQuestions)")
self.scores["User"] = PFUser.currentUser()
self.scores["Right"] = self.rightAnswers
self.scores["Wrong"] = self.wrongAnswers
self.scores["Skipped"] = self.skippedQuestions
self.scores.saveInBackground()
} else if self.userID == objects![i] {
print("Updated Scores")
self.scores.incrementKey("Right", byAmount: 1)
self.scores.incrementKey("Wrong", byAmount: 1)
self.scores.incrementKey("Skipped", byAmount: 1)
print("R: \(self.rightAnswers)")
print("W: \(self.wrongAnswers)")
print("S: \(self.skippedQuestions)")
self.scores.saveInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
if (success) {
// The score key has been incremented
} else {
// There was a problem, check error.description
}
}
} else {
print("Error")
}
}
} else {
print(error)
}
}
But it only stores the objectId column and not the Pointer<_User> column. I know this because when I print the stuff that is inside, it prints out the objectIds.
This is what happens, instead of just updating the current user's scores, it just makes new ones. I want the if statement to check if the user already exists in that column and if it does updates the scores and if it doesn't, make new ones. (The new code's if statement doesn't work, i have to bring it out for it to save...)

Your updated question make clearer what you are actually wanting to do;
Save or update a user's scores in your Parse Score object. To do this, there is no reason to retrieve any object Ids or loop through any results. More often than not you don't do use Object Ids explicitly when using Parse; you can simply pass the object itself with Parse working out the references for you.
I am not sure how you exactly want to change the scores; in your code above you increment in one case but set the scores explicitly in another, but the code below shows the general approach.
If you are frequently or repeatedly going to update a score record then you could make your code more efficient by holding a reference to the Scores object in a property after you find it the first time and simply update & save it subsequently.
func saveScoresOnParse() {
if let currentUser=PFUser.currentUser() {
let scoreQuery= PFQuery(className: "Scores")
scoreQuery.whereKey("User",equalTo:currentUser)
scoreQuery.getFirstObjectInBackgroundWithBlock {
(object : PFObject? , error : NSError?) -> Void in
if error == nil {
var scoreObject=object ?? PFObject.objectWithClassName("Scores")
if (scoreObject["User"]==nil) {
scoreObject["User"]=currentUser
}
scoreObject["Right"]=self.rightAnswers
scoreObject.saveInBackground()
} else {
print(error)
}
}
} else {
print("No current user!")
}
}

Related

how to properly check for an existing object in parse with swift

I've made a function that creates a "favorite" object back in parse with the tap of a favorite button on the UI :
//MARK: Create the favorite object
func createFavorite(){
let currentUser = PFUser.currentUser()
let currentBook = PFObject(withoutDataWithClassName: "Books", objectId: objectIdSelected)
let favorite = PFObject(className:"Favorites")
favorite["user"] = currentUser
favorite["books"] = currentBook
favorite.saveInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
if (success) {
// success
} else {
// There was a problem, check error.description
}
}
}
Now I am attempting to create a query that checks to see if a favorite object with those exact properties exists using the following logic, I've placed it in the viewDidLoad of a VC that shows a specific book:
//MARK: Check if there is a favorite object available
func isFavoritedByUser(){
let currentUser = PFUser.currentUser()
let currentBook = PFObject(withoutDataWithClassName: "Books", objectId: objectIdSelected)
let query = PFQuery(className:"Favorites")
query.whereKey("user", equalTo: currentUser!)
query.whereKey("books", equalTo: currentBook)
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if objects != nil {
print("this book was favorited by this user")
} else if objects == nil {
print("this book WAS NOT favorited by this user")
} else if error == nil {
// succeed
} else if error != nil {
// There was a problem, check error.description
}
}
}
However, even if no favorite object with those properties exists back in my parse class, it still prints "this book was favorited by this user".. what am i missing here?
The list existing is not proof. That simply proves that some kinda of error weren't observed.
You should check that there was no error first, then you should check that the list of objects has one item in it.
If the list has more than one item then you're creating duplicates and you should fix that...
What you're getting returned is an array, as long as there isn't an error. So, rather than checking for nil, you should be checking the content of the returned array (objects) to see if there is anything in there.
In Parse's guide, you can see what they recommend for this function call:
https://parse.com/docs/ios/guide#queries
Additionally, Parse recommends considering getFirstObjectInBackground instead if this query only ever needs to return a single object, so that might be something to consider as an alternative.
So the problem was i was checking to see if object was nil rather then checking for the count, here is the correct code:
//MARK: Check if there is a favorite object available
func isFavoritedByUser(){
let currentUser = PFUser.currentUser()
let currentBook = PFObject(withoutDataWithClassName: "Books", objectId: objectIdSelected)
let query = PFQuery(className:"Favorites")
query.whereKey("user", equalTo: currentUser!)
query.whereKey("books", equalTo: currentBook)
query.findObjectsInBackgroundWithBlock { (object, error) -> Void in
if error == nil {
if object?.count > 0{
print("this book was favorited by this user")
} else {
print("this book WAS NOT favorited by this user")
}
} else {
}
}
}
For iOS, you can just check the dictionary on an object like so:
rideParse["driver"] == nil
If it exists, then you will get the pointer. If it doesn't/you did not query to include the pointer, then it will just return nil.

Swift can't update Parse object

I have a problem and I could really use some help..
I have the method below and everything works fine, until line 907.. When it comes for the object3.saveInBackgroundWithBlock, it does nothing.. Not even errors! It never saves the object and it never goes inside the block..
Any idea why?
func addUserToThoseIFollow(sender: UIButton) {
//self.navigationItem.rightBarButtonItem?.enabled = false
sender.enabled = false
let userQuery = PFQuery(className: "_User")
let userQuery2 = PFQuery(className: "_User")
userQuery.getObjectInBackgroundWithId(PFUser.currentUser().objectId) { (object: PFObject!, error: NSError!) -> Void in
if error == nil {
// If I already follow some users, make
// an array with them, add the user I
// want to follow and save. Else,
// just save an array, with that one user.
if object["following"] != nil {
var thoseIFollow = object["following"] as! [String]
thoseIFollow.append(self.userID!)
object["following"] = thoseIFollow
}
else {
var myUsers = [String]()
myUsers.append(self.userID!)
object["following"] = myUsers
}
object.saveInBackgroundWithBlock({ (ok: Bool, error2: NSError!) -> Void in
if error2 == nil {
self.followButton.setTitle("Unfollow", forState: .Normal)
self.followButton.backgroundColor = UIColor(red: 1, green: 0, blue: 0, alpha: 0.7)
sender.enabled = true
self.doIFollow = true
}
})
}
}
// Add me to his followers
userQuery2.getObjectInBackgroundWithId(self.userID) { (object3: PFObject!, error3: NSError!) -> Void in
if error3 == nil {
// If the user I just followed, has already followers, make
// an array with them and add the current user to
// them. Else, just save an array, with the current user.
if object3["followers"] != nil {
var hisFollowers = object3["followers"] as! [String]
hisFollowers.append(PFUser.currentUser().objectId)
object3["followers"] = hisFollowers
/* Line 907 */ object3.saveInBackgroundWithBlock({ (ok7: Bool, error7: NSError?) -> Void in // Line 907
if error7 == nil {
print("ok")
}
else {
print(error7)
}
})
}
else {
var hisFollowers = [String]()
hisFollowers.append(PFUser.currentUser().objectId)
object3["followers"] = hisFollowers
object3.saveInBackgroundWithBlock( { (ok5: Bool, error7: NSError!) -> Void in
print("otinanai")
if error7 != nil {
print(error7.localizedDescription)
}
})
}
}
}
}
Attempt #1
What PFUser.currentUser().objectId return ? If it returns nil so it doesn't work.
Attempt #2
Available Parse Types
So far we've used values with type NSString, NSNumber, and PFObject. Parse also supports NSDate, and NSNull.
You can nest NSDictionary and NSArray objects to store more structured data within a single PFObject.
Try to use var hisFollowers = [NSString]() instead of var hisFollowers = [String]()
self.userID
Where exactly is this coming from?
Did your check, whether it is an optional?
Comment out the first query and see if it works.
Each Parse object can only have one background thread running for it at a time. Say you save an object, then immediately in the next line (not inside of its call back), edit it and then save it again. The second save will not be called, since the first save is still running. You don't even get an error. You get zero notification whatsoever that this call didn't happen.
My guess is that you have objects being saved inside both the first query and the second query, and because of that, the second query's save is being skipped.
The solution would be to stick the second query inside of the first's callback.
I think that there's a PromiseKit library you can download that adds javascript functionality in iOS, making it more similar to how you'd chain these calls in cloud code, but I haven't used it.

Parse not updating the core data Swift

I am trying to make changes to the data already stored in the core data in Parse. But it is not making the necessary changes. And I looked at the documentation for parse regarding objects here: https://parse.com/docs/ios/guide#objects. And it seems that I am doing exactly what the document is telling me to do. Maybe I am missing something? Here is my code:
import UIKit
import Parse
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var products = PFObject(className:"Products")
products["name"] = "ice cream"
products["description"] = "Strawberry"
products["price"] = 4.99
products.saveInBackgroundWithBlock {
(success, error) -> Void in
if (success) {
println("Object saved with id \(products.objectId)")
} else {
println("Not successful!")
print(error)
}
}
//ignore the code below this line for now please :)
var query = PFQuery(className: "Products")
query.getObjectInBackgroundWithId("7lmnxHxibK", block: { (object: PFObject?, error) -> Void in
if error != nil {
print(error)
} else if let product = object {
print("YO")
product["description"] = "Rocky Road"
product["price"] = 5.99
products.saveInBackground()
}
})
}
}
So the code above created an object with the ID 7lmnxHxibK. The description being Strawberry, the name being ice cream, and the price being 4.99. Which worked as it should. So now as an attempt to change the attributes in the object with the ID 7lmnxHxibK, I wrote the following code:
var query = PFQuery(className: "Products")
query.getObjectInBackgroundWithId("7lmnxHxibK", block: { (object: PFObject?, error) -> Void in
if error != nil {
print(error)
} else if let product = object {
print("YO")
product["description"] = "Rocky Road"
product["price"] = 5.99
products.saveInBackground()
}
})
This code should make the necessary changes to the object with the id 7lmnxHxibK. But rather than making the necessary changes to the object's attributes, it is creating a new object with it's description, name, and price all being (undefined). Anybody have a solution to fix this?
You have to change products.saveInBackground() to product.saveInBackground(). Also by the time you call your second query parse may not be done saving the object for the first time.

Retrieve values out of PFObject

I have a save button to save Score and Playername to GameScore in Parse. When I load the GameScore from Parse I want to set the value that i loaded to the variable "score". This don't work, can anyone please tell me what i am doing wrong?
Thanks
Exapmle: let score = gameScore["score"] as Int
// Load button tapped
#IBAction func loadButtonTapped(sender: UIButton) {
var query = PFQuery(className:"GameScore")
query.getObjectInBackgroundWithId("F1efANYzOE") {
(gameScore: PFObject?, error: NSError?) -> Void in
if error == nil && gameScore != nil {
println(gameScore)
let score = gameScore["score"] as Int
} else {
println(error)
}
}
}
}
Try this following code for retrieving specific data from specific columns. You have to enter your object name, Object ID and column name in the below code and run it. It will work.
let query = PFQuery(className:"Your Object name")
query.getObjectInBackgroundWithId("Your Object ID") {
(gameScore: PFObject?, error: NSError?) -> Void in
if error == nil {
print(gameScore!.objectForKey("your column name") as! String)
} else {
print(error)
}
}

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 {
...
}

Resources