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)
Related
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()
}
}
I am trying to implement loading data from Parse server after tapping a button on postVC to load data and navigate to guestVC. It was working fine and at some point began to crash the app...not sure why? I am getting the fatal error: unexpectedly found nil while unwrapping an Optional value...Any and all direction or help would be greatly appreciated. Thanks!
import UIKit
import Parse
var postuuid = [String]()
class postVC: UITableViewController {
//postVC button click function
//clicked username button from post
#IBAction func usernameBtn_click(sender: AnyObject) {
let i = sender.layer.valueForKey("index") as! NSIndexPath
let cell = tableView.cellForRowAtIndexPath(i) as! postCell
// if user tapped on himself go home, else go guest
if cell.usernameBtn.titleLabel?.text == PFUser.currentUser()?.username {
let home = self.storyboard?.instantiateViewControllerWithIdentifier("homeVC") as! homeVC
self.navigationController?.pushViewController(home, animated: true)
} else {
let guest = self.storyboard?.instantiateViewControllerWithIdentifier("guestVC") as! guestVC
self.navigationController?.pushViewController(guest, animated: true)
}
}
// guestVC relevant code
import UIKit
import Parse
var guestname = [String]()
class guestVC: UICollectionViewController {
var uuidArray = [String]()
var picArray = [PFFile]()
// posts loading function
func loadPosts() {
let query = PFQuery(className: "posts")
// app keeps crashing in line below when I try to load data to guestVC
query.whereKey("username", equalTo: guestname.last!)
query.limit = self.page
query.findObjectsInBackgroundWithBlock( { (objects:[PFObject]?, error:NSError?) -> Void in
if error == nil {
self.uuidArray.removeAll(keepCapacity: false)
self.picArray.removeAll(keepCapacity: false)
for object in objects! {
self.uuidArray.append(object.valueForKey("uuid") as! String)
self.picArray.append(object.valueForKey("pic") as! PFFile)
}
self.collectionView?.reloadData()
} else {
print(error!.localizedDescription)
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
// code here will be executed as the main queue
})
})
}
You use a lot of exclamation marks to force unwrap optional values in your code, it's a bad habit.
For example, you can unwrap guestname.last safely by:
guard let lastItem = guestname.last else {
// do something else
return
}
query.whereKey("username", equalTo: lastItem)
Before adding or appending, check dictionary key has valid value or not. Check if 'uuid' or 'pic' key has value in dictionary or not. If it has then add/append.
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
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)
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)"
}