Swift Properties - ios

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

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: How to cancel a PFQuery if function is run again before PFQuery is finished?

Apologies that I couldn't think of a better way to title this.
Basically I have an app which connects to Parse. I have specified an action, which runs when a text field is changed (i.e when the user types a letter)
within this action I'm calling a PFQuery to Parse, which I then ask to findObjectsInBackgroundWithBlock.
The problem is that if a user were to type another letter before this query has finished running, then 2 queries are now running and the results of both end up populating the tableView.
So my question is simply, if the user were to type in another letter before the first findObjectsInBackgroundWithBlock has finished, how would I cancel the first and run a new one?
I have tried inserting PFQuery.cancel(query) at the start of the action, but the code gets confused as there isn't a query running yet when the action runs for the first time.
My code, incase it may help:
#IBAction func textFieldChanged (sender: AnyObject) {
let query = PFUser.Query()
query!.whereKey("Postcode", containsString: searchField.text)
query?.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if let objects = objects {
for object in objects {
self.citiesArray.append(object["city"] as! String)
}
}
})
Many thanks for your patience!
You can try wrapping those requests in NSOperation and adding them to a dedicated (for such search requests) NSOperationQueue and calling cancelAllOperations() when a new character is typed.
In NSOperation's inner Parse block check for self.cancelled and return doing nothing if cancelled. Should work fine.
UPDATE:
class ViewController: UIViewController {
#IBOutlet weak var searchField: UITextField!
var citiesArray = [String]()
lazy var textRequestQueue: NSOperationQueue = {
var queue = NSOperationQueue()
queue.maxConcurrentOperationCount = 1
queue.qualityOfService = NSQualityOfService.UserInteractive
return queue
}()
#IBAction func textFieldChanged(sender: AnyObject) {
textRequestQueue.cancelAllOperations()
let query = PFQuery()
query.whereKey("Postcode", containsString: searchField.text)
textRequestQueue.addOperation(TextRequestOperation(query: query, resultBlock: { (objects, error) -> Void in
if let objects = objects {
for object in objects {
self.citiesArray.append(object["city"] as! String)
}
}
}))
}
}
class TextRequestOperation: NSOperation {
typealias ResultBlock = ((result: String)->())
var _resultBlock: PFArrayResultBlock
var _query: PFQuery
init(query: PFQuery, resultBlock: PFArrayResultBlock) {
self._resultBlock = resultBlock
self._query = query
}
override func main()
{
if self.cancelled { return }
_query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if self.cancelled { return }
self._resultBlock(objects, error)
}
}
}
NSOperation is one option. ReativeCocoa is another option that could help you solve this problem quite easily if you know how to use it.
However the easiest way (and hackiest) would prob be to keep some state of the search and use it to only apply the most recent searches results.
var mostRecentSearchQuery: String = ""
#IBAction func textFieldChanged (sender: AnyObject) {
var queryString: String?
if let textField: UITextField = sender as? UITextField {
queryString = textField.text
self.mostRecentSearchQuery = textField.text
}
let query = PFUser.Query()
query!.whereKey("Postcode", containsString: searchField.text)
query?.findObjectsInBackgroundWithBlock({[weak self] (objects, error) -> Void in
if let objects = objects where queryString == self?.mostRecentSearchQuery {
for object in objects {
self.citiesArray.append(object["city"] as! String)
}
}
})
This will only update your results if block used the most recent text typed.
ps. I am assuming the sender passed into the method is the textField which text has changed.

Object added in Parse several times after checking for all non similar objects:

I am querying objects from Parse, that I then append into an array. I check if this array contains a particular object, if not, I wish to add this particular object to Parse.
However, for each different object in this array, and therefore in Parse, the particular object gets added.
For instance, if I have four different objects, the object in question will be added four times.
See my code below:
#IBAction func nextWeek(sender: AnyObject) {
self.view.layoutIfNeeded()
UIView.animateWithDuration(0.5, animations: {
self.viewHeight.constant = 93
self.view.layoutIfNeeded() }, completion: nil)
var array2 = [String]()
var query1 = PFQuery(className: "sendMessage")
query1.whereKey("messageSent", equalTo:PFUser.currentUser()!.username!)
query1.whereKey("messageReceived", equalTo:self.nameLabel.text!)
query1.findObjectsInBackgroundWithBlock { (objects1, NSError) -> Void in
if let objects1 = objects1 as? [PFObject] {
for object1 in objects1 {
if object1["message"] != nil {
var textMessage3: String = object1["message"] as! String
var message3 = Message(sender: PFUser.currentUser()!.username!, message: textMessage3, time: NSDate())
array2.append(message3.message)
if contains(array2, self.nextWeekButtonL.titleLabel!.text!) {
} else {
println(array2)
println("it does not contain")
var heyMessage: PFObject = PFObject(className: "sendMessage")
heyMessage["messageSent"] = PFUser.currentUser()?.username
heyMessage["messageReceived"] = self.nameLabel.text
heyMessage.setObject(self.nextWeekButtonL.titleLabel!.text!, forKey: "message")
heyMessage.save()
var message = Message(sender: PFUser.currentUser()!.username!, message: self.nextWeekButtonL.titleLabel!.text!, time: NSDate())
self.array.append(message)
self.tableView.reloadData()
} } } } } }
I have tried to use a dispatch async with main queue, as well as different solutions to add the object to Parse, but the "else" after the check if the array contains, is executed as many times as the array does not contain.
Any idea ?
Thanks a lot,

Swift Not Presenting All text Within Text Field

I am querying information from the parse database, and want to display all the data in a text field. My code prints everything into the output box, however the actual iOS simulator only displays one piece of the entire data. Would appreciate any suggestions!
#IBOutlet var Groupnames: UITextView!
#IBAction func QueryDataAction(sender: AnyObject) {
var query = PFQuery(className: "BeaterGroups")
query.findObjectsInBackgroundWithBlock { (object: [AnyObject]?, error: NSError?) -> Void in
if error == nil && object != nil {
if let object = object as? [PFObject] {
for objects in object {
println(objects.valueForKey("GroupName")!)
let groupname = objects.valueForKey("GroupName") as! String
self.Groupnames.text = groupname
}
}
}
}
}
You are probably only getting the text for the last object. You need to append to the text, not assign to it.
For example, initialize the text field to "" prior to entering the loop, then change the assignment line as follows as in this excerpt:
self.Groupnames.text = ""
for objects in object {
println(objects.valueForKey("GroupName")!)
let groupname = objects.valueForKey("GroupName") as! String
self.Groupnames.text = self.Groupnames.text + groupname + " "
}
This will separate the objects with a space; you can change the separator if you wish.

How to add PFObjects to a tableview in swift

I am building a checkin app, and am having trouble filling my tableview with guests stored using Parse. I get an error when trying to append the objects. I also have a user login that I followed from a Udemy course. In the course he showed how to display PFUsers, but I can't get it to work using PFObjects. Any help would be great on this.
Here is the working code with PFUsers.
var users = [""]
override func viewDidLoad() {
super.viewDidLoad()
var query = PFUser.query()
query!.findObjectsInBackgroundWithBlock({ (objects: [AnyObject]?, error: NSError?) -> Void in
self.users.removeAll(keepCapacity: true)
for object in objects! {
var user:PFUser = object as! PFUser
self.users.append(user.username!)
}
self.tableView.reloadData()
})
}
And here is the nonworking code with PFObjects.
var users = [""]
override func viewDidLoad() {
super.viewDidLoad()
var query = PFQuery(className: "TestObject")
query.findObjectsInBackgroundWithBlock({ (objects: [AnyObject]?, error: NSError?) -> Void in
self.users.removeAll(keepCapacity: true)
for object in objects! {
var guest = object as! PFObject
self.users.append(guest.foo!)
}
})
}
The error shows on the line
self.users.append(guest.foo!)
And the error says "'PFObject' does not have a member named 'foo'"
You define your PFUser object with the variable user, this will make the first example work (you get the name of the user) The second example doesn’t work cause you still define the PFObject as user but try to access the name of guest which is not defined.
You could either go with the first example or change
var user:PFObject = object as! PFObject
With
var guest:PFObject = object as! PFObject
Either way, it doesn’t matter for your code, it is just the name of the variable.
This explanation will fix your “Use of unresolved identifier ‘guest’”
But this isn’t your only problem,
the PFUser object which the first example uses is a special kind of a PFObject, the PFUser class does have a .name which refers to (obviously) the name of the user. Not every PFObject has a name though so you can’t just access the .name of a PFObject. Parse has an excellent documentation about retrieving objects I would first access this documentation. If this is still unclear to you, you can open another specific question about your new problem.
To retreive the data from an object you need to use []
Let’s suggest we have a class named gameScore with the following info
score: 1337, playerName: “Sean Plott”, cheatMode: false
We would do that as follows
var gameScore = PFObject(className:"GameScore")
gameScore["score"] = 1337
gameScore["playerName"] = "Sean Plott"
gameScore["cheatMode"] = false
gameScore.saveInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
if (success) {
// The object has been saved.
} else {
// There was a problem, check error.description
}
}
To retrieve the object you need to query (as you already did in your post)
Ones you’ve received the data you can extract it as follows:
let score = gameScore["score"] as Int
let playerName = gameScore[“playerName"] as String
let cheatMode = gameScore["cheatMode"] as Bool
I figured it out, I needed to get the object label as a string before I could append it to the array to then add it to the tableview.
Here is the working code:
var users = [""]
override func viewDidLoad() {
super.viewDidLoad()
var query = PFQuery(className: "TestObject")
query.findObjectsInBackgroundWithBlock({ (objects: [AnyObject]?, error: NSError?) -> Void in
self.users.removeAll(keepCapacity: true)
for object in objects! {
var foo = object.objectForKey("foo") as? String
self.users.append(foo!)
}
self.tableView.reloadData()
})
}

Resources