Updating parse object for current user instead of creating duplicate entry - ios

I am trying to let the user update their location. When I try to save the data it creates a duplicate entry instead of updating the old information. I have a heroku parse server deployed to a mongolabs db.
class DropLoc: UIViewController, CLLocationManagerDelegate {
var user = PFUser.currentUser()
var post = PFObject(className:"Post")
var query = PFQuery(className:"Post")
var point: PFGeoPoint!
let porta = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
var curGate = porta.location
self.porta.delegate = self
self.porta.desiredAccuracy = kCLLocationAccuracyBest
self.porta.requestWhenInUseAuthorization()
self.porta.startUpdatingLocation()
point = PFGeoPoint(location: curGate)
query.whereKey("user", equalTo: user!)
query.findObjectsInBackgroundWithBlock {(objects: [PFObject]?, error: NSError?) -> Void in
if error != nil {
print(error)
}else{
self.post["user"] = self.user ?? NSNull()
self.post["location"] = self.point ?? NSNull()
self.post.saveInBackground()
}
}
}
}

It appears point is only set once. Try adding the following method from the CLLocationManagerDelegate protocol.
func locationManager(manager: CLLocationManager, didUpdateToLocation newLocation: CLLocation, fromLocation oldLocation: CLLocation) {
// updated coordinate
point = PFGeoPoint(location: manager.location!)
}

Ok I found out what my issue was. My problem was that I was querying user and not .username.
var user = PFUser.currentUser()?.username
Also I needed to set the force the elements to update and if the query fails create a new instance.
query.whereKey("user", equalTo: user!)
query.getFirstObjectInBackgroundWithBlock() {(uobject: PFObject?, error: NSError?) -> Void in
if error != nil {
self.post["user"] = self.user ?? NSNull()
self.post["location"] = self.point ?? NSNull()
self.post.saveInBackground()
}else if let uobject = uobject{
uobject["user"] = self.user ?? NSNull()
uobject["location"] = self.point ?? NSNull()
uobject.saveInBackground()
}
}

Related

How to create an array of usersnames when working with Parse and Swift

I am trying to create an array of strings for all the usernames using the following code and populate a TableViewController.
class TableViewController: UITableViewController {
var randomUser = [String]()
override func viewDidLoad() {
super.viewDidLoad()
var query: PFQuery = PFUser.query()!
query.findObjectsInBackgroundWithBlock {(objects: [PFObject]?, error: NSError?) -> Void in
if error == nil{
if let objects = (objects as? [PFObject]!){
for object in objects{
self.randomUser.append(object.objectForKey("username") as! String)
print(object.objectForKey("username") as! String)
print(self.randomUser.count)
}
}
}
}
print(self.randomUser.count)
}
the output in the console:
0
username
1
username
2
username
3
But UItableview does not populate.. What could be causing this?
My guess is that query is delayed and view is created before it can return data. Thank you for any help!
Yes, you are right. You need to call self.tableView.reloadData() after you get the results of the query. Below is an example of where to call it.
private var usersArray = [PFUser]()
func fetchUsers() {
let userQuery: PFQuery = PFUser.query()!
userQuery.orderByAscending("username")
userQuery.whereKey("username", notEqualTo: (currentUser?.username)!)
userQuery.findObjectsInBackgroundWithBlock({
(users, error) -> Void in
if error == nil {
self.usersArray = users as! [PFUser]
self.tableView.reloadData()
} else {
print(error)
}
})
}
In this example, you can then access the username property by doing usersArray[i].username

Swift Properties

So, while learning swift, I've run into an issue with adding values to an array property. When I try to print the first value of the array after adding a value to it, I receive an index out of bounds error. How do I add a value to an array property that is accessible to the entire class?
class HomeViewController: UIViewController {
var geofences = [Geofence]()
override func viewDidLoad() {
super.viewDidLoad()
getFences()
print(self.geofences[0])
}
func getFences() {
var query = PFQuery(className:"Geofence")
query.whereKey("username", equalTo: "Peter")
query.findObjectsInBackgroundWithBlock {
(fences: [PFObject]?, error: NSError?) -> Void in
if error == nil && fences != nil {
if let fences = fences {
for (index, element) in fences.enumerate() {
var unique_id = element.objectId
var fence_radius = element["radius"] as! Int
var fence_name = element["name"] as! String
var lat = element["centerPoint"].latitude
var lon = element["centerPoint"].longitude
var center = CLLocationCoordinate2D(latitude: lat, longitude: lon)
var new_fence: Geofence? = Geofence(uniqueID: unique_id!, radius: fence_radius, centerPoint: center, name: fence_name)
self.geofences.append(new_fence!)
}
}
} else {
print(error)
}
}
}
EDIT: It seems I oversimplified the issue. Here is the code that's getting the index out of bounds error. When I retrieve the geofence from Parse, the geofences array is populated, but once it exits the getFences method, the array is emptied.
It's likely that your print call is being run before getFences() has had time to populate the array. You can check this with another print call outside of query.findObjectsInBackgroundWithBlock

Passing objectId from viewDidLoad to another function using Parse method getObjectInBackgroundWithId not working

I'm a beginner working with Parse and Swift. I need to update the object referred to in my viewDidLoad in another function within the same controller. How do I pass the currently loaded object's objectId without having to hardcode it like this:
query.getObjectInBackgroundWithId("8DkYgraEJq")
Here is my viewDidLoad function:
override func viewDidLoad() {
var query = PFQuery(className: "CheckedBaggage")
query.orderByAscending("createdAt")
query.whereKey("respondedTo", notEqualTo: true)
query.getFirstObjectInBackgroundWithBlock {
(CheckedBaggage: PFObject!, error: NSError!) -> Void in
if error != nil {
println("The getFirstObject request failed.")
} else {
// The find succeeded.
self.randomBaggageLabel.text = CheckedBaggage.objectForKey("message") as? NSString
CheckedBaggage.save()
println(CheckedBaggage.objectId)
let baggageId = CheckedBaggage.objectId
println("Successfully retrieved the object.")
}
}
I would like to try and pass the variable baggageId, which should be the object's ID as a string, as an argument to the getObjectInBackgroundWithId block in my carryIt function:
#IBAction func carryIt(sender: AnyObject!) {
println("CarryIt is being called")
var query = PFQuery(className: "CheckedBaggage")
query.getObjectInBackgroundWithId(baggageId) {
(CheckedBaggage: PFObject?, error: NSError?) -> Void in
if error != nil {
println(error)
} else if let CheckedBaggage = CheckedBaggage {
println("object hello!")
CheckedBaggage["respondedTo"] = true
CheckedBaggage["response"] = self.kindnessMessage.text
CheckedBaggage.save()
}
}
}
But I'm getting an "unresolved identifier" error. It updates my Parse database perfectly fine if I hardcode the object ID, but I can't do it this way. Here's a screenshot of the error:
Thank you so much for your help!
You have to initialize baggageId. To use it in multiple functions, it must be scoped at class level as the comment said. To set it after it has been declared, it must be a "var", not a constant "let".
var baggageId = ""
func viewDidload() {
var query = ...
query.get... {
baggageId = CheckedBaggege.objectId
}
}
func shipIt() {
var query = ...
query.getObjectWithId(baggageId) ...
}

iOS Swift Adding Object To Array

I am trying to add the retrieved object from Parse to an array. It has found the user, it is printed in my logs. All the key names match up, I can't seem to find why both my userArray and imageFiles array are printed to the logs as empty. Thanks for the help!
var userArray: [String] = []
var refresher: UIRefreshControl!
var imageFiles = [PFFile]()
override func viewDidLoad() {
super.viewDidLoad()
PFGeoPoint.geoPointForCurrentLocationInBackground { (geopoint: PFGeoPoint!, error: NSError!) -> Void in
if error == nil {
println(geopoint)
var user = PFUser.currentUser()
user["location"] = geopoint
user.save()
var query = PFUser.query()
query.whereKey("location", nearGeoPoint:geopoint)
query.limit = 10
query.findObjectsInBackgroundWithBlock({ (users: [AnyObject]!, error: NSError!) -> Void in
for user in users {
self.userArray.append(user["name"] as! String)
self.imageFiles.append(user["profilePicFile"] as! PFFile)
println(user)
}
})
}
}
self.refresher = UIRefreshControl()
self.refresher.attributedTitle = NSAttributedString(string: "Pull to refresh")
self.refresher.addTarget(self, action: "refresh", forControlEvents: UIControlEvents.ValueChanged)
self.tableView.addSubview(self.refresher)
//follow udemy to get pull to refresh , need update users
println(userArray)
println(imageFiles.count)
}
func updateUsers() {
self.userArray.removeAll(keepCapacity: true)
self.imageFiles.removeAll(keepCapacity: true)
var aquery = PFUser.query()
aquery.whereKey("username", equalTo: PFUser.currentUser().username)
var cools = aquery.findObjects()
var query = PFUser.query()
query.whereKey("location", nearGeoPoint: cools[0]["location"] as! PFGeoPoint!)
query.limit = 10
query.findObjectsInBackgroundWithBlock({ (users: [AnyObject]!, error: NSError!) -> Void in
for user in users {
self.userArray.append(user["name"] as! String)
self.imageFiles.append(user["profilePicFile"] as! PFFile)
}
self.tableView.reloadData()
self.refresher.endRefreshing()
})
}
The reason is because PFGeoPoint.geoPointForCurrentLocationInBackground is an asynchronous method that takes a callback closure as a parameter to fire off as soon as the asynchronous work is done. So viewDidLoad will call that method but continue on and print userArray before the geoPointForCurrentLocationInBackground callback gets fired.
This is a big feature of functional programming languages so I suggest reading up on closures when you can.
http://code.tutsplus.com/tutorials/swift-from-scratch-closures--cms-23138
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html
If you put the println statements after you iterate the users inside of the callback, you will see your data:
for user in users {
self.userArray.append(user["name"] as! String)
self.imageFiles.append(user["profilePicFile"] as! PFFile)
println(user)
}
}
println(userArray) // you will see userArray is populated
println(imageFiles.count)

Re-accessing random data between methods in ViewController

For my Swift app, I want the data accessed in the didReceiveMemoryWarning function to be from the same random column of data retrieved from the viewDidLoad function, which was done with let randNumber = Int(arc4random_uniform(UInt32(count))). My app is a poll app where users can vote for different poll options then when they click the "Next" button, it takes them to another poll at random. The code under the didReceiveMemoryWarning function is for adding the vote (and retrieving it) from a poll, but I need that poll to be the same one displayed by the viewDidLoad function. How do I do that? For some reason no matter what poll I retrieve (crepes or pancakes, Coke or Pepsi, chocolate or vanilla, etc.) it only adds votes to and retrieves the results from the "crepes or pancakes" poll. Like if the user gets the poll "Coke or Pepsi" and they select Coke, it'll add a vote to the crepes vote count and retrieve the results from that poll. How do I retrieve data from the poll that is retrieved?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var query = PFQuery(className: "VoteCount")
query.countObjectsInBackgroundWithBlock {
(count: Int32, error: NSError!) -> Void in
if error == nil {
let randNumber = Int(arc4random_uniform(UInt32(count)))
query.whereKey("pollNumber", equalTo: randNumber)
query.getFirstObjectInBackgroundWithBlock {
(voteCount1: PFObject!, error: NSError!) -> Void in
if error != nil {
NSLog("%#", error)
} else {
let votes = voteCount1["votes"] as Int
let votes2 = voteCount1["votes2"] as Int
let option1 = voteCount1["optionName"] as String
let option2 = voteCount1["optionName2"] as String
self.showOption1.text = "\(option1)"
self.showOption2.text = "\(option2)"
}
}
} else {
println("error \(error)")
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBOutlet weak var pollResults: UILabel!
#IBAction func addVote1(sender: AnyObject) {
for button in self.buttons {
button.enabled = false
}
var query = PFQuery(className: "VoteCount")
query.getFirstObjectInBackgroundWithBlock {
(voteCount1: PFObject!, error: NSError!) -> Void in
if error != nil {
NSLog("%#", error)
} else {
voteCount1.incrementKey("votes")
voteCount1.saveInBackgroundWithTarget(nil, selector: nil)
let votes = voteCount1["votes"] as Int
let votes2 = voteCount1["votes2"] as Int
self.pollResults.text = "\(votes) \(votes2)"
}
}
}
You could make randomNumber a property instead of a local variable. However I think what you're actually trying to do is make sure you access the same PFObject in your later methods as you do in viewDidLoad. To do this, you don't need to re-fetch from Parse. Just keep a reference to the PFObject:
var voteCount : PFObject?
And in your completion block in viewDidLoad:
(voteCount1: PFObject!, error: NSError!) -> Void in
if error != nil {
NSLog("%#", error)
} else {
self.voteCount = voteCount1
// The rest of your code...
let votes = voteCount1["votes"] as Int
Then, later on, instead of fetching again, you just use the voteCount property:
#IBAction func addVote1(sender: AnyObject) {
for button in self.buttons {
button.enabled = false
}
voteCount.incrementKey("votes")
voteCount.saveInBackgroundWithTarget(nil, selector: nil)
let votes = voteCount["votes"] as Int
let votes2 = voteCount["votes2"] as Int
self.pollResults.text = "\(votes) \(votes2)"
}

Resources