Passing the results from findObjectsInBackgroundWithBlock into a variable - ios

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.

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

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) ...
}

Retrieving user data from Parse "_User" class using "ObjectId"

i am trying to retrive a user's data to get the user info from the _User class using the object id. i used this :
var data:NSMutableArray = NSMutableArray()
func loadData() {
data.removeAllObjects()
var profileQuery:PFQuery = PFUser.query()!
profileQuery.getObjectInBackgroundWithId(userId, block: { (objects, error) -> Void in
if error == nil {
self.data.addObject(objects!)
}
})
println(userId) // this is the userId as String.
println(self.data) ********* // empty array.
}
i am getting an empty array data here.. I've tried this also but same thing's happening here too. :
var profileQuery:PFQuery = PFUser.query()!
profileQuery.whereKey("objectId", equalTo: userId)
profileQuery.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if let objects = objects {
for object in objects {
self.data.addObject(object)
}
}
}
Remember that "findObjectsInBackgroundWithBlock" happens async! You need to put any logic pertaining to the data inside of the block.
var profileQuery:PFQuery = PFUser.query()!
profileQuery.getObjectInBackgroundWithId(userId, block: { (objects, error) -> Void in
if error == nil {
self.data.addObject(objects!)
println(self.data) //shouldn't be empty.
}
})
Putting aside why you're using an NSMutableArray for a singular PFUser object—you're basically expecting to see self.data populated in the wrong location. The user would be added to it (assuming a successful retrieval), inside the closure. So do something like:
var data:NSMutableArray = NSMutableArray()
func loadData() {
data.removeAllObjects()
var profileQuery:PFQuery = PFUser.query()!
profileQuery.getObjectInBackgroundWithId(userId, block: { (objects, error) -> Void in
if error == nil {
self.data.addObject(objects!)
println(self.data) //...shouldn't be empty here
} else {
println("Error retrieving user: \(error.description")
}
})
}

How to put asynchronous Parse.com functions in a separate Class in Swift?

I am using Parse.com as my backend. I would like to put all functions/code related to accessing Parse.com in a single class that I can call from different ViewControllers.
Problem is that - since many of these functions from Parse.com are asynchronous, how does one return a value from these functions to update the UI?
For example, in the following function I am going and getting Earnings information of the current user. Since this function is using the asynchronous method findObjectsInBackgroundWithBlock from parse.com, I cannot really return anything from this function.
Is there a workaround to this problem? Currently, I am having to place this in function in the same ViewController class. (and hence having to repeat this same function in multiple viewControllers. Would like to have it in a single function only)
Only solution I see is to go to the synchronous method findObjects. Is there any other way?
func getcurrUserEarnings() {
/// Get details of currentUser from Earnings Class
///
/// :param - NSInterval
/// :returns - Int
func loadEarningsInfo() {
if (PFUser.currentUser() != nil) {
var query = PFQuery(className:"Earnings")
query.whereKey("user", equalTo: PFUser.currentUser()!)
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
// The find succeeded.
if let objects = objects as? [PFObject] {
for object in objects {
println(object.objectId)
//WANT TO UPDATE UI HERE WITH THE VALUES THAT WERE JUST RETURNED
}
}
} else {
// Log details of the failure
println("Error: \(error!) \(error!.userInfo!)")
}
}
}
}
}
You can use callback to pass in something.
For example:
func doSomething(callBack:(String)->())->(){
callBack("abc")
}
doSomething { (str:String) -> () in
println(str)
}
Also, do not forget to update UI on main thread
For example
func loadEarningsInfo(callBack:([PFObject])->()) {
if (PFUser.currentUser() != nil) {
var query = PFQuery(className:"Earnings")
query.whereKey("user", equalTo: PFUser.currentUser()!)
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
// The find succeeded.
if let objects = objects as? [PFObject] {
callBack(objects)
}
} else {
// Log details of the failure
println("Error: \(error!) \(error!.userInfo!)")
}
}
}
}
Then when you use
loadEarningsInfo { (objects:[PFObject]) -> () in
//Update UI with objects
}
You can also handle error in callback as well,I just post a simple example

Using parse to query info in Swift

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
}
})

Resources