TableView Duplicates and firebase search query - ios

I have two issues with the snippet below.
I have get duplication in my TableView.
When I search for something that returns a result, and I tried searching for another thing that returns a result, the existing result will be the one showing not the new result, for example:
a. I search for "ola" and my TableView returns a list of -> olabode, olatunde, olaide.
b. Then I searched for "bisi", if found matching query for "bisi" I still get the query result for "ola".
func searchView(_ searchView: AZSearchViewController, didTextChangeTo text: String, textLength: Int) {
self.resultArray.removeAll()
searchView.reloadData()
guard text != "" else {
return
}
AppFirRef.userRef.queryOrdered(byChild: "username")
.queryStarting(atValue: text)
.queryEnding(atValue: text + "\\uf8ff")
.observe(.value, with: { (snapshot) in
if (snapshot.value is NSNull) {
print("not found")
} else {
print("found")
self.resultArray.removeAll()
searchView.reloadData()
print("\(snapshot.value)")
for case let snap as FIRDataSnapshot in snapshot.children {
guard let value = snap.value as? [String : Any] else { continue }
//print("\(snap.key)")
print(value)
let user = LContact(value: value, searchUserId: snap.key)
self.set.add(user)
if let username = user.username{
self.resultArray.append(username.lowercased())
searchView.reloadData()
}
}
}
})
}

It might be too late, but change
.observe(.value, with: { (snapshot) in
...
)}
to
.observeSingleEvent(of: .value, with: { (snapshot) in
...
)}
see my answer to this question for the explanation; it applies to you as well because you are also appending to the array every time the data at your reference path changes (such as when someone on another device changes data in the userRef reference path.

Related

Firebase child query coming up nil after deleting a key then adding it back

I run the code below and using print statements the Database is saying the children are null
But in the Database there is definitely something there:
The only thing I can think of is I let a user delete a message (by deleting the -LoyeLv... key) but if they want to add it back they can. I keep a copy of the deleted key and just send it back to the database so that the message in still in sync. The only problem with that is even when I don't do it (as in my example) and I delete the message, come back and make a completely new one with a completely new key, it's still coming up as null?
How is this possible even though there are children there? Btw this is the first time this has ever happened to me.
Database.database().reference()
.child("favorite")
.child(uid) // TKAETTLWAuREMZXCvrVeZd8yIPw2
.queryOrderedByKey()
.queryLimited(toLast: 10)
.observeSingleEvent(of: .value, with: { [weak self](snapshot) in
print(snapshot.key) // prints TKAETTLWAuREMZXCvrVeZd8yIPw2
print(snapshot.value) // prints null
if snapshot.hasChildren() {
print("yay")
} else {
print("nay") // prints nay
}
// doesn't go past here
guard let children = snapshot.children.allObjects.first as? DataSnapshot else { return }
Update I just tried this below and it works fine but the above still doesn't work:
Database.database().reference()
.child("favorite")
.child(uid) // prints TKAETTLWAuREMZXCvrVeZd8yIPw2
.observeSingleEvent(of: .value, with: { [weak self](snapshot) in
print(snapshot.key) // prints TKAETTLWAuREMZXCvrVeZd8yIPw2
print(snapshot.value) // prints the json data
if snapshot.hasChildren() {
print("yay") // prints nay
} else {
print("nay")
}
I woder does deleting the key at that ref than adding the same key than deleting it again than adding a brand new key somehow throws the db off?
Update Again Instead of using .queryOrderedByKey() I changed it to use .queryOrdered(byChild: "timeStamp") and it works fine:
Database.database().reference()
.child("favorite")
.child(uid) // TKAETTLWAuREMZXCvrVeZd8yIPw2
.queryOrdered(byChild: "timeStamp")
.queryLimited(toLast: 10)
.observeSingleEvent(of: .value, with: { [weak self](snapshot) in
When deleting a key then adding the same exact key back to the db then querying by queryOrderedByKey() must cause some sort of internal problem that doesn't just mess up the key, it messes up the entire ref. I think what happens is after I delete the key, whatever internal tracking mechanism wipes it from the system. When I add it back it's no longer the same key but instead just a regular String with no internal meaning and that's why is says null. I'm asking it to query something with no meaning. This is a wild guess.
But I would like to know why the same problem occurs with brand new keys that have never been deleted and added back at that same ref?
Here's the code that #FrankvanPuffelen requested in the comments:
This is the vc that sends/deletes the data.
1- sendDataToFirebase()
2- deleteDataFromFirebase()
3- sendDataToFirebase()
Do it in that order
var someOtherId = "12345" // this id has nothing to do with firebase and might be something like "x778-248-000"
var snapKey: String?
var originalTimeStamp: Double?
func sendDataToFirebase() {
guard let uid = Auth.auth().currentUser?.uid else { return }
guard let fbKey = Database.database().reference().child("favorite").childByAutoId().key else { return }
let timeStamp = Date().timeIntervalSince1970
var favoriteDict = [String: Any]()
favoriteDict.updateValue(uid, forKey: "uid")
favoriteDict.updateValue(originalTimeStamp ?? timeStamp, forKey: "timeStamp")
var whichKeyToUse: String?
if let snapKey = self.snapKey {
whichKeyToUse = snapKey
} else {
whichKeyToUse = fbKey
}
var carDict = [String: Any]()
carDict.updateValue(originalTimeStamp ?? timeStamp, forKey: whichKeyToUse!)
let favoriteRef = "favorite/\(uid)/\(whichKeyToUse!)"
let carRef = "carType/\(someOtherId)/\(uid)"
var multiLocationDict = [String: Any]()
multiLocationDict.updateValue(favoriteDict, forKey: favoriteRef)
multiLocationDict.updateValue(carDict, forKey: carRef)
Database.database().reference().updateChildValues(multiLocationDict, withCompletionBlock: { (error, ref) in
if self.snapKey == nil {
self.snapKey = fbKey
}
if self.originalTimeStamp == nil {
self.originalTimeStamp = timeStamp
}
// if no error this 100% saves it the way it's supposed to
})
}
func deleteDataFromFirebase() {
guard let uid = Auth.auth().currentUser?.uid else { return }
guard let snapKey = self.snapKey else { return }
let favoriteRef = "favorite/\(uid)/\(snapKey)"
let carRef = "carType/\(someOtherId)/\(uid)"
var multiLocationDict = [String: Any]()
multiLocationDict.updateValue(NSNull(), forKey: favoriteRef)
multiLocationDict.updateValue(NSNull(), forKey: carRef)
Database.database().reference().updateChildValues(multiLocationDict, withCompletionBlock: { [weak self](error, ref) in
// if no error this 100% deletes what it's supposed to
})
}
2. This is the vc that reads the data, this is an entirely different vc and where the problem lies
func firstCheckIfCurrentUserExistsAtFavoriteRef() {
guard let uid = Auth.auth().currentUser?.uid else { return }
let favoriteRef = Database.database().reference()
.child("favorite")
.child(uid)
favoriteRef.observeSingleEvent(of: .value) { [weak self](snapshot) in
if !snapshot.exists() {
return
}
print(snapshot.key) // prints the key
print(snapshot.value) // *** the value prints fine here but in handlePagination it prints null ***
if snapshot.hasChildren() {
print("yay") // prints yay
} else {
print("nay")
}
self?.handlePagination(with: uid)
}
}
var startKey: String?
func handlePagination(with currentUserId: String) {
if startKey == nil {
Database.database().reference()
.child("favorite")
.child(currentUserId)
.queryOrderedByKey() // it works fine with this >>> .queryOrdered(byChild: "timeStamp")
.queryLimited(toLast: 10)
.observeSingleEvent(of: .value, with: { [weak self](snapshot) in
print(snapshot.key) // prints the uid
print(snapshot.value) // *** prints null but in firstCheckIfCurrentUserExistsAtFavoriteRef() it prints fine ***
if snapshot.hasChildren() {
print("yay")
} else {
print("nay") // prints nay
}
guard let children = snapshot.children.allObjects.first as? DataSnapshot else { return }
// ...
})
} else {
// it never makes it this far but it's the same code from above
Database.database().reference()
.child("favorite")
.child(currentUserId)
.queryOrderedByKey() // it works fine with this >>> .queryOrdered(byChild: "timeStamp")
.queryEnding(atValue: startKey!)
.queryLimited(toLast: 11)
.observeSingleEvent(of: .value, with: { [weak self](snapshot) in
}
I call firstCheckIfCurrentUserExistsAtFavoriteRef() in viewDidLoad
I have seen this issue before with persistence turned on - the issue is the data is partially sync'd but not fully.
For a quick test, try turning persistence off and re-running the query.
Database.database().isPersistenceEnabled = false
That's obviously not a long term solution but just for testing.
If you want to use persistence, you also need to ensure the data is kept fresh and always in sync. Here's the code
let postsRef = Database.database().reference(withPath: "posts")
postsRef.keepSynced(true)
The Firebase Realtime Database client automatically downloads the data
at these locations and keeps it in sync even if the reference has no
active listeners. You can turn synchronization back off with the
following line of code
postsRef.keepSynced(false)

Fetch first key from firebase database with swift 4 using ChildAdded

I'm trying to fetch the first key from my firebase database but for some reason nothing is being printed out. How can I get the first key from my firebase database using .childAdded
let userMessagesRef = Database.database().reference().child("user-message").child(uid).child(userId)
userMessagesRef.observe(.childAdded, with: { (snapshot) in
guard let first = snapshot.children.allObjects.first as? DataSnapshot else { return }
print(first)
This in incredibly easy if you literally are asking how to only ever get the first child of a node. Here's how to only get the first child of a /users node
func getFirstChild() {
let usersRef = self.ref.child("users")
usersRef.observeSingleEvent(of: .childAdded, with: { snapshot in
print(snapshot)
})
}
or
print(snapshot.key)
if you just want the key.
Or, if you want to use a query to do the same thing
func getFirstChildAgain() {
let usersRef = self.ref.child("users")
let query = usersRef.queryOrderedByKey().queryLimited(toFirst: 1)
query.observeSingleEvent(of: .value, with: { snapshot in
print(snapshot)
})
}
The child_added event is typically used when retrieving a list of items from the database. Unlike value which returns the entire contents of the location, child_added is triggered once for each existing child and then again every time a new child is added to the specified path. The event callback is passed a snapshot containing the new child's data. For ordering purposes, it is also passed a second argument containing the key of the previous child.
From: Read and Write Data on iOS
Per your requirements, this is possible in .value and childAdded.
var child_array = [String:String]
let userMessagesRef = Database.database().reference().child("user-message").child(uid).child(userId)
userMessagesRef.observe(.childAdded, with: { (snapshot) in
let value = snapshot.value as? String ?? "Empty String"
let key = snapshot.key
child_array[key] = value;
}) { (error) in
print(error.localizedDescription)
}
then:
if let first = child_array.first?.key {
print(first) // First Key
}
Big NOTE: child_added randomly collects this data, you should never use it to sort your data

Firebase database query by user name

My DB looks like this :
In my app, I have a search bar, at the time the user search I want to find all the users with the same name or starting with the same text.
So what I did was :
func seachUser(named : String){
usersRef.queryOrdered(byChild: "userName")
.queryStarting(atValue: named)
.observeSingleEvent(of: .value) { (snap) in
print(snap.value)
}
}
But, each time I get all the list of the users.
How can I fix it?
thanks
Your querying for a so-called open-ended range. I.e. if named is Jo, then you'll get all results starting with Jone and then every child node after that.
To only get user names starting with Jo, you'll want to close the range by adding queryEnding(atValue:. The trick here is to end at the last known character that you want, for which we typically use the highest known unicode character:
func seachUser(named : String){
usersRef.queryOrdered(byChild: "userName")
.queryStarting(atValue: named)
.queryEnding(atValue: named+"\uf8ff")
.observeSingleEvent(of: .value) { (snap) in
print(snap.value)
}
}
Try this may be helpful.
usersRef.child("users").queryOrdered(byChild: "userName").queryEqual(toValue: "userName like for ex Jone").observeSingleEvent(of: DataEventType.value) { (snapshot) in
if snapshot.value is NSNull {
return
}else{
for item in snapshot.children {
print(snapshot.value as! [String: AnyObject])
}
}
}

Convert Firebase Dictionary Data to Array (Swift)

This may be a simple answer, so apologies in advance, but I'm stuck because I'm still getting head wrapped around how Firebase works. I want to query a Firebase Database based on unix date data that is saved there and then take the related "Unique ID" data and put it into an array.
The data in Firebase looks like this:
posts
node_0
Unix Date: Int
Unique ID Event Number: Int
node_1
Unix Date: Int
Unique ID Event Number: Int
node_2
Unix Date: Int
Unique ID Event Number: Int
What I have so far is as follows. The query part seems to be working as expected. Where I'm struggling is how to put the "Unique ID Event Number" data into an array. This is the approach that seemed closest to success, which is based on this post, but I get an error that child has no member of "value".
// Log user in
if let user = FIRAuth.auth()?.currentUser {
// values for vars sevenDaysAgo and oneDayAgo set here
...
let uid = user.uid
//Query Database to get the places searched by the user between one and seven days ago.
let historyRef = self.ref.child("historyForFeedbackLoop/\(uid)")
historyRef.queryOrdered(byChild: "Unix Date").queryStarting(atValue: sevenDaysAgo).queryEnding(atValue: oneDayAgo).observeSingleEvent(of: .value, with: { snapshot in
if (snapshot.value is NSNull) {
print("error")
} else {
for child in snapshot.children {
if let uniqueID = child.value["Unique ID Event Number"] as? Int {
arrayOfUserSearchHistoryIDs.append(uniqueID)
}
}
}
})
} else {
print("auth error")
}
Any ideas are greatly appreciated!
Try using this:-
historyRef.queryOrdered(byChild: "Unix Date").queryStarting(atValue: sevenDaysAgo).queryEnding(atValue: oneDayAgo).observeSingleEvent(of: .value, with: { snapshot in
if let snapDict = snapshot.value as? [String:AnyObject]{
for each in snapDict{
let unID = each.value["Unique ID Event Number"] as! Int
arrayOfUserSearchHistoryIDs.append(unID)
}
}else{
print("SnapDict is null")
}
})
I ended up re-working how I read the Firebase data based on the approach outlined in this post. The actual working code I used follows in case it's helpful for someone else.
// Log user in
if let user = FIRAuth.auth()?.currentUser {
let uid = user.uid
// values for vars sevenDaysAgo and oneDayAgo set here
...
let historyRef = self.ref.child("historyForFeedbackLoop/\(uid)")
historyRef.queryOrdered(byChild: "Unix Date").queryStarting(atValue: sevenDaysAgo).queryEnding(atValue: oneDayAgo).observeSingleEvent(of: .value, with: { snapshot in
if (snapshot.value is NSNull) {
print("user data not found")
}
else {
for child in snapshot.children {
let data = child as! FIRDataSnapshot
let value = data.value! as! [String:Any]
self.arrayOfUserSearchHistoryIDs.append(value["Unique ID Event Number"] as! Int)
}
}
})
}

Firebase with Swift ambiguous use of observeEventType

I've been pulling my hair out because of this. Going to all the pages with related incidents and multiple tutorials I find nothing wrong with my code here, but somehow it only doesn't fail if I print out the values (which works) or assign them as! NSArray which then gives me an empty array.
a print of snapshot.value shows
( friend1,
friend2,
friend3
)
I've tried snapshot.value.values ... no dice.
I've tried playing with the unwrapping ... no dice.
and this is my final attempt:
friendsList = [String]()
ref.observeSingleEventOfType(.Value) { (snapshot) -> Void in
if snapshot.value is NSNull {
} else {
for child in snapshot {
self.friendsList.append(child.value)
}
}
}
which gives me the ambiguous thing again.
Just some coding errors
Remove: (snapshot)-> Void
Change: child in snapshot as snapshot is not a sequence, whereas snapshot.children is
I assume you want to store the friends name as a string and name is a key in your structure. So change self.friendsList.append(child.value) to
let name = child.value["name"] as? String
friendsList.append(name!)
Here's the corrected code:
var friendsList = [String]()
ref.observeSingleEventOfType(.Value, withBlock: { snapshot in
if snapshot.value is NSNull {
} else {
for child in snapshot.children {
let name = child.value["name"] as? String
friendsList.append(name!)
}
print("\(friendsList)")
}
})
ref.observeSingleEvent(of: .value, with: { (snapshot) -> Void in
})
Work for me, Xcode 8.3.2

Resources