I am currently trying to implement a parse database into a Swift app. I am having trouble understanding how to use the data, when you query from parse. Here is a query I am using:
var query = PFQuery(className: "CompanyInfo")
query.findObjectsInBackgroundWithBlock({
(objects: [AnyObject]! , error: NSError!) -> Void in
if error == nil{
println("Successfully retrieved \(objects.count) specials.")
println(objects[0])
}else{
println(error)
}
})
So I know this works because it prints out all the data to the console.
Then when I do the objects[0] it prints out the first.
How would I use the objects to set data into my app? For instance, if I have a title section in my parse class CompanyInfo, how do I get that information for later on?
To get the objects as PFObjects just cast them..
query.findObjectsInBackgroundWithBlock({
(objects: [AnyObject]! , error: NSError!) -> Void in
var myPFObjects = objects as? [PFObject] // now you have your array of pfobjects
})
To get any attribute/column of a pfobject just call it like this
var aPFObject = myPFObjects[0]
var title = aPFObject["title"] as? String
A better way to do all these things is to subclass the pfobject and get them via class properties, which would make following code:
The subclass..
class CompanyInfo: PFObject, PFSubclassing {
var title: String? {
get {
return self["title"] as? String
}
set {
self["title"] = newValue
}
}
class func parseClassName() -> String! {
return "CompanyInfo"
}
}
and the code where you call the query:
var cpQuery = CompanyInfo.query()
cp.findObjectsInBackgroundWithBlock({
(objects: [AnyObject]! , error: NSError!) -> Void in
var myCompanyInfos = objects as? [CompanyInfo] //Directly cast them to your objects
for cp in myCompanyInfos {
println(cp.title) //print all the titles
}
})
Related
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 am making a running app and would like to have a viewController in which the user has running tips and facts randomly display on the field. I would like to query parse for the objectIds to then reference the id and assign the label the tip or fact. Currently I have hard coded the Ids into the app but I would like that array to contain the results from the query the code is as follows:
func GetObjectID(){
ObjectIDs = ["id1","id2","id3","id4","id5","id6","id7","id8"]
RandomID = Int(arc4random_uniform(UInt32(ObjectIDs.count)))
}
func TheInfo(){
GetObjectID()
var query : PFQuery = PFQuery(className: "FactsandTips")
query.getObjectInBackgroundWithId(ObjectIDs[RandomID]){
(ObjectHolder : PFObject?, error: NSError?) -> Void in
if (error == nil) {
self.fact = ObjectHolder!.valueForKey("Fact") as? String
self.tips = ObjectHolder!.valueForKey("Tips") as? Array
if(self.tips.count > 0){
self.factLabel.text = self.fact
self.Button1.setTitle(self.tips[0], forState: UIControlState.Normal)
self.Button2.setTitle(self.tips[1], forState: UIControlState.Normal)
self.Button3.setTitle(self.tips[2], forState: UIControlState.Normal)
self.Button4.setTitle(self.tips[3], forState: UIControlState.Normal)
}
} else {
print("There is something wrong!")
}
}
}
I am using swift, Xcode7, and parse as my backend
Below is the code I use to query a Parse table, retrieve all results and add it all into an array. I then use the array as the source for a pickerView.
var query:PFQuery = PFQuery(className: "Drivers")
query.addAscendingOrder("Name")
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
if let objects = objects as? [PFObject] {
for object in objects {
self.astrDrivers.append(object["Name"]! as! String)
}
}
self.pkvDriverTrack.reloadAllComponents()
} else {
print("Error: \(error) \(error!.userInfo)")
}
}
Note the line self.astrDrivers.append(object["Name"]! as! String). This is adding the Name column of each record to my self.astrDrivers array.
If you wanted to do retrieve multiple columns, your best bet is to create a custom object like below:
class ObjectNewFact:NSObject {
var column1:String = String() // You might want to choose more descriptive variable names (I just don't know what your column names are).
var column2:Int = Int()
// etc.
}
You could then create an array of ObjectNewFacts with a line like
var aFacts:[ObjectNewFact] = [ObjectNewFact]()
Then you could amend your routine to retrieve the data from Parse to:
var query:PFQuery = PFQuery(className: "Drivers")
query.addAscendingOrder("Name")
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
if let objects = objects as? [PFObject] {
for object in objects {
var NewFact:ObjectNewFact = ObjectNewFact()
NewFact.column1 = object["Column1"] as! String
NewFact.column2 = object["Column2"] as! Int
self.aFacts.append(NewFact)
}
}
self.pkvDriverTrack.reloadAllComponents()
} else {
print("Error: \(error) \(error!.userInfo)")
}
}
Now you have an array full of facts. You might want to go down this custom object approach because you can also include things like the Fact ID or how many times the fact has been shown (if you're keeping track of that sort of thing). It provides a more flexible solution for any changes in the future.
I hope this helped.
I want to get all items from my Parse.com table called Sticker, from a particular shop. My Sticker table has a column called shopId. So the obvious solution is this:
//get all stickers from one shop of category dress
var query = PFQuery(className:"Sticker")
query.whereKey("shopId", equalTo: "QjSbyC6k5C")
query.whereKey("category", equalTo: "DR")
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
// The find succeeded.
println("Successfully retrieved \(objects!.count) scores.")
// Do something with the found objects
if let objects = objects as? [PFObject] {
for object in objects {
println(object.objectId)
}
}
} else {
// Log details of the failure
println("Error: \(error!) \(error!.userInfo!)")
}
}
However that causes this error:
error: pointer field shopId needs a pointer value
I have seen a common solution for this seems to be to pass the query the actual object and not a string of the ID. Does this mean I have to first do a separate query to get the specific shop object, and then pass that to my query? Or is there a shorter way?
Here is my attempt to get the shop but it's causing this error:
Can only call -[PFObject init] on subclasses conforming to
PFSubclassing
var query1 = PFQuery(className: "Shop")
var shop1 = PFObject()
query1.getObjectInBackgroundWithId("QjSbyC6k5C") {
(shop: PFObject?, error: NSError?) -> Void in
shop1 = shop!
}
EDIT: So my solution was basically doing what the answer suggested. My code was this (Glamour is the name of the shop):
var shopQuery = PFQuery(className:"Shop")
shopQuery.getObjectInBackgroundWithId("QjSbyC6k5C") {
(glamour: PFObject?, error: NSError?) -> Void in
if error == nil && glamour != nil {
println(glamour)
//get all stickers from one shop of category dress
var query = PFQuery(className:"Sticker")
query.whereKey("shopId", equalTo: glamour!)
query.whereKey("category", equalTo: "DR")
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
// The find succeeded.
println("Successfully retrieved \(objects!.count) scores.")
// Do something with the found objects
if let objects = objects as? [PFObject] {
for object in objects {
println(object.objectId)
}
}
} else {
// Log details of the failure
println("Error: \(error!) \(error!.userInfo!)")
}
}
} else {
println(error)
}
}
I will leave this question here and maybe someone will answer with a comment: Is there any way to get the shop and give it class scope so that we do not have to nest the second query inside the success of the first query? Would that be more elegant?
You need to pass PFObject. change your code with following
PFObject *object = ...
var query = PFQuery(className:"Sticker")
query.whereKey("shopId", equalTo: "QjSbyC6k5C")
query.whereKey("category", equalTo: object);
I'm trying to pass the result from the query into the variable array
var petitions = [PFObject] = []
Then return that result. How I can do that in Swift?
func getPetitions(employeeId: String, employeeBusiness: String) -> [PFObject] {
var petitions: [PFObject] = []
var query = PFQuery(className:"Petitions")
query.selectKeys(["petitionDate", "availableFrom", "availableTo"])
query.whereKey("employeeId", equalTo:employeeId)
query.whereKey("employeeBusiness", equalTo:employeeBusiness)
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
if let objects = objects as? [PFObject] {
NSLog("Successfully retrieved \(objects.count) petitions.")
for object in objects {
petitions.append(object)
}
}
}
}
return petitions
}
The query is ansynchronusly passed which means query.findObjectsInBackgroundWithBlock might not (99% won't) be finished when you return your petitions.
You can do your stuff which you want to do directly in the function:
var petitions: [PFObject] = [] //class variable
func getPetitions(employeeId: String, employeeBusiness: String) {
var query = PFQuery(className:"Petitions")
query.selectKeys(["petitionDate", "availableFrom", "availableTo"])
query.whereKey("employeeId", equalTo:employeeId)
query.whereKey("employeeBusiness", equalTo:employeeBusiness)
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
if let objects = objects as? [PFObject] {
NSLog("Successfully retrieved \(objects.count) petitions.")
for object in objects {
self.petitions.append(object)
}
// update UI
// or just do whatever you want with the petitions.
}
}
}
}
Or you can write a block function:
func getPetitions(employeeId: String, employeeBusiness: String, block:PFArrayResultBlock) {
var query = PFQuery(className:"Petitions")
query.selectKeys(["petitionDate", "availableFrom", "availableTo"])
query.whereKey("employeeId", equalTo:employeeId)
query.whereKey("employeeBusiness", equalTo:employeeBusiness)
query.findObjectsInBackgroundWithBlock(block)
}
}
To call it:
getPetitions("employeeId", "employeeBusiness", block: {
(objects: [AnyObject]?, error: NSError?) -> Void in
// do whatever you want with your objects..
// update UI
// or just do whatever you want with the petitions.
})
Another way to solve this, is to make the query synchronusly:
func getPetitions(employeeId: String, employeeBusiness: String) -> [PFObject] {
var query = PFQuery(className:"Petitions")
query.selectKeys(["petitionDate", "availableFrom", "availableTo"])
query.whereKey("employeeId", equalTo:employeeId)
query.whereKey("employeeBusiness", equalTo:employeeBusiness)
var objects = query.findObjects()
if let petititons = objects as? [PFObject] {
return petititons
} else {
return [PFObject]() // return an empty pf object array. If you want, return nil.. but the return value must be [PFObject]? if you do this.
}
}
You misunderstand completion blocks. You can't return a result from an async call like this. Instead you need to make the function a void (no result) and then put the code that deals with the result in your completion block.
You should refactor your getPetitions method to take it's own completion block as a parameter. Once the async Parse call is done and you're code to append the items is done, you'd then invoke the provided block.
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()
})
}