Update values on parse - ios

Is there a way to update a number on parse not just by replacing it or creating a new object id then deleting the old one but by actually incrementing the value from the stored value on parse and the new value being added to it. Ie.
On parse: Correct = 5 ; On app: Correct = 10 ; new value on parse: 15
This is my code but it just creates a new object id with the new values every time...
func saveScoresOnParse() {
scores["Right"] = rightAnswers
scores["Wrong"] = wrongAnswers
scores["Skipped"] = skippedQuestions
scores["User"] = PFUser.currentUser()
scores.saveInBackground()
}
On parse:
I want the values to be added up and as well as only have one objectid for each one.

let scoreQuery = PFQuery(className: "Scores")
scoreQuery.whereKey("user", equalTo: PFUser.currentUser()!)
scoreQuery.getFirstObjectInBackgroundWithBlock { (object, error) -> Void in
if error == nil {
if let scores = object {
scores["Right"] = rightAnswers
scores["Wrong"] = wrongAnswers
scores["Skipped"] = skippedQuestions
scores.saveInBackground()
}
}
}

Related

Unable to get PFUser field

I want to get the xp of the current user and display it in a label. The xp field of the user is equal to 252 but it is displaying 0. I can get other fields like email, username and password but can't get the value for the xp field. Code:
xpRequiredLabel.text = "\(PFUser.currentUser()!["XP"])/300 XP"
What am I doing wrong ?
This expression PFUser.currentUser()!["XP"] returns AnyObject?. You need to unwrap this optional and cast it to string. Try this:
let userXP = PFUser.currentUser()!["XP"] as! String
xpRequiredLabel.text = "\(userXP)/300 XP"
Or this (less error-prone):
if let userXP = PFUser.currentUser()?["XP"] as? String {
xpRequiredLabel.text = "\(userXP)/300 XP"
}
Update
It turns out that you have to fetch the object from the server before accessing new properties. So, your code should look like:
let user = PFUser.currentUser()!
user.fetch()
let userXP = ["XP"] as! Int // If you have "Number" as your column datatype
xpRequiredLabel.text = "\(userXP)/300 XP"
Note that fetch() will block the UI. You can also make this code async:
if let user = User.currentUser() {
user.fetchInBackgroundWithBlock({ (result, error) -> Void in
if let u = result as? PFUser {
if let userXP = u["XP"] as? Int {
self.xpRequiredLabel.text = "\(userXP)/300 XP"
}
}
})
}
those are Int values
252 / 300 = 0 when ints invovled
you can do something like
xpRequiredLabel.text = "Float((PFUser.currentUser()!["XP"]))/300.0 XP"

How to stop unwrapping nil values from Parse Array?

I'm having trouble getting my information back from a saved array in Parse. I'm saving the information like this.....
videoARY = [videoId, vidTitleText, vidDescription, vidIMG]
let videoSave = PFObject(className:"UserVideos")
videoSave["userObjectId"] = PFUser.currentUser()!.objectId
videoSave["vid\(saveValueLBL.text!)user"] = PFUser.currentUser()!.username
videoSave["vid\(saveValueLBL.text!)"] = videoARY
videoSave.saveInBackgroundWithBlock { (success, error ) -> Void in
if success == true
{
print("Success")
}
}
In a different viewcontroller I'm initializing an array like this....
var vid1array = [String]!()
and retrieving this way.......
let query = PFQuery(className: "UserVideos")
query.whereKey("userObjectId", equalTo: PFUser.currentUser()!.objectId!)
query.findObjectsInBackgroundWithBlock { (vid:Array?, error:NSError?) -> Void in
if !(error != nil)
{
for items in vid!
{
if let myfav1 = items["vid1"] as? NSArray
{
self.videoDescription1 = myfav1[2] as! String
self.videoTitle1 = myfav1[1] as! String
self.videoIMG1 = myfav1[3] as! String
print(self.videoDescription1)
print(self.videoIMG1)
print(self.videoTitle1)
self.vid1array.append(self.videoTitle1)
print(self.videoTitle1)
}
}
}
}
I'm printing a successful save and each element of the saved array. When I try to add one of those elements to vid1array the app crashes I get "Fatal error: unexpectedly found nil while unwrapping an Optional value". Please help.
* UPDATE *
When I print vid! I get this...
vid1 = (
"YiiqHq_kNnU",
"INFINITE \"Back\" Official MV",
"If you like this video, plz click \"LIKE\" and \"SUBSCRIBE\". INFINITE \"Back\" Official MV INFINITE Official Website : http://www.ifnt7.com INFINITE Official YouTube ...",
"https://i.ytimg.com/vi/YiiqHq_kNnU/default.jpg"
);
It's not nil but I don't see array brackets either, could that be it? I'm printing out the elements I just want to save them to my array so I can use them in my tableview later and the array is coming up nil.
* FOLLOW UP QUESTION *
Would it be advised to place this in viewWillAppear vs. I currently have it in viewDidLoad?

FMDB: NULL Values Are Retrieved as Empty Strings

I'm retrieving a customer record with FMDB and Swift using the (simplified) function below. When the optional value in the title column is NULLthe title member of the returned customer object is an empty string rather than nil, which is misleading. Can this be re-written such that NULL values are retrieved as nil? -- Ideally without testing for empty strings and setting nil explicitly (also wrong if the value is in fact an empty string)?
func getCustomerById(id: NSUUID) -> Customer? {
let db = FMDatabase(path: dbPath as String)
if db.open() {
let queryStatement = "SELECT * FROM Customers WHERE id = ?"
let result = db.executeQuery(queryStatement, withArgumentsInArray: [id.UUIDString])
while result.next() {
var customer = Customer();
customer.id = NSUUID(UUIDString: result.stringForColumn("customerId"))
customer.firstName = result.stringForColumn("firstName")
customer.lastName = result.stringForColumn("lastName")
customer.title = result.stringForColumn("title")
return customer
}
}
else {
println("Error: \(db.lastErrorMessage())")
}
return nil
}
The NULL values are returned as nil:
db.executeUpdate("create table foo (bar text)", withArgumentsInArray: nil)
db.executeUpdate("insert into foo (bar) values (?)", withArgumentsInArray: ["baz"])
db.executeUpdate("insert into foo (bar) values (?)", withArgumentsInArray: [NSNull()])
if let rs = db.executeQuery("select * from foo", withArgumentsInArray: nil) {
while rs.next() {
if let string = rs.stringForColumn("bar") {
println("bar = \(string)")
} else {
println("bar is null")
}
}
}
That outputs:
bar = bazbar is null
You might want to double check how the values were inserted. Specifically, were empty values added using NSNull? Or perhaps open the database in an external tool and verify that the columns are really NULL like you expected.

Parse via Swift with Complex Queries

Im having some difficulty with creating complex queries in Parse. What I'm looking to achieve is searching the _Users class and not returning myself as a result and if an Invite already exists in the Invites class, show Pending text rather than Add Button and if both myself and user have already accepted invite, dont show them at all.
I've achieved some of it but I'm not sure i'm doing it the most efficient way. For instance, I first query the _User class and find any users that match the search terms, then loop through and if the objectId == currentUser().objectId, skip that record. Then I run another query in that loop on each record to see if there are any pending invites, if so, i set a flag to true for Pending however i've done that search not in the background because i was having isses with the first query block finishing before the second and my flag not getting set first. So would I then do a third query to see if the field is accepted? Or is there a way to make this all one big query? My code is below for the searching:
func performSearchForText(text: String, completion: SearchComplete) {
state = .Loading
// Query Parse
var containsDisplayName = PFQuery(className:"_User")
containsDisplayName.whereKey("displayName", containsString: text)
var containsUsername = PFQuery(className: "_User")
containsUsername.whereKey("username", containsString: text)
var query = PFQuery.orQueryWithSubqueries([containsUsername, containsDisplayName])
query.findObjectsInBackgroundWithBlock {
(results: [AnyObject]?, error: NSError?) -> Void in
//self.state = .NotSearchedYet
var success = false
if error == nil {
// Found results
// Set Result state to either Results or NoResults
if let results = results {
//println(results)
if results.count == 0 {
self.state = .NoResults
} else {
// Read Results into UserSearchResult Array
// Dont show self in results
// If user has already accepted a request, dont show
// If they have already invited, show Pending instead of button
var userSearchResults = [UserSearchResult]()
var searchResult = UserSearchResult()
for result in results {
if result.objectId != PFUser.currentUser()?.objectId {
// Query invites table to see if they already are accepted with user
// or if a pending invite exists
// Set invite or accepted respectively
var invitedQuery = PFQuery(className: "Invites")
invitedQuery.whereKey("pending", equalTo: true)
invitedQuery.whereKey("inviteToUser", equalTo: result.objectId!!)
var invitedQueryResults = invitedQuery.findObjects()
if invitedQueryResults?.count > 0 {
self.pendingInvite = true
}
searchResult.displayName = result["displayName"] as! String
searchResult.emailAddress = result["username"] as! String
searchResult.inviteUserID = result.objectId!!
searchResult.invited = self.pendingInvite
searchResult.accepted = false
userSearchResults.append(searchResult)
}
}
if userSearchResults.count > 0 {
self.state = .Results(userSearchResults)
} else {
self.state = .NoResults
}
}
success = true
}
dispatch_async(dispatch_get_main_queue()) {
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
completion(success)
}
} else {
// Error, print it
println(error)
}
}
}
Unless there's a lot more to your invites table, I'd suggest removing it and adding an "invitePending" field to your user table.
Then you can just further refine your other queries by using wherekey("invitePending", equalTo: true).
Additionally, you can do something like this at the start so you don't have to check for currentUser in the query completion block:
containsDisplayName.whereKey("objectId", notEqualTo: PFUser.currentUser()!.objectId!)
But you definitely don't need the invites table if it isn't storing lots of other info. If you have to keep it as a separate table, using a pointer (for 1 to 1) or a PFRelation (for 1 to many) would save you the headache of that inner query.

Retrieving objects from Parse iOS

I am new to programming and am trying to develop an iOS app using Swift. However, I ran into a problem trying to retrieve previously saved information from Parse to use in my app.
To save the information I use the following code:
var user = PFUser.currentUser()
var number = self.phoneNumber.text as String
var insta = self.instagramUsername.text as String
var snapp = self.snapchatName.text as String
var twitter = self.twitterHandle.text as String
var bio = self.profileBio.text as String
var socialData = PFObject(className: "socialData")
socialData["phoneNumber"] = number
socialData["instagram"] = insta
socialData["snapchat"] = snapp
socialData["twitter"] = twitter
socialData["bio"] = bio
socialData["user"] = user
socialData.save()
This saves the information on Parse correctly but then when I try to retrieve the Instagram name or anything else specifically using a query I get an error. Here is how I have been trying to retrieve it:
var user = PFUser.currentUser()
var query = PFQuery(className:"socialData")
query.whereKey("user", equalTo: user)
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]!, error: NSError!) -> Void in
if error == nil {
// The find succeeded.
// Do something with the found objects
var socialData = PFObject(className: "socialData")
let instaUsername = socialData["instagram"] as String
println(instaUsername)
} else {
// Log details of the failure
NSLog("Error: %# %#", error, error.userInfo!)
}
}
The logs say fatal error: unexpectedly found nil while unwrapping an Optional value. After the app crashes. Yet, the information is correctly displayed on Parse. Any help is greatly appreciated, thanks!
Remove this line within your findObjectsInBackgroundWithBlock block entirely:
var socialData = PFObject(className: "socialData")
and replace this line:
let instaUsername = socialData["instagram"] as String
with this:
let instaUsername = objects[0]["instagram"] as String
so you're actually utilizing the object array retrieved from the query, accessing the first object which matches the current user, then getting the "instagram" key's stored value from that PFObject dictionary.

Resources