Getting old value of field in Firestore with Swift 5 - swift

I'm trying to get the old value of a field when it is changed in Firestore. Is there any way to access what the previous value of a field is after it is changed? Here is my current code, I want to access the old nickName under .modified and print out what the new nickName is and also the old one.
db.collection("cities").whereField("state", isEqualTo: "CA").addSnapshotListener { querySnapshot, error in
guard let snapshot = querySnapshot else {
print("Error fetching snapshots: \(error!)")
return
}
snapshot.documentChanges.forEach { diff in
if (diff.type == .added) {
print("New city: \(diff.document.data())")
let nickName = myData["nickName"] as? String ?? ""
}
if (diff.type == .modified) {
let nickName = myData["nickName"] as? String ?? ""
}
if (diff.type == .removed) {
let nickName = myData["nickName"] as? String ?? ""
}
}
}

Unfortunately, that is not a feature of Firestore. What you can do is have another field oldNickName and using Firebase Functions, automatically update that when the nickName field is changed.
The best solution is storing nickName locally, so you can refer back to your local variable when nickName changes, accessing the newly updated one in the database and the previous nickName locally. Here is the updated code:
var nickNames = [String : String]()
db.collection("cities").whereField("state", isEqualTo: "CA").addSnapshotListener { snapshot, error in
guard error == nil, let snapshot = snapshot?.documentChanges else { return }
snapshot.forEach {
let document = $0.document
let documentID = document.documentID
let nickName = document.get("nickName") as? String ?? "Error"
switch $0.type {
case .added:
print("New city: \(document.data())")
nickNames[documentID] = nickName
case .modified:
print("Nickname changed from \(nickNames[documentID]) to \(nickName)")
nickNames[documentID] = nickName
case .removed:
print("City removed with nickname \(nickNames[documentID])")
nickNames.removeValue(forKey: documentID)
}
}
}
nickNames is a dictionary with key cityID and value nickName. This code is written in Swift 5.

Related

SiwftUI Firebase modifying variable inside snapshot change not working

The following is a function to add a listener to a query. Whenever a document is added/removed I make some changes on two arrays (one of the user Ids and one of the user details). As you can see I tried printing everything: I correctly receive the data whenever it is added/removed, I can retrieve the document ID I need but whenever I append it to the usersReqestedUIDs array it always prints it as empty, even if I try to append a random string in it. Why is that?
func addRequestedUsersSnapshot() {
let db = Firestore.firestore()
let userRef = db.collection("user").document(user.UID)
let userRequestedRef = userRef.collection("friends").whereField("status", isEqualTo: "request")
// First query to fetch all friendIDs
userRequestedRef.addSnapshotListener { querySnapshot, error in
guard let snapshot = querySnapshot else {
print("Error fetching snapshots: (error!)")
return
}
snapshot.documentChanges.forEach { diff in
print("ids : (self.usersReqestedUIDs)")
print("type : (diff.type)")
if diff.type == .added {
print("doc id (diff.document.documentID)")
self.usersReqestedUIDs.append("hello")
print("added (diff.document.data())")
print("ids : (self.usersReqestedUIDs)")
self.fetchUserDetailsByUID(uid: diff.document.documentID) { result in
switch result {
case let .success(user):
self.usersReqestedDetails.append(user)
case let .failure(error):
print(error)
}
}
}
if diff.type == .removed {
print("removed (diff.document.data())")
self.usersReqestedDetails.removeAll(where: { $0.UID == diff.document.documentID })
self.usersReqestedUIDs.removeAll(where: { $0 == diff.document.documentID })
}
if diff.type == .modified {
print("modified (diff.document.data())")
}
}
}
}

How to extract individual values from Firestore getDocument request using Swift

let db = Firestore.firestore()
let docRef = db.collection("users").document(result!.user.uid)
docRef.getDocument { (document, error) in
if let document = document, document.exists {
let dataDescription = document.data().map(String.init(describing:)) ?? "nil"
print("Document data: \(dataDescription)")
print()
} else {
print("Document does not exist")
}
}
print("Document data: \(dataDescription)") outputs the following:
Document data: ["uid": LjqBXo41qMStt89ysQ4I9hxla2h1, "firstname": Tim, "lastname": Dorsemagen]
How can I extract each value such as the uid, the firstname and the lastname from dataDescription?
There are several ways to accomplish this. One of the most easily understood would be to break the string down in sections. Assuming you only want the values rather than their keys:
let values = dataDescription
.dropFirst()
.dropLast()
.components(separatedBy: ",")
.map{$0.components(separatedBy: ":")}
.map{$0.last!.trimmingCharacters(in: .whitespaces)}
print(values) //["LjqBXo41qMStt89ysQ4I9hxla2h1", "Tim", "Dorsemagen"]
Firestore has everything needed to easily get components of a document. Here's an asynchronous example of reading a users name from a document and returning it
func getUserAsync() async -> String{
let usersCollection = self.db.collection("users") //self.db is my Firestore
let thisUserDoc = usersCollection.document("uid_0")
let document = try! await thisUserDoc.getDocument()
let name = document.get("name") as? String ?? "No Name"
return name
}
if you want to use Codable (advised! See Mapping Firestore Data), this works for printing the name (can also be combined with the above solution)
func readUser() {
let usersCollection = self.db.collection("users") //self.db is my Firestore
let thisUserDoc = usersCollection.document("uid_0")
thisUserDoc.getDocument(completion: { document, error in
if let doc = document {
let user = try! doc.data(as: UserCodable.self)
print(user.name) //assume the UserCodable object has a name property
}
}
}
or just a regular old read of a document and print the name
func readUser() {
let usersCollection = self.db.collection("users") //self.db is my Firestore
let thisUserDoc = usersCollection.document("uid_0")
thisUserDoc.getDocument(completion: { document, error in
let name = document?.get("name") as? String ?? "No Name"
print(name)
})
}
*note: no error checking and I am force unwrapping options. Don't do that.

Firebase query returns empty when data is there

I have this RTDB I am trying to search my users in, from a path called users -> UID -> and then the user key/values. One of them being "username". I want to append these to an array to return in my table view but no matter what I do, I keep getting back nothing.
var userRef = Database.database().reference(withPath: "users")
func queryText(_ text: String, inField child: String) {
print(text)
userRef.queryOrdered(byChild: child)
.queryStarting(atValue: text)
.queryEnding(atValue: text+"\u{f8ff}")
.observeSingleEvent(of: .value) { [weak self] (snapshot) in
for case let item as DataSnapshot in snapshot.children {
//Don't show the current user in search results
if self?.currentUser?.uid == item.key {
continue
}
if var itemData = item.value as? [String:String] {
itemData["uid"] = item.key
self?.resultsArray.append(itemData)
print(self?.resultsArray)
}
}
self?.tableView.reloadData()
}
}
Edit: I have verified I am able to print out the snapshot, I am just not getting the usernames added to my resultsArray. Anyone have a clue why?
bio = " dfdf";
displayname = chattest4;
email = "test#test.com";
"first_name" = chattest4;
followers = 0;
following = 0;
"join_date" = "June 28, 2021";
languages = English;
"last_name" = test;
location = "United States";
profileImageURL = "hidjkfsf";
username = chattest4;
So I found out the issue. Some of the values in my database were not strings, but I had coded it to only look for Strings. Username was stored after these non-string values, so it never reached it. I just changed the array to [String:Any] and then it worked!
if var itemData = item.value as? [String:Any]

How to query multiple fields with one value in Firebase?

I'm a newbie at firebase I have implemented a sample app that able to transfer point to each other after transfer success I also added two fields called "sender_name" and "receiver_name" but it's too difficult to get all transitions based on user login I found sample ways to do just add multiple where to it, its work fine if true both but that's not what I want I want whereOr like SQL as an example below
SELECT column1, column2, ...
FROM table_name
WHERE condition1 OR condition2 OR condition3 ...;
any solution help, please
func getUserTransition(){
// process
/*
1.get all transition from tm_members sender and receiver by current user login
2.
*/
guard let username = self.userSession?.username else {
return
}
print("username in user session : \(username)")
COLLECTION_TM_TRANSITIONS_UAT
.whereField("sender_name", isEqualTo: username)
.whereField("receiver_name", isEqualTo: username)
.getDocuments { documentSnapshot, error in
if error == nil {
guard let value = documentSnapshot?.documents else { return }
self.tmTransitions = value.map { (queryDocumentSnapshot) -> TmTransition in
let data = queryDocumentSnapshot.data()
let email = data["email"] as? String ?? ""
let is_sender = data["is_sender"] as? Bool ?? false
let point = data["point"] as? Int ?? 0
let username = data["username"] as? String ?? ""
let sender_id = data["sender_id"] as? String ?? ""
let receiver_id = data["receiver_id"] as? String ?? ""
let created_at = data["created_at"] as? Timestamp
let sender_name = data["sender_name"] as? String ?? ""
let receiver_name = data["receiver_name"] as? String ?? ""
print("username : \(email)")
return TmTransition(id: queryDocumentSnapshot.documentID, sender_id: sender_id, receiver_id: receiver_id, username: username, is_sender: is_sender, point: point, email: email,created_at: created_at,sender_name: sender_name,receiver_name: receiver_name)
}
}
else{
print("error during fetch data ")
}
}
}

How to find out if an attribute is in the table for a user?

I have a firebase table where I need to find out if an attribute exists in the table for a particular user. Table is structured like this:
Users
-Lf9xUh53VeL4OLlwqQo
username: "my#test.com"
price: "$100"
special: "No"
-L12345ff322223345fd
username: "my2#test.com"
special: "No"
I need to find out if the "price" has been added for a specific user. Can't seem to figure that one out!
In swift I need something like:
self.ref?.child("Users").queryOrdered(byChild: "username").queryEqual(toValue: username.text!).observe(.value, with: { (snapShot) in
if (snapShot.value! is NSNull) {
print("nothing found")
} else {
print("found it!")
print(snapShot)
let snapShotValue = snapShot.value as! [String:[String:Any]]
Array(snapShotValue.values).forEach { // error here if it doesn't exist
let price = $0["price"] as! String
self.userPrice.text = price
}}
})
But if the price doesn't exist I'm getting an error. Thanks for your help.
Use as? instead of as!
if let price = $0["price"] as? String {
print(price)
}
else {
print("No price")
}
Or shortly
self.userPrice.text = ($0["price"] as? String) ?? "No price"