I am wanting to update the child values after editing inside the textfields.
At the moment I have this action:
#IBAction func updateAction(_ sender: Any) {
guard let itemNameText = itemName.text, let itemDateText = itemDate.text else { return }
guard itemNameText.characters.count > 0, itemDateText.characters.count > 0 else {
print("Complete all fields")
return
}
let uid = FIRAuth.auth()?.currentUser?.uid
let key = item.ref!.key
let itemList = Item(itemName: itemNameText, itemDate: itemDateText, uid: uid!)
let editItemRef = databaseRef.child("/usersList/\(key)")
editItemRef.updateChildValues(itemList.toAnyObject())
print("edited")
}
I was following this tutorial but he seems to use the username, and as I only have the email or uid (userID) as authentication I thought I'd use the uid.
This is my toAnyObject function inside my class:
func toAnyObject() -> [String: AnyObject] {
return ["itemName": itemName as AnyObject, "itemDate": itemDate as AnyObject, "userID": userID as AnyObject]
}
When I run the breakpoint it does show the edited value of the item however the update doesn't appear to be performing.
Just to be extra safe, try dropping the leading slash from your path:
databaseRef.child("usersList/\(key)")
…and try printing the Error returned by Firebase, if any:
editItemRef.updateChildValues(itemList.toAnyObject()) {
(error, _) in
if let error = error {
print("ERROR: \(error)")
} else {
print("SUCCESS")
}
Edit. We found out he was using the wrong database path. The right one is:
databaseRef.child("users").child(uid!).child("usersList/\(key)")
Related
EDIT:
I’m trying to add additional data into Cloud Firestore under the same UID. However, the new data deletes the old data and rewrites on it. Because as for the result later, I am desired to show every data of the same UID on my UI.
I have also checked on this example but it was in javascript and I am not sure on how to apply it on my code. Thank you.
this is my program code.
#IBAction func newBookTapped(_ sender: Any) {
guard let uid = Auth.auth().currentUser?.uid,
let data = bookData() else {
return
}
db.collection("bookData").document(uid).setData(data)
}
func bookData() -> [String: Any]? {
guard let title = bookTitleTextField.text,
let author = bookAuthorTextField.text,
let summary = bookSummaryTextField.text else {
return nil
}
let data: [String: Any] = [
"bookTitle": title,
"bookAuthor": author,
"bookSummary": summary
]
self.transitionToMenu()
return data
}
Saving data to Firebase and retrieving data to display in label is working but when I try to add an Int to the label it overwrites the label data.
I add to the label with
var pointsCount = 0
func addPoints(_ points: NSInteger) {
pointsCount += points
pointsLabel.text = "\(self.pointsCount)"
}
Then I save the label contents to Firebase as a string.
func saveToFirebase() {
let userID = Auth.auth().currentUser!.uid
let points: String = ("\(self.pointsCount)")
let savedScores = ["points": points,
"blah": blah,
"blahblah": blahblah]
Database.database().reference().child("users").child(userID).updateChildValues(savedScores, withCompletionBlock:
{
(error, ref) in
if let error = error
{
print(error.localizedDescription)
return
}
else
{
print("Data saved successfully!")
}
})
}
I retrieve the string from the Realtime Database and convert it to an Int.
func retrieveFromFirebase() {
guard let userID = Auth.auth().currentUser?.uid else { return }
Database.database().reference().child("users").child(userID).child("points").observeSingleEvent(of: .value) {
(snapshot)
in
guard let points = snapshot.value as? String else { return }
let pointsString = points
if let pointsInt = NumberFormatter().number(from: pointsString) {
let retrievedPoints = pointsInt.intValue
self.pointsLabel.text = "\(retrievedPoints)"
} else {
print("NOT WORKING")
}
}
The label displays the retrieved data from the database perfectly.
Then if I try to add more points to the label, it erases the retrieved data and starts adding from 0 as if the label displayed nil.
I've been searching for answers all day for what seems to be a rather simple problem but haven't been able to figure it out due to my lack of experience.
I have tried separating everything and saving the data as an integer and retrieving the data back as an integer but the issue seems to be from the addPoints function.
Please let me know what I'm doing wrong.
The solution ended up being as simple as adding an 'if' statement to the points function.
Instead of...
func addPoints(_ points: NSInteger) {
pointsCount += points
pointsLabel.text = "\(self.pointsCount)"
}
It needed to be...
func addPoints(_ points: NSInteger) {
if let text = self.pointsLabel.text, var pointsCount = Int(text)
{
pointsCount += points
pointsLabel.text = "\(pointsCount)"
}
}
I have the following case. The root controller is UITabViewController. There is a ProfileViewController, in it I make an observer that users started to be friends (and then the screen functions change). ProfileViewController can be opened with 4 tabs out of 5, and so the current user can open the screen with the same user in four places. In previous versions, when ProfileViewController opened in one place, I deleted the observer in deinit and did the deletion just by ref.removeAllObservers(), now when the user case is such, I started using handle and delete observer in viewDidDisappear. I would like to demonstrate the code to find out whether it can be improved and whether I'm doing it right in this situation.
I call this function in viewWillAppear
fileprivate func firObserve(_ isObserve: Bool) {
guard let _user = user else { return }
FIRFriendsDatabaseManager.shared.observeSpecificUserFriendshipStart(observer: self, isObserve: isObserve, userID: _user.id, success: { [weak self] (friendModel) in
}) { (error) in
}
}
This is in the FIRFriendsDatabaseManager
fileprivate var observeSpecificUserFriendshipStartDict = [AnyHashable : UInt]()
func observeSpecificUserFriendshipStart(observer: Any, isObserve: Bool, userID: String, success: ((_ friendModel: FriendModel) -> Void)?, fail: ((_ error: Error) -> Void)?) {
let realmManager = RealmManager()
guard let currentUserID = realmManager.getCurrentUser()?.id else { return }
DispatchQueue.global(qos: .background).async {
let specificUserFriendRef = Database.database().reference().child(MainGateways.friends.description).child(currentUserID).child(SubGateways.userFriends.description).queryOrdered(byChild: "friendID").queryEqual(toValue: userID)
if !isObserve {
guard let observerHashable = observer as? AnyHashable else { return }
if let handle = self.observeSpecificUserFriendshipStartDict[observerHashable] {
self.observeSpecificUserFriendshipStartDict[observerHashable] = nil
specificUserFriendRef.removeObserver(withHandle: handle)
debugPrint("removed handle", handle)
}
return
}
var handle: UInt = 0
handle = specificUserFriendRef.observe(.childAdded, with: { (snapshot) in
if snapshot.value is NSNull {
return
}
guard let dict = snapshot.value as? [String : Any] else { return }
guard let friendModel = Mapper<FriendModel>().map(JSON: dict) else { return }
if friendModel.friendID == userID {
success?(friendModel)
}
}, withCancel: { (error) in
fail?(error)
})
guard let observerHashable = observer as? AnyHashable else { return }
self.observeSpecificUserFriendshipStartDict[observerHashable] = handle
}
}
Concerning your implementation of maintaining a reference to each viewController, I would consider moving the logic to an extension of the viewController itself.
And if you'd like to avoid calling ref.removeAllObservers() like you were previously, and assuming that there is just one of these listeners per viewController. I'd make the listener ref a variable on the view controller.
This way everything is contained to just the viewController. It also is potentially a good candidate for creating a protocol if other types of viewControllers will be doing similar types of management of listeners.
func updateFirebase(){
myFun = thisIsMyFunTextView.text
IAm = iAmTextView.text
var profileKey = String()
profileRef.queryOrdered(byChild: "uid").queryEqual(toValue: userID).observe(.value, with:{
snapshot in
for item in snapshot.children {
guard let data = item as? FIRDataSnapshot else { continue }
guard let dict = data.value as? [String: Any] else { continue }
guard let profileKey = dict["profileKey"] else { continue }
self.profileRef.child(profileKey as! String).child("bodyOfIAM").setValue(IAm)
self.profileRef.child(profileKey as! String).child("bodyOfThisIsMyFun").setValue(myFun)
}
})
}
#IBAction func backButtonClicked(_ sender: Any) {
updateFirebase()
DispatchQueue.main.asyncAfter(deadline: .now() + 4, execute: {
self.dismiss(animated: true)
})
}
myFun and IAm are successfully defined by the changes to the textviews by the user. I can't extract the childByAutoID value without triggering this for in loop that does not end once called, continuing even as a new view controller is presented. The "bodyOfThisIsMyFun" vacillates between the old value and the new value during this loop while the "bodyOfIAM" gets correctly redefined right away and stays that way like it should. How do I get the extracted new values to replace the old values here?
I needed to add this line of code at the end of the for...in statement:
self.profileRef.removeAllObservers()
In a tableView I have a list of jobs. These jobs can be accessed by multiple users, therefore I need to use FIRTransaction. Based on the result of the first write to FirebaseDatabase, I need to write/not write to another path in Firebase.
The schema is as follows:
//Claim job by - Cleaner
//cleaner tries to claim booking at Users/UID/cus/bookingNumber
//if FIRMutableData contains a key ‘claimed’ or key:value “claimed”:true
//update at Users/UID/cus/bookingNumber with key:value
//based on response received write or not to another path
//write to Cleaners/UID/bookingNumber
//key:value
If the internet connection drops before client app receives response from firebase server, write to Cleaners/UID/bookingNumber will not be made.
How can I solve this problem?
#IBAction func claimJob(_ sender: Any) {
dbRef.runTransactionBlock({ (_ currentData:FIRMutableData) -> FIRTransactionResult in
//if valueRetrieved is nil abort
guard let val = currentData.value as? [String : AnyObject] else {
return FIRTransactionResult.abort()
}
self.valueRetrieved = val
guard let uid = FIRAuth.auth()?.currentUser?.uid else {
print("abort no uid line 80")
return FIRTransactionResult.abort()
}
self.uid = uid
for key in self.valueRetrieved.keys {
//unwrap value of 'claimed' key
guard let keyValue = self.valueRetrieved["Claimed"] as? String else {
print("abort line 88")
return FIRTransactionResult.abort()
}
//check if key value is true
if keyValue == "true"{
//booking already assigned show alert,stop transaction
self.alertText = "Booking already taken, please refresh table!"
self.alertActionTitle = "OK"
self.segueIdentifier = "unwindfromClaimDetailToClaim"
self.showAlert()
return FIRTransactionResult.abort()
} else {
//write the new values to firebase
let newData = self.createDictionary()
currentData.value = newData
return FIRTransactionResult.success(withValue: currentData)
}//end of else
}//end of for key in self
return FIRTransactionResult.abort()
}) {(error, committed,snapshot) in
if let error = error {
//display an alert with the error, ask user to try again
self.alertText = "Booking could not be claimed, please try again."
self.alertActionTitle = "OK"
self.segueIdentifier = "unwindfromClaimDetailToClaim"
self.showAlert()
//what if internet connection drops here or client quits app ????????????
} else if committed == true {
//write to Cleaners/UID/bookingNumber
//what if internet connection drops here or client quits app??????
self.cleanersRef.setValue(snapshot?.value)
self.alertText = "Booking claimed.Please check your calendar"
self.alertActionTitle = "OK"
self.segueIdentifier = "unwindfromClaimDetailToClaim"
self.showAlert()
}
}
}//end of claimJob button