How to add fields to existing records in CloudKit - swift

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

Related

Swift + Firebase. Accessing current user's document

My current firebase structure is Collection of Users which then have a subcollection of habits. For a given user, I want them to be able to add to their own collection of routines. however, running into an issue. When I run the function below, it just creates a separate user with a separate routine. How would I tie a new routine to a current authenticated user?
func addData(routineMsg: String){
let db = Firestore.firestore()
let user = db.collection("users").document()
let routine = db.collection("users").document("GzsHAHq1P0uXGdlYwF8P").collection("routines").document()
routine.setData(["id": routine.documentID, "routine": routineMsg]) { err in
if err != nil {
print((err?.localizedDescription)!)
return
}
}
}
Right now, the code shows how I hard-code it to a certain document (GzsHAHq1P0uXGdlYwF8P), but would like to be able to determine the document dynamically by user
let user = db.collection("users").document()
By not passing document() an argument, what you are doing is creating a new document reference with an auto-generated document ID. What you want to do is pass the method with a string that locates the user's document. Ideally, this would be the user's ID:
guard let uid = Auth.auth().currentUser?.uid else {
return
}
let userDocRef = db.collection("users").document(uid)
From there, to generate random document IDs in the subcollection, do what you were doing before:
func addData(routineMsg: String) {
guard let uid = Auth.auth().currentUser?.uid else {
return
}
let db = Firestore.firestore()
let userDocRef = db.collection("users").document(uid)
let routineDocRef = userDocRef.collection("routines").document()
routineDocRef.setData([
"id": routineDocRef.documentID,
"routine": routineMsg
]) { error in
if let error = error {
print(error)
}
}
}

How To Check if locally created ID does not exist in Firestore database, if it does, regerate ID and try again until Document doesn't alreadt exist

I am trying to create a unique identifier for a document in a Firestore database. This id is a session ID that needs to be unique, locally generated and not loads of characters long (like the automatically generated one). I am trying to write code that checks to see if the document with that ID already exists. If it does not then set the SessionID, if it does exist then randomly generate another ID and perform the check again. This should loop continue until no document exists with that ID and then the session can initialize. The issue is that I can't put this in a while loop as Firebase connectivity cannot handle this. Does anyone have any suggestions, or another way to achieve the same thing? Thank you in advance.
func setsessionid () {
sessionid = randomAlphaNumericString(length: 6)
let docRef = self.db.collection("strokedata").document(self.sessionid!)
docRef.getDocument { (document, error) in
if let document = document, document.exists {
} else {
let currentsession = self.sessionid!
self.sessionID.text = "SESSION ID: \(currentsession)"
let user = Auth.auth().currentUser
if let user = user {
self.userid = user.uid
}
print("Document does not exist")
}
}

How to delete from Firebase database

Hi i have followed this tutorial: https://www.youtube.com/watch?v=XIQsQ2injLo
This explains how to save and retrieve from the database, but not how to delete. I am wondering how to delete the database node that belongs to the cell that is being deleted. Thanks
Edit. Code updated for Swift 3 and Swift 4.
I'm always using the remove with completion handler:
static let ref = Database.database().reference()
static func remove(child: String) {
let ref = self.ref.child(child)
ref.removeValue { error, _ in
print(error)
}
}
So for example if I want to delete the following value:
I call my function: remove(child: "mixcloudLinks")
If I want to go deeper and delete for example "added":
I need to change the function a little bit.
static func remove(parentA: String, parentB: String, child: String) {
self.ref.child("mixcloudLinks").child(parentA).child(parentB).child(child)
ref.removeValue { error, _ in
print(error)
}
}
Called like:
let parentA = "DDD30E1E-8478-AA4E-FF79-1A2371B70700"
let parentB = "-KSCRJGNPZrTYpjpZIRC"
let child = "added"
remove(parentA: parentA, parentB: parentB, child: child)
This would delete just the key/value "added"
EDIT
In case of autoID, you need to save the autoID into your Dictionary to be able to use it later.
This is for example one of my functions:
func store(item: MyClassObject) {
var item = item.toJson()
let ref = self.ref.child("MyParentFolder").childByAutoId()
item["uuid"] = ref.key // here I'm saving the autoID key into the dictionary to be able to delete this item later
ref.setValue(item) { error, _ in
if let error = error {
print(error)
}
}
}
Because then I'm having the autoID as part of my dictionary and am able to delete it later:
Then I can use it like .child(MyClassObject.uuid).remove... which is then the automatic generated id.
we can store the randomly generated id as the user id in the database and retrieve that to delete
for e.g. :
let key = ref?.childByAutoId().key
let post = ["uid": key,
"name": myName,
"task": myTask]
ref?.child(key!).setValue(post)
in order to delete
setvalue of the id as nil
for e.g. :
var key = [string]()
ref?.child(key[indexPath.row]).setValue(nil)
key.remove(at: indexPath.row)
myArray.remove(at: indexPath.row)
myTable.reloadData()

Parse relation query error on 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?

CKQuery with CloudKit Issue

I'm very new to CloudKit and am simply trying to run the most basic query. I want to pull from RecordType "Users" and field type "Name" and have the name equal to my nameText label!
Please see my code below:
func getUserInformation() {
let Pred = NSPredicate(value: true)
let Query = CKQuery(recordType: "Namer", predicate: Pred)
let AgeOp = CKQueryOperation(query: Query)
AgeOp.database = self.Pub
AgeOp.recordFetchedBlock = {(record : CKRecord!) in
let Recorded : CKRecord = record!
self.nameText.text = Recorded.objectForKey("Name") as? String
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.Container = CKContainer.defaultContainer() as CKContainer
self.Pub = Container.publicCloudDatabase as CKDatabase
self.Container.accountStatusWithCompletionHandler {
accountStatus, error in
if error != nil {
print("Error: \(error?.localizedDescription)")
} else {
self.getUserInformation()
}
}
}
Users is a system table. You could add new fields to that, but it's advised not to do so. One of the main reasons is that you cannot query the Users recordType.
Besides that I see in your code that you query the recordType Namer and not Users. Is your question wrong or is your code wrong?
In the recordFetchedBlock you directly put the result in the .text. That block will be executed in a background thread. You should first go to the mainQueue. You could do that like this:
AgeOp.recordFetchedBlock = {(record : CKRecord!) in
NSOperationQueue.mainQueue().addOperationWithBlock {
let Recorded : CKRecord = record!
self.nameText.text = Recorded.objectForKey("Name") as? String
}
}
When there is no query match, then the .recordFetchedBlock will never be called. You should also set the .queryCompletionBlock
The query could return multiple records (if there are more people with the same name). In the recordFetchedBlock you should add records to an array. Then in the .queryCompletionBlock you could use that array.