SWIFT: Parse data is deleted when UIButton Action is performed - ios

Here are the steps that I'm taking -
Step 1: I enter values in the following fields, and they get saved in the Parse table. Works perfect.
Step 2: I access this screen again, and click 'Submit' again without editing the fields, and the Parse table row is deleted.
I am trying to figure out why this is happening. The desired functionality is that when the user loads this screen for the first time, the fields are blank. Once this screen is loaded thereafter, the existing values are displaying the fields, and if the user edits the fields and clicks 'Submit', the values will chance.
This is my code for this particular view controller -
import UIKit
import Parse
class RSVPViewController: UIViewController {
/*----------- ~ OUTLETS AND ACTIONS ~ -----------*/
//View Controller Title Bar Outlets
#IBOutlet weak var usernameTitle: UIBarButtonItem!
#IBOutlet weak var menuTitle: UIBarButtonItem!
//View Controller Body Outlets
#IBOutlet weak var label1: UILabel!
#IBOutlet weak var label2: UILabel!
#IBOutlet weak var label3: UILabel!
#IBOutlet weak var label4: UILabel!
#IBOutlet weak var label1Field: UITextField!
#IBOutlet weak var label2Field: UITextField!
#IBOutlet weak var label3Field: UITextField!
#IBOutlet weak var label4Field: UITextField!
//View Controller Body Actions
#IBAction func submitButton(sender: AnyObject) {
let query = PFQuery(className:"RSVPData")
query.whereKey("username", equalTo: (PFUser.currentUser()?.username)!)
query.findObjectsInBackgroundWithBlock { (objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
if let objects = objects! as? [PFObject] {
for object in objects {
object.deleteInBackground()
}
}
} else {
print(error)
}
}
let RSVPData = PFObject(className: "RSVPData")
RSVPData["label1"] = label1Field.text
RSVPData["label2"] = label2Field.text
RSVPData["label3"] = label3Field.text
RSVPData["label4"] = label4Field.text
RSVPData["username"] = PFUser.currentUser()?.username
RSVPData.saveInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
if (success) {
if let error = error {
if let errorString = error.userInfo["error"] as? String {
self.displayAlert("Failed", message: errorString)
}
} else {
self.performSegueWithIdentifier("RSVPToMain", sender: self)
}
} else {
// There was a problem, check error.description
}
}
}
/*----------- ~ MAIN THREAD ~ -----------*/
override func viewDidLoad() {
super.viewDidLoad()
//Run title bar function
TitleBar()
label1.text = "Label 1"
label2.text = "Label 2"
label3.text = "Label 3"
label4.text = "Label 4"
let query = PFQuery(className:"RSVPData")
query.whereKey("username", equalTo: (PFUser.currentUser()?.username)!)
query.findObjectsInBackgroundWithBlock { (objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
if let objects = objects! as? [PFObject] {
for object in objects {
self.label1Field.text = object["label1"] as? String
self.label2Field.text = object["label2"] as? String
self.label3Field.text = object["label3"] as? String
self.label4Field.text = object["label4"] as? String
}
}
} else {
print(error)
}
}
}
/*----------- ~ FUNCTIONS ~ -----------*/
//Function containing all title bar instructions
func TitleBar() {
/*----------- ~ NAVIGATION BAR (USER INTERFACE) ~ -----------*/
//Set the navigation bar tet color and size
let nav = self.navigationController?.navigationBar
nav?.titleTextAttributes = [NSFontAttributeName: UIFont(name: "HelveticaNeue-Light", size: 20)!, NSForegroundColorAttributeName: UIColor(red: 255/255, green: 255/255, blue: 255/255, alpha: 1.0)]
/*----------- ~ OUTLET - 'usernameTitle' ~ -----------*/
//Set the 'usernameTitle' font and size
usernameTitle.setTitleTextAttributes([NSFontAttributeName: UIFont(name: "Arial", size: 11)!], forState: UIControlState.Normal)
//Set the 'usernameTitle' outlet to the current user
usernameTitle.title = PFUser.currentUser()?.username
}
//Function to display an alert
func displayAlert(title: String, message: String) {
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
SOLUTION:
#IBAction func submitButton(sender: AnyObject) {
let query = PFQuery(className:"RSVPData")
query.whereKey("username", equalTo: (PFUser.currentUser()?.username)!)
query.findObjectsInBackgroundWithBlock { (object: [PFObject]?, error: NSError?) -> Void in
if error == nil {
if object!.count > 0 {
if let object = object! as? [PFObject] {
for object in object {
object["label1"] = self.label1Field.text
object["label2"] = self.label2Field.text
object["label3"] = self.label3Field.text
object["label4"] = self.label4Field.text
object.saveInBackground()
}
self.performSegueWithIdentifier("RSVPToMain", sender: self)
}
} else if object?.count == 0 {
let RSVPData = PFObject(className: "RSVPData")
RSVPData["label1"] = self.label1Field.text
RSVPData["label2"] = self.label2Field.text
RSVPData["label3"] = self.label3Field.text
RSVPData["label4"] = self.label4Field.text
RSVPData["username"] = PFUser.currentUser()?.username
RSVPData.saveInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
if (success) {
if let error = error {
if let errorString = error.userInfo["error"] as? String {
self.displayAlert("Failed", message: errorString)
}
} else {
self.performSegueWithIdentifier("RSVPToMain", sender: self)
}
} else {
// There was a problem, check error.description
}
}
}
} else {
print(error)
}
}
}

I do not need to delete the row if it exists. Instead of deleting the row, you can just update with the new values that you get from the user. Here is a sample from parse.com website on how to update an existing object. Let me know if you still have a question and i will try it in Xcode and update it with your code. https://parse.com/docs/ios/guide
var query = PFQuery(className:"GameScore")
query.getObjectInBackgroundWithId("xWMyZEGZ") {
(gameScore: PFObject?, error: NSError?) -> Void in
if error != nil {
print(error)
} else if let gameScore = gameScore {
gameScore["cheatMode"] = true
gameScore["score"] = 1338
gameScore.saveInBackground()
}
}
You can use your code and see comments bellow.
query.whereKey("username", equalTo: (PFUser.currentUser()?.username)!)
query.findObjectsInBackgroundWithBlock { (objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
if let objects = objects! as? [PFObject] {
//see comments bellow
//for object in objects {
//object.deleteInBackground()
// }
}
} else {
print(error)
}
}
When you get the array of objects, you would check if there is only one object in the array to make sure there is only one row for that user and then do object["sangeet"] = label1Field.text
update all the fields and do object.saveInBackground();
==========
Update
You can create a new row when objects return nil. I am thinking you should be able excuse save operation inside of the findObjectsInBackgroundWithBlock block I am not very familiar with swift so i copy pasted your code and it should work. Let me know if you still having issues.
let query = PFQuery(className:"RSVPData")
query.whereKey("username", equalTo: (PFUser.currentUser()?.username)!)
query.findObjectsInBackgroundWithBlock { (objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
if let objects = objects! as? [PFObject] {
for object in objects {
object.deleteInBackground()
}
}else{
//assuming that object is nil and there is no record for that user
//you would then add the user and information into the database
let RSVPData = PFObject(className: "RSVPData")
RSVPData["sangeet"] = label1Field.text
RSVPData["tibetan"] = label2Field.text
RSVPData["hindu"] = label3Field.text
RSVPData["reception"] = label4Field.text
RSVPData["username"] = PFUser.currentUser()?.username
RSVPData.saveInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
if (success) {
if let error = error {
if let errorString = error.userInfo["error"] as? String {
self.displayAlert("Failed", message: errorString)
}
} else {
self.performSegueWithIdentifier("RSVPToMain", sender: self)
}
} else {
// There was a problem, check error.description
}
}
}
} else {
print(error)
}
}

Seems to me that you do delete the row if the username is the same value as your PFUser's username. I have not used Parse before but object.deleteInBackground() looks like a delete to me.
let query = PFQuery(className:"RSVPData")
query.whereKey("username", equalTo: (PFUser.currentUser()?.username)!)
query.findObjectsInBackgroundWithBlock { (objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
if let objects = objects! as? [PFObject] {
for object in objects {
object.deleteInBackground()
}
}
}
}
So I guess you only delete the entry if you use the same user twice.

Related

Parse does not return on first query attempt

I have a segment control that determines what is loaded into the table view. I load the table for index 0 in viewDidLoad and it works fine. The segment control is 'selected' to index 0 via storyboard. When the index is switched to index 1 I get the alert message I set "No items owned" and it has me stay at index 0. When I click index 1 for the second time, it finds the items and loads the table I want for index 1. Why does the query for getOwnedCosmetics not return anything the first time the segment is changed to index 1? I print cosmeticIdOwned and it returns [ ]. The second time the array is filled with the objects.
import UIKit
import Parse
class SkinsTableViewController: UITableViewController {
var cosmeticTracker = "Skins"
var cosmeticName = [String]()
var cosmeticImage = [PFFile]()
var cosmeticRarity = [String]()
var cosmeticId = [String]()
var owned = "Owned"
var cosmeticIdOwned = [String]()
#IBOutlet var cosmeticTable: UITableView!
#IBOutlet weak var seg: UISegmentedControl!
#IBAction func segmentChanged(_ sender: UISegmentedControl) {
switch seg.selectedSegmentIndex {
case 0:
getAllCosmetics(cosmetic: cosmeticTracker)
case 1:
getOwnedCosmetics(cosmetic: cosmeticTracker)
default:
break;
}
}
override func viewDidLoad() {
super.viewDidLoad()
getAllCosmetics(cosmetic: cosmeticTracker)
}
#objc func getOwnedCosmetics (cosmetic : String){
if PFUser.current() != nil {
let query = PFQuery(className: (cosmetic + owned))
query.whereKey("theUser", equalTo: PFUser.current()?.objectId as Any)
query.findObjectsInBackground(block: { (objects, error) in
if error != nil {
print(error)
} else if let objects = objects {
self.cosmeticIdOwned.removeAll()
for object in objects {
self.cosmeticIdOwned.append(object["theCosmetic"] as! String)
}
}
})
if cosmeticIdOwned.count > 0 {
getOwnedCosmeticsQuery()
}
else {
self.displayAlert(title: "No items owned", message: "Add \(cosmeticTracker) to your colection")
seg.selectedSegmentIndex = 0
}
}
else {
self.displayAlert(title: "Unable to retrieve data", message: "Please sign in")
seg.selectedSegmentIndex = 0
}
}
func getOwnedCosmeticsQuery (){
let queryOwned = PFQuery(className: cosmeticTracker)
queryOwned.whereKey("objectId", containedIn: cosmeticIdOwned as [AnyObject])
queryOwned.order(byAscending: "Rarity")
queryOwned.addAscendingOrder("Name")
queryOwned.findObjectsInBackground(block: { (objects, error) in
if error != nil {
print(error)
} else if let objects = objects {
self.cosmeticName.removeAll()
self.cosmeticImage.removeAll()
self.cosmeticId.removeAll()
self.cosmeticRarity.removeAll()
for object in objects {
self.cosmeticName.append(object["Name"] as! String)
self.cosmeticImage.append(object["Image"] as! PFFile)
self.cosmeticId.append(object.objectId as String!)
self.cosmeticRarity.append(object["Rarity"] as! String)
self.cosmeticTable.reloadData()
if self.cosmeticIdOwned.count > 99 {
self.getOwnedCosmeticsQueryTwo()
}
}
}
})
}
}
I would like to note if I populate cosmeticIdOwned with an item in viewDidLoad. It will make it to getOwnedCosmeticsQuery and find the item, even though it is not actually 'owned'. When I click the segment for getOwnedCosmetics for the second time, it runs getOwnedCosmeticsQuery with the correct objects it found from getOwnedCosmetics.

SWIFT: Tapping a UIButton really fast results in incorrect vote count

I've created a simple song list app below.
It works as follows:
1. Users can add songs to the list (using the '+' icon)
2. Uses can click to the vote button to increase or decrease their vote by one. It works similar to the 'LIKE' button on Facebook.
As you can see, some of the vote counts are negative numbers. The reason for this is because during QA, I clicked the 'Vote' button extremely fast and it causes the vote count to go haywire. I do not see this as being a scenario that would occur a lot, but does anyone have a creative way to prevent this from happening? Any solutions would be appreciated. Thanks.
I can add my code if it's required, but I am hoping that there is a simple solution to this that I haven't found as yet.
UPDATE: Added code for the Table View Controller and the Table View Cell -
Table View Cell (with UIButton Action for 'vote'):
#IBAction func voteButton(sender: UIButton) {
var query = PFQuery(className:"UserVotes")
//query the database to check if the user actually like the song in the paticular row that's clicked
query.whereKey("username", equalTo: (PFUser.currentUser()?.username)!)
query.whereKey("song", equalTo: songID!)
query.whereKey("vote", equalTo: true)
query.findObjectsInBackgroundWithBlock {
(object: [PFObject]?, error: NSError?) -> Void in
if error == nil {
//this is the case where a TRUE value is found. LIKE is ON
if object!.count > 0 {
if let object = object! as? [PFObject] {
for object in object {
if self.delegate != nil && self.rowValue != nil {
//decrease the count by 1
self.voteCount = self.voteCount! - 1
self.votes.text = "\(self.voteCount!)"
self.voteButtonOn = true
self.delegate!.voteButtonTurnOffFromCellRow(self.rowValue!)
print(self.voteButtonOn)
}
}
}
//this is the case where a TRUE value is NOT found, LIKE is OFF.
} else if object?.count == 0 {
if self.delegate != nil && self.rowValue != nil {
//increase the count by 1
self.voteCount = self.voteCount! + 1
self.votes.text = "\(self.voteCount!)"
self.voteButtonOn = false
self.delegate!.voteButtonTurnOnFromCellRow(self.rowValue!)
print(self.voteButtonOn)
}
}
} else {
// Log details of the failure
print("Error: \(error!) \(error!.userInfo)")
}
}
}
Table View Controller:
import UIKit
import Parse
protocol VoteProtocol {
func voteButtonTurnOnFromCellRow(row: Int)
func voteButtonTurnOffFromCellRow(row: Int)
}
class MusicPlaylistTableViewController: UITableViewController, VoteProtocol {
var usernames = [String]()
var songs = [String]()
var songVotes = [Int]()
//Function to display an alert
func displayAlert(title: String, message: String) {
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.separatorColor = UIColor.grayColor()
let query = PFQuery(className:"PlaylistData")
query.findObjectsInBackgroundWithBlock { (objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
if let objects = objects! as? [PFObject] {
self.usernames.removeAll()
self.songs.removeAll()
for object in objects {
let username = object["username"] as? String
self.usernames.append(username!)
let track = object["song"] as? String
self.songs.append(track!)
let votes = object["numVotes"] as? Int
self.songVotes.append(votes!)
}
self.tableView.reloadData()
}
} else {
print(error)
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return usernames.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CellTrack", forIndexPath: indexPath) as! TrackTableViewCell
cell.username.text = usernames[indexPath.row]
cell.songTitle.text = songs[indexPath.row]
var voteCount: Int = songVotes[indexPath.row]
var voteCountString = String(voteCount)
cell.votes.text = voteCountString
cell.delegate = self
cell.rowValue = indexPath.row
cell.songID = songs[indexPath.row]
cell.voteCount = songVotes[indexPath.row]
cell.selectionStyle = UITableViewCellSelectionStyle.None
return cell
}
override func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
}
//If the LIKE button is OFF perform this. This means that a record in the database has NOT been found with a value of TRUE.
func voteButtonTurnOnFromCellRow(var row: Int) {
// At this point, the value needs to be changed to TRUE, and the vote count needs to be increased by 1.
//note: does not take into account scenario where there are duplicate records (slight case)
let query = PFQuery(className:"UserVotes")
query.whereKey("username", equalTo: (PFUser.currentUser()?.username)!)
query.whereKey("song", equalTo: songs[row])
query.whereKey("vote", equalTo: false)
query.findObjectsInBackgroundWithBlock { (object: [PFObject]?, error: NSError?) -> Void in
if error == nil {
//this is the case where a FALSE value is found. LIKE is OFF
if object!.count > 0 {
if let object = object! as? [PFObject] {
for object in object {
object["vote"] = true
object.saveInBackground()
let query = PFQuery(className:"PlaylistData")
query.whereKey("song", equalTo: self.songs[row])
query.findObjectsInBackgroundWithBlock { (object: [PFObject]?, error: NSError?) -> Void in
if error == nil {
if let object = object! as? [PFObject] {
for object in object {
var voteCount:Int = object["numVotes"] as! Int
voteCount = voteCount + 1
object["numVotes"] = voteCount
object.saveInBackground()
}
}
}
}
}
}
//this is the case where a TRUE value is NOT found, LIKE is OFF (first time user vote)
} else if object?.count == 0 {
//add row to table
let addUserVote = PFObject(className: "UserVotes")
addUserVote["song"] = self.songs[row]
addUserVote["username"] = PFUser.currentUser()?.username
addUserVote["vote"] = true
addUserVote.saveInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
if (success) {
if let error = error {
if let errorString = error.userInfo["error"] as? String {
self.displayAlert("Error", message: errorString)
}
} else {
let query = PFQuery(className:"PlaylistData")
query.whereKey("song", equalTo: self.songs[row])
query.findObjectsInBackgroundWithBlock { (object: [PFObject]?, error: NSError?) -> Void in
if error == nil {
if let object = object! as? [PFObject] {
for object in object {
var voteCount:Int = object["numVotes"] as! Int
voteCount = voteCount + 1
object["numVotes"] = voteCount
object.saveInBackground()
}
}
}
}
}
} else {
// There was a problem, check error.description
}
}
}
} else {
// Log details of the failure
print("Error: \(error!) \(error!.userInfo)")
}
}
}
//If the LIKE button is ON perform this. This means that a record in the database has been found with a value of TRUE.
func voteButtonTurnOffFromCellRow(var row: Int) {
// At this point, the value needs to be changed to FALSE, and the vote count needs to be decreased by 1.
//note: does not take into account scenario where there are duplicate records (slight case)
let query = PFQuery(className:"UserVotes")
query.whereKey("username", equalTo: (PFUser.currentUser()?.username)!)
query.whereKey("song", equalTo: songs[row])
query.whereKey("vote", equalTo: true)
query.findObjectsInBackgroundWithBlock { (object: [PFObject]?, error: NSError?) -> Void in
if error == nil {
if let object = object! as? [PFObject] {
for object in object {
object["vote"] = false
object.saveInBackground()
let query = PFQuery(className:"PlaylistData")
query.whereKey("song", equalTo: self.songs[row])
query.findObjectsInBackgroundWithBlock { (object: [PFObject]?, error: NSError?) -> Void in
if error == nil {
if let object = object! as? [PFObject] {
for object in object {
var voteCount:Int = object["numVotes"] as! Int
voteCount = voteCount - 1
object["numVotes"] = voteCount
object.saveInBackground()
}
}
}
}
}
} else {
print(error)
}
}
}
}
}
In your button action toggle button userInteractionEnabled property to NO at the beginning and to YES at the end of the method. This may help you

UIButtons in tableViewCell, how to get already following users from Parse using Swift

I've been working on it from 2 weeks now, I've Instagram like app, where user can signUp and Login using Parse backend. Now after signUp a segue is made for the next screen called userListTableViewController here users will be allowed to follow the already existing users. Now i trying to retrieve data in NSMutableArray and show them as follows:
import UIKit
class userListTableViewController: UITableViewController {
var data:NSMutableArray = NSMutableArray()
var isFollowing = [PFObject:Bool]()
func loadData() {
data.removeAllObjects()
isFollowing.removeAll(keepCapacity: true)
var userQuery = PFUser.query()
userQuery?.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if error != nil {
} else {
if let objects = objects {
for object in objects {
if let user = object as? PFObject {
if user.objectId != PFUser.currentUser()?.objectId {
self.data.addObject(user)
var followerQuery: PFQuery = PFQuery(className: "Followers")
followerQuery.whereKey("follower", equalTo: PFUser.currentUser()!)
followerQuery.whereKey("user", equalTo: user)
followerQuery.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if let objects = objects {
if objects.count > 0 {
self.isFollowing[user] = true
} else {
self.isFollowing[user] = false
}
}
if self.isFollowing.count == self.data.count {
self.tableView.reloadData()
}
})
}
}
}
}
}
})
}
override func viewDidLoad() {
super.viewDidLoad()
loadData()
}
/*
var array:NSMutableArray = NSMutableArray(array: self.data.reverseObjectEnumerator().allObjects)
self.data = array as NSMutableArray
self.tableView.reloadData()
*/
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return data.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let myCell = tableView.dequeueReusableCellWithIdentifier("users", forIndexPath: indexPath) as! userListTableViewCell
let userData:PFObject = self.data.objectAtIndex(indexPath.row) as! PFObject
myCell.fullName.text = userData["objectId"] as! String!
myCell.genderLabel.text = userData["gender"] as! String!
// var array:NSMutableArray = NSMutableArray(array: self.data.reverseObjectEnumerator().allObjects)
// self.data = array as NSMutableArray
let followedId = // What should i use here?? *****************
if isFollowing[followedId] == true { // ERROR!! not able to use followedId here.. how should i give a value so that it will check the particular user's objectId and isFollowing Bool value. ******************
myCell.followButtton.setTitle("unfollow", forState: UIControlState.Normal)
} else {
myCell.followButtton.setTitle("follow", forState: UIControlState.Normal)
}
userData["profilePicture"]?.getDataInBackgroundWithBlock({ (data, error) -> Void in
if let downloadeImage = UIImage(data: data!) {
myCell.dp.image = downloadeImage
}
myCell.followButtton.tag = indexPath.row
myCell.followButtton.addTarget(self, action: "followButtonTapped:", forControlEvents: UIControlEvents.TouchUpInside)
})
return myCell
}
// IBActions..
func followButtonTapped(sender: UIButton){
let userData:PFObject = self.data.objectAtIndex(sender.tag) as! PFObject
let followedId = userData["objectId"] as! PFObject
if isFollowing[followedId] == false {
isFollowing[followedId] = true
sender.setTitle("unfollow", forState: UIControlState.Normal)
let getObjectByIdQuery = PFUser.query()
getObjectByIdQuery?.whereKey("objectId", equalTo: userData.objectId!)
getObjectByIdQuery?.getFirstObjectInBackgroundWithBlock({ (foundObject:PFObject?, error:NSError?) -> Void in
if let object = foundObject {
var followers:PFObject = PFObject(className: "Followers")
followers["user"] = object
followers["follower"] = PFUser.currentUser()
followers.saveInBackgroundWithBlock({ (success, error) -> Void in
if error != nil {
println(error)
} else {
println("saved")
}
})
}
})
} else {
isFollowing[followedId] = false
sender.setTitle("follow", forState: UIControlState.Normal)
var query:PFQuery = PFQuery(className: "Followers")
query.whereKey("follower", equalTo: PFUser.currentUser()!)
query.whereKey("user", equalTo: data[sender.tag])
query.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if let objects = objects {
for object in objects {
object.deleteInBackgroundWithBlock({ (success, error) -> Void in
if error != nil {
println(error)
} else {
println("deleted")
}
})
}
}
})
}
}
}
shown the problem in the code as comment.. or else can you please suggest me some other way better than this.. i've tried to retrieve data in arrays also, there also i'm getting problem for the "follow" buttons. You can see the problem here.. Not able to reload data properly when retrieving objects from Parse .. Please check this link too..
Please help me out here.. Thanks..
Edit: I just want to make the user list with there gender full name and profile picture, and want to allow user to follow them with a button "follow" on each cell.. and if the app restarts, the users which have been already followed by the current user must be marked as "unfollow" to unfollow the user if current user wants it..
check the comments in the code.. error is there where you see "***************" it's in cellForRowAtIndexPath
Instead of playing with PFUser, arrays and dictionaries, create a class User in application side.
class User: NSObject {
var pfUser : PFObject?
var isFollowing : Bool = false
}
And in loadData method,
func loadData() {
// **************** Your Codes ********************
if user.objectId != PFUser.currentUser()?.objectId {
var otherUser = User()
otherUser.pfUser = user
// **************** Your Codes ********************
followerQuery.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if let objects = objects {
if objects.count > 0 {
otherUser.isFollowing = true
}
}
})
self.data.addObject(otherUser)
}
}
Then use the single mutableArray data to populate tableView.

iOS swift parse update multiple objects

i have 2 objects(parent , child) with cash and value fields for each object .
at pressing a button , it does some calculations and update objects fields to the new values ,I'm able to update the parent fields to the new values but the child values remain as they are.
heres the full code :
#IBOutlet weak var childImage: UIImageView!
#IBOutlet weak var childInfo: UILabel!
#IBOutlet weak var adoptButton: UIButton!
#IBOutlet weak var nextButton: UIButton!
#IBOutlet weak var malesButton: UIButton!
#IBOutlet weak var femalesButton: UIButton!
#IBOutlet weak var statusLabel: UILabel!
// empty array to load ids from parse
var objectIds = [String]()
// counter is the index number of Ids array
var counter = 0
var childCash = 0
var childValue = 0
var childId: String? = ""
var childName = ""
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
loadingMalesIds()
childInfo.hidden = true
adoptButton.hidden = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func maleButtonPressed(sender: AnyObject) {
loadingMalesIds()
}
#IBAction func femaleButtonPressed(sender: AnyObject) {
loadingFemalesIds()
}
#IBAction func nextButtonPressed(sender: AnyObject) {
counter++
if (counter <= objectIds.count){
var query = PFQuery(className:"_User")
query.getObjectInBackgroundWithId(objectIds[(counter - 1) ] ) {
(child: PFObject?, error: NSError?) -> Void in
if error == nil && child != nil {
self.childName = child?["username"] as! String
self.childValue = child?["value"] as! Int
var status = child?["status"] as! String
self.childCash = child?["cash"] as! Int
self.childId = (child?.objectId)!
let userImageFile = child?["userImage"] as! PFFile
userImageFile.getDataInBackgroundWithBlock {
(imageData: NSData?, error: NSError?) -> Void in
if error == nil {
if let imageData = imageData {
let image = UIImage(data:imageData)
self.childImage.layer.borderWidth = 2
self.childImage.layer.masksToBounds = true
self.childImage.layer.borderColor = UIColor.whiteColor().CGColor
self.childImage.image = image
}
}
}
self.statusLabel.text = status
self.childInfo.text = "\(self.childName), $\(self.childValue)"
} else {
println(error)
} // end of if error == nil
}
}else{
counter = 0
} //end of if (counter <= objectIds.count)
childInfo.hidden = false
adoptButton.hidden = false
}//end of method
func loadingMalesIds(){
objectIds.removeAll(keepCapacity: false)
var query = PFQuery(className:"_User")
// get Ids with a condition that users are males
query.whereKey("gender", equalTo:"male")
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
// The find succeeded.
if let objects = objects as? [PFObject] {
for object in objects {
self.objectIds.append((object.objectId)! as String)
}
}
} else {
// Log details of the failure
println("Error: \(error!) \(error!.userInfo!)")
}
}
self.malesButton.enabled = false
self.femalesButton.enabled = true
}
func loadingFemalesIds(){
objectIds.removeAll(keepCapacity: false)
var query = PFQuery(className:"_User")
// get Ids with a condition that users are males
query.whereKey("gender", equalTo:"female")
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
// The find succeeded.
if let objects = objects as? [PFObject] {
for object in objects {
self.objectIds.append((object.objectId)! as String)
}
}
} else {
// Log details of the failure
println("Error: \(error!) \(error!.userInfo!)")
}
}
self.malesButton.enabled = true
self.femalesButton.enabled = false
}
#IBAction func adoptPressed(sender: AnyObject) {
// get parent cash and value
let cash = PFUser.currentUser()!["cash"] as? Int
let value = PFUser.currentUser()!["value"] as? Int
let numberOfChildren = PFUser.currentUser()!["numberOfChildren"] as? Int
//check
if (cash < childValue){
noCashAlert()
}else{
var parentNewCash = cash! - childValue
var parentNewValue = value! + childValue
var childNewCash = childCash + (10 * childValue / 100)
//number of children
var newNumberOfChildren = numberOfChildren! + 1
// save parent new cash and value
var queryParent = PFQuery(className:"_User")
//get parent ID
var currentUserId = PFUser.currentUser()?.objectId!
println("current: \(currentUserId)")
// do the saving operation
queryParent.getObjectInBackgroundWithId(currentUserId!) {
(parent: PFObject?, error: NSError?) -> Void in
if error != nil {
println(error)
} else if let parent = parent {
//parent new cash
parent["cash"] = parentNewCash
//parent new value
parent["value"] = parentNewValue
//parent number of children
parent["numberOfChildren"] = newNumberOfChildren
parent.saveInBackground()
}
}//end parent query
var queryChild = PFQuery(className:"_User")
queryChild.getObjectInBackgroundWithId(self.childId!) {
(child: PFObject?, error: NSError?) -> Void in
if error != nil {
println(error)
} else if let child = child {
child["cash"] = childNewCash
child.saveInBackground()
}
}//end child query
}
}
func noCashAlert(){
let message = "Sorry!, You Don't Have Enough Money,You Can Use Money Printer For Some Extra Cash "
let okText = "Ok"
let alert = UIAlertController(title: nil, message: message, preferredStyle: UIAlertControllerStyle.Alert)
let okayButton = UIAlertAction(title: okText, style: UIAlertActionStyle.Cancel, handler: nil)
alert.addAction(okayButton)
self.presentViewController(alert, animated: true, completion: nil)
}
}
We need more information.
One possibility is that childValue is declared as an Int, and its value is less than 100, so that childValue / 100 is 0. Remember integer division vs. floating-point division. You could say instead Double(childValue) / 100.0 to force floating point.
Have you used a debugger or debugging println()s to determine if childNewCash is what you expect?
Another possibility is that child is declared as a struct not a class, but this is very unlikely given the context.
As a minor style point, you probably want:
if let e = error {
println(e)
}
so that you don't get Optional(...) in your output.
This answer should have gone into comments section but for now I'll keep it and just edit...This is impossible to answer without a lot more context. When you say "it's not updating at all" where and when are you looking?
If you're looking in the getObjectInBackgroundWithId completion handler right after setting the cash value, I'm sure you'd get the right value back.
Are you looking in the cloud database? With a background save there's some delay, and you could be looking too early. If there's conflicting saves, the save tasks could be overwriting one another. How about using the optional completion handler for saveInBackground() to make sure you're not checking too soon?

Swift instagram clone : loading followed user post

I' m trying to make an instagram clone with swift, based on the Ribbit tuto. People can share photos and follow other users etc etc. I made a PFRelation between users using Parse. What i would like to do now, is to show in my WalltableViewController, only the followed users post.
I made a function to load all the post in a NSMutable array called timeLineData.
I ve even made a function to get the followed users ina NSMutableArray called followedFriends..
But i don't succeed in filtering the loading post function with followedFriends. I have this error : Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Cannot do a comparison query for type: __NSArrayM'
Here is my code :
import UIKit
import QuartzCore
class WallTableViewController: UITableViewController, UINavigationControllerDelegate {
#IBOutlet var posterAvatar: UIImageView!
override func preferredStatusBarStyle() -> UIStatusBarStyle {
return UIStatusBarStyle.LightContent
}
var timeLineData:NSMutableArray = NSMutableArray ()
var followedFriends:NSMutableArray = NSMutableArray ()
func loadUser () {
followedFriends.removeAllObjects()
var friendsRelation: AnyObject! = PFUser.currentUser().objectForKey("KfriendsRelation")
var findUser : PFQuery = friendsRelation.query()
findUser.findObjectsInBackgroundWithBlock { (objects:[AnyObject]!, error:NSError!) -> Void in
if !(error != nil) {
// The find succeeded.
println("succesfull load Users")
// Do something with the found objects
for object in objects {
self.followedFriends.addObject(object)
println("users added to userlist")
for item in self.followedFriends {
println(item) }
}
self.tableView.reloadData()
} else {
// Log details of the failure
println("error loadind user ")
}
}
}
func loadPost () {
timeLineData.removeAllObjects()
let currentUser = PFUser.currentUser()
var findPost:PFQuery = PFQuery(className: "UserPost")
findPost.whereKey("from", equalTo: followedFriends )
findPost.orderByDescending("createdAt")
findPost.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]! , error: NSError!) -> Void in
if !(error != nil) {
// The find succeeded.
println("current user post finded")
// Do something with the found objects
for object in objects {
self.timeLineData.addObject(object)
}
self.tableView.reloadData()
} else {
// Log details of the failure
println("error loadind user post")
}
}
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return timeLineData.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:WallTableViewCell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as WallTableViewCell
let userPost:PFObject = self.timeLineData.objectAtIndex(indexPath.row) as PFObject
//define the username
var findUser:PFQuery = PFUser.query()
findUser.whereKey("objectId", equalTo: userPost.objectForKey("from").objectId)
findUser.findObjectsInBackgroundWithBlock{
(objects:[AnyObject]!, error:NSError!) -> Void in
if !(error != nil) {
if let user:PFUser = (objects as NSArray).lastObject as? PFUser {
cell.usernameLabel.text = user.username
// define avatar poster
if let avatarImage:PFFile = user["profileImage"] as? PFFile {
avatarImage.getDataInBackgroundWithBlock{(imageData:NSData!, error:NSError!)-> Void in
if !(error != nil) {
let image:UIImage = UIImage(data: imageData)
cell.posterAvatar.image = image as UIImage
cell.posterAvatar.layer.cornerRadius = 24
cell.posterAvatar.clipsToBounds = true
}
}
}
else {
cell.posterAvatar.image = UIImage(named: "Avatar-1")
cell.posterAvatar.layer.cornerRadius = 24
cell.posterAvatar.clipsToBounds = true
}
}
}
}
//define the imagepost
let imagesPost:PFFile = userPost["imageFile"] as PFFile
imagesPost.getDataInBackgroundWithBlock{(imageData:NSData!, error:NSError!)-> Void in
if !(error != nil) {
let image:UIImage = UIImage(data: imageData)
cell.imagePosted.image = image as UIImage
}
else {
println("error")
}
}
return cell
}
override func viewDidAppear(animated: Bool) {
var currentUser = PFUser.currentUser()
if (currentUser != nil) {
println("User allready logued")
}
else {
// Show the signup or login screen
self.performSegueWithIdentifier("goToLogIn", sender: self)
}
}
override func viewWillAppear(animated: Bool) {
self.tableView.separatorColor = UIColor.whiteColor()
tabBarController?.tabBar.tintColor = UIColor.whiteColor()
self.loadUser()
self.loadPost()
self.tableView.reloadData()
}
}
FollowedFriends is an array, so you need to use containedIn. You use equalTo if you're comparing to one individual object. Change it to this.
findPost.whereKey("from", containedIn:followedFriends)
I also founded this solution :
//query for the friends of the user
var friendsRelation: AnyObject! = PFUser.currentUser().objectForKey("KfriendsRelation")
var findFriends : PFQuery = friendsRelation.query()
//using the friends from the query above, we find all the posts of the friends of the current user
var findPosts:PFQuery = PFQuery(className: "UserPost")
findPosts.whereKey("from", matchesQuery:findFriends)
findPost.orderByDescending("createdAt")
//run the query
findPosts.findObjectsInBackgroundWithBlock { (objects:[AnyObject]!, error:NSError!) -> Void in
if !(error != nil) {
//found posts
self.tableView.reloadData()
} else {
// Log details of the failure
println("error loadind posts ")
}
}

Resources