Parse relation query error on swift - swift

I have 2 table on parse.com. i want to make friendship system.
first table User -> username, email ...
Friendship Table -> RequestFrom(Related User Table), RequestTo(Related User Table), status
and i use this code :
let usr = PFObject(className: "User")
let friend = PFObject(className: "Friends")
var relation = friend.relationForKey("RequestFrom")
relation.addObject(PFUser.object())
relation.query().findObjectsInBackgroundWithBlock { (objects , error ) -> Void in
for obje in objects! {
print(obje)
}
}
this code error.
"NSInternalInconsistencyException" with reason "Tried to save an object with a new, unsaved child.":
This is Friends Table
https://www.dropbox.com/s/ywfpl7di4i767o7/Screenshot%202015-12-31%2014.09.40.png?dl=0
User table is classic parse User class.
How can i solve this problem?

Related

How to add fields to existing records in CloudKit

I have a function to input a users name into a new recordName in CloudKit (shown below), however I also need to add the users 'score' into the same record.
Is there a way to insert another field "usersScore" by referencing its unique 'recordName' value?
/// INSERT NEW ENTRY TO DB.
func insertName(nameEntry: String) -> CKRecordID {
// Connects to the user database table.
let userTable = CKRecord(recordType: "User")
// Connects the Database to the public container.
let publicData = CKContainer.default().publicCloudDatabase
// Adds the users entry into the "name" field.
userTable["name"] = nameEntry as NSString
// If the record has saved or an error has occurred.
publicData.save(userTable) { (record, error) in
if let saveError = error {
//Error.
print("An error occurred in \(saveError)")
}else {
// Success.
print("Saved Record!")
}
}
// RecordName (ID) of the current user.
recordNameID = userTable.recordID
return recordNameID!
}
To enter multiple records it's better to useCKModifyRecordsOperation.
Here's how I did it...
func insertItems(nameEntry: String) {
// Creates a record name based on name passed into function.
let userNamesID = CKRecordID(recordName: "\(nameEntry)")
// Inserts name into the User table.
let userName = CKRecord.init(recordType: "User", recordID: userNamesID)
// Adds the users entry into the "name" field along with score and the amount of questions answered.
userName["Name"] = nameEntry as CKRecordValue
userName["Score"] = questionsCorr as CKRecordValue
userName["Questions_Answered"] = questionsAns as CKRecordValue
// Insert all records into the database.
publicDatabase.add(CKModifyRecordsOperation.init(recordsToSave: [userName], recordIDsToDelete: nil))
}

Relational query on PFObject

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.

Querying for Relation Data in a PFQuery Table View Controller in Swift

I am trying to retrieve the the current user's friends by accessing the "friends" column in Parse. The way I set the friends up is by having the user select users in another view that are then added to a Relation called "friends" in their User row in Parse. I can get the users to save as a Relation in Parse but I can't figure out how to actually get those Relationed Users to show up in the Friends Table View. Here is my current code:
override func queryForTable() -> PFQuery {
let currentuser = PFUser.currentUser()
// Start the query object
let query = PFQuery(className: "_User")
// Add a where clause if there is a search criteria
if FriendSearch.text != "" {
query.whereKey("username", containsString: FriendSearch.text)
}
// Order the results
query.orderByDescending("score")
// Return the query object
return query
}
How do I get the current user's related users to appear in the PFTable View Controller? Thanks
You don't create a new query (particularly using the private class name for users). Instead you get the relation from the user and ask the relation for its query. Once you have that you can add additional parameters to it and return it.
I figured out what I did wrong. I needed to make a value for the relation in the current user, then make that a query.
// Define the query that will provide the data for the table view
override func queryForTable() -> PFQuery {
**let currentuser = PFUser.currentUser()?.relationForKey("friends")
// Start the query object
let query = currentuser?.query()**
// Add a where clause if there is a search criteria
if FriendSearch.text != "" {
query!.whereKey("username", containsString: FriendSearch.text)
}
// Order the results
query!.orderByDescending("score")
// Return the qwuery object
return query!
}

cloudkit enter userID as reference of a new record (in code)

I am setting an app in which a user could create records. Let's say he/she is the owner of a chatroom. Therefore there is an attribute called "admin" in the chatroom record. This attribute takes a reference which is the ID of the owner.
Here is what I tried:
CKContainer.defaultContainer().fetchUserRecordIDWithCompletionHandler({
userRecordID, error in
if error != nil {
println("caca")
} else {
println("gettin close")
let UserRecordIDToStore = NSKeyedArchiver.archivedDataWithRootObject(userRecordID)
let iDString = userRecordID.recordName as String
daMainUser.setObject(iDString, forKey: "user")
}
})
When I pass iDString as above, and I create the record (the room), its admin reference is empty. Whether I did cast iDString as a String or not.
When I pass userRecordID directly, I get an error:
'CKRecordID' is not identical to 'CKRecordValue'
I have been looking everywhere but I cannot find more information about this.
Any help would be greatly appreciated. Thank you guys!
If your user field is set up as a CKReference field, then you should set it like this:
daMainUser.setObject(CKReference(recordID: userRecordID, action: CKReferenceAction.None), forKey: "user")
In your case you do not have to create a recordID because you already have it. Otherwise you had to create it with something like:
var recordID = CKRecordID(recordName: userRecordID.recordName)

How to access custom user table columns from Parse.com

How should I access columns that I've added to the User table when I have a currentUser object?
I have a PFUser.currentUser() and I want to access the nickname column that I added via the web interface.
Can I use the currentUser to get the data e.g.:
var nickname = PFUser.currentUser()["nickname"] as String
Or do I have to use a user query? e.g.:
var query = PFUser.query()
query.whereKey("username", equalTo:PFUser.currentUser().username)
var user = query.findObjects().first as PFUser
var nickname = user["nickname"]
If you added date to the column locally, then you have to use the first way as you wrote, or if you added date in the browser, or uploaded to parse.com some way, you have to use the second way.
I would like to give my two cents too. First of all, Daniel was right in saying that if you added the date in the browser or uploaded it to parse.com, you need to use the second way. This is an updated answer with iOS 9 and Xcode 7.2:
var query = PFUser.query()
query!.whereKey("username", equalTo:PFUser.currentUser()!.username!)
do {
user = try query!.findObjects().first as! PFUser
} catch {
print("Error finding user")
}
if user?["rankNumber"] as? Int == nil {
user!["rankNumber"] = 0
user!.saveInBackground()
} else {
print(user!["rankNumber"] as! Int)
}
If I did it any other way, xcode would give me an error saying "failing to unwrap optional". I hope this can help someone!