Relational query on PFObject - swift

I have a PFObject, Account that contains an array of Users which are subclasses of PFUserss. The User then has a NSDictonary property, allowableApps, that's a NSDictionary of arrays, where they arrays contain PFObjects.
So as a structure:
Account
var users: [User]
which points to....
User
// Each key is an array of AllowApp
var allowableApps: NSMutableDictionary
which points to...
AllowableApp
var appName: String
var appURL: String
var isAllowed: Bool
I'm trying to fetch all of these relations down to AllowableApp in a single query. I've tried using the .includeKey like this:
accountQuery?.includeKey("users")
accountQuery?.includeKey("allowableApps")
which didn't work. I've also tried:
accountQuery?.includeKey("users.allowableApps.appName")
accountQuery?.includeKey("users.allowableApps.appURL")
accountQuery?.includeKey("users.allowableApps.isAllowed")
I try to populate a UITableView with all the AllowableApp objects but I get this error:
Key "appName" has no data. Call fetchIfNeeded before getting its value.
Which I understand, I need to fetch all of them before trying to access the appName property. (which I'm trying to set cellForRowAtIndexPath).
Here is my full query:
let currentUser = User.currentUser()
let accountQuery = Account.query()
accountQuery?.whereKey("primaryUser", equalTo: currentUser!)
accountQuery?.includeKey("users.allowableApps")
accountQuery?.getFirstObjectInBackgroundWithBlock({ (account, error) in
if (error != nil) {
completion(users: nil, error: error)
}
else {
let users = (account as? Account)!.users
completion(users: users, error: nil)
}
})
My thought right now is to just loop through all of the AllowableApp objects in viewDidAppear calling fetchInBackgroundWithBlock. Then once they are all loaded I reload the table data.
This seems realllly messy and a common problem. Is there a more elegant solution that I'm just not seeing?

From what i understand you have the following structure:
Account
Users (Array of User)
AllowsableApps (Array of AllowApps)
First of all change the NSMutableDictionary to Array. NSMutableDictionary is a key-value pairs and in parse you should create one field. So you can use Array of AllowApps and it will do the same effect.
In order to fetch all accounts and users in each of the account and allowable apps per user you need to build the following query:
// You can do it with sub classing if you want
let query = PFQuery(className: "Account")
query.includeKey("users.allowableApps")
query.findObjectsInBackgroundWithBlock {
(objects: [PFObject]?, error: NSError?) -> Void in
}
Now for your users array. If your users array is users that needs to login the app it's better to inherit from PFUser and not from PFObject because PFUser contains all the logic for handling users in your app.

Related

Array of Parse Pointers (Swift) - is This Possible

I am working on an app where the user is connected (in) multiple school classes. Since a student will be in more than one class, am I able to set an array of pointers to an individual user or is that not possible (maybe a relation is better)?
Here is my code:
let classPointerQuery = PFQuery(className: "Classes")
classPointerQuery.whereKey("class_name", equalTo: self.classNameTextField.text!)
let classQuery = PFQuery.orQueryWithSubqueries([classPointerQuery])
classQuery.findObjectsInBackgroundWithBlock({ (results: [PFObject]?, error: NSError?) -> Void in
if let objects = results {
for object in objects {
let userInfo = PFUser.currentUser()!
userInfo["my_classes"] = object
userInfo.saveInBackgroundWithBlock({ (success: Bool, error: NSError?) -> Void in
if error != nil {
spinningActivity.hideAnimated(true)
self.displayAlert("Error", message: error!.localizedDescription)
} else if success {
spinningActivity.hideAnimated(true)
self.dismissViewControllerAnimated(true, completion: nil)
} else {
spinningActivity.hideAnimated(true)
self.displayAlert("Something Went Wrong", message: "Please try again")
}
})
}
}
})
* Note: I also tried changing - userInfo["my_classes"] = object - to - userInfo["my_classes"] = [object] - and got an error, "invalid type for key my_classes, expected *Classes, but got array (Code: 111, Version: 1.12.0)"
What I am doing here is querying for the object of the class that I want - lets say the user wants to add the class "Physics" - the query queries for the "class_name" in the Parse class "Classes" and spits out the object. This object is then set the current user's "my_classes" -> a pointer object. I there a way, when the user wants to add "Calculus" that the pointer object in Parse will have 2 pointers instead of replacing the current pointer?
Thanks in advance for the help!
you cant store Pointers in array, you can store objectID in the array as string and do the query like that.... the general rule is that u use Pointers for 1:many relationships in database and Relations in many:many...
Update 1 - Saving objectID to Array in Parse
PFUser.currentUser()!.addObject(somePFObject.objectID!, forKey: "my_classes")
for queries you will than use containedIn
querySetup.whereKey("class", containedIn: array)

parse.com swift return values from class after query

I have a second class in parse where each row is linked to the user and stores an int number. This is already setup.
Now I want to check for the current user and get the int that is saved and put that in a label. Any Ideas?
you will need to query with constraints ("meaning that get data only for the currentUser"). Then you have two options to retrieve their data either use getfirstobjectinbackgroundwithblock() method which will get at least one row of data from parse. Or use findObjectsInBackgroundWithBlock() method which will return multiple rows from parse.
if in parse, the user is being saved as pointer use that line:
let userT = PFUser.CurrentUser() //<-- to get CurrentUser
if in parse, the user is being saved as string use that line
let userT = PFUser.CurrentUser().username //<-- to get CurrentUser
In this code, I use the getFirstObjectInBackgroundWithBlock method because I think you are only displaying one thing for the user, So if I am wrong use the second method findObjectsInBackgroundWithBlock
let query = PFQuery(className:"whateverNameYourClassIs")
let userT = PFUser.CurrentUser() //<-- to get CurrentUser
query.whereKey("NameOfUserColummInParse" equalTo:userT!)
query.getFirstObjectInBackgroundWithBlock { (object: PFObject?, error: NSError?) -> Void in
if error == nil
{
if let retreiveObject = object
{
let data = retreiveObject["IntValue"] as! Int //<-- IntValue supposed to be the name of your class column in parse where you want to retrieve the value.
}
}
})

PFQuery categorizing results into multilevel arrays

I am trying to do a multiple PFQuery and storing the data properly.
My plan is to query for group members in my Group
I am looking for the key member. It gives me an array of objectIds. Then I want to query in my _User class for their picture with key firstImage. Displaying the data is not the problem, just getting it the right way.
The data needs to be stored considering it's groups. This is what i tried so far:
let fetchGroupsQuery = PFQuery(className: "Group")
fetchGroupsQuery.whereKey("member", equalTo: objectID!)
fetchGroupsQuery.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if error != nil
{
}
else {
if let objects = objects {
self.memberPhotos.removeAll()
for object in objects {
if let member = object["member"] as? [String]
{
// returns array of objectIds
for user in member
{
// returns single ids
self.groupIds.append(user)
let photoQuery = PFUser.query()
photoQuery?.whereKey("objectId", equalTo: user)
photoQuery?.findObjectsInBackgroundWithBlock({ (results, error) -> Void in
if error != nil {
// ....
}
else {
self.memberPhotos.removeAll()
if let results = results
{
for result in results
{
if result["firstImage"] != nil
{
self.memberPhotos.append(result["firstImage"])
}
}
}
}})
}
}
My idea was to store the data within an array of arrays.
So that I can later go into array[indexPath.row] to get the array I need to loop through in order to get the right group pictures.
Any ideas how to solve this?
I would use two separate queries function:
1) to get their ID's
2) for their pictures.
For the first query: what you need to change?
Your objectID array should be of type of NSMutableArray because in order to fetch their info you should use the constraint whereKey containedIn not whereKey equalTo
and for that constraint you should downcast your NSMutableArray to type AnyObject.
Also change the Type of your groupIds to NSMutableArray, then append your array.
For the Second query:
Since your groupIds Type is already NSMutableArray just cast it to AnyObject,then use the whereKey containedIn
Note: It would be better to download all images before appending your
array. So you have the option of using struct or class(easier to
group data to its owner) then create an array of that data
structure.Where it because easier to populate your UI.

Retrieve User info from Parse

I am coding an app and have user upon the user logging in I would like to take them to their dashboard and display info regarding their account. I am trying to display the username in a label but am having issues retrieving it. I am using the following code:
func getData(){
var query: PFQuery = PFQuery(className: "User")
query.whereKey("username", equalTo: "actualUsername")
query.getFirstObjectInBackgroundWithBlock{
(object: PFObject!, error: NSError!) - >Void; in
if object != nil {
NSLog("Success!")
} else {
NSLog("something went wrong")
}
}
I am using swift and xcode7
You can use the currentUser property of the PFUser object. This returns the informations about the user who is currently logged in. Then you can access the username etc:
PFUser.currentUser.username
Your problem is that the class you're querying, "User", doesn't exist. It's actually "_User", though it is more proper to use var query : PFQuery = PFUser.query() (see this answer)
Christian isn't wrong, though. If you have a user signed in, PFUser.currentUser returns the current user. So create a user PFUser variable and assign PFUser.currentUser to it, then you can access the elements using user["username"]

How do you store a dictionary on Parse using swift?

I am very new to swift and I don't know Obj C at all so many of the resources are hard to understand. Basically I'm trying to populate the dictionary with PFUsers from my query and then set PFUser["friends"] to this dictionary. Simply put I want a friends list in my PFUser class, where each friend is a PFUser and a string.
Thanks!
var user = PFUser()
var friendsPFUser:[PFUser] = []
var friendListDict: [PFUser:String] = Dictionary()
var query = PFUser.query()
query!.findObjectsInBackgroundWithBlock {
(users: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
// The find succeeded.
println("Successfully retrieved \(users!.count) users.")
// Do something with the found objects
if let users = users as? [PFUser] {
friendsPFUser = users
for user in friendsPFUser{
friendListDict[user] = "confirmed"
}
user["friends"] = friendListDict //this line breaks things
user.saveInBackground()
}
} else {
// Log details of the failure
println("Error: \(error!) \(error!.userInfo!)")
}
}
To be clear, this code compiles but when I add
user["friends"] = friendListDict
my app crashes.
For those who might have this issues with. "NSInternalInconsistencyException" with reason "PFObject contains container item that isn't cached."
Adding Objects to a user (such as arrays or dictionaries) for security reasons on Parse, the user for such field that will be modified must be the current user.
Try signing up and using addObject inside the block and don't forget do save it!
It helped for a similar problem I had.