Firebase is storing value even when user deleted it - ios

I am struggling with the problem of storing the data. I am writing app that uses the firebase for authentication and as the database. The application works as scooter sharing. When user taps on one of the markers and taps on reserve button scooter is reserved by sending string value. It goes something like this:
I am tapping on reserve button
I am sendnig check value and user ID to selected scooter record as userKey
I am waiting for server response (feedback)
If there is response start reservation
If user change their mind and click on cancel button I am clearing the userKey and changing the status from Reserved to Off.
And there is a problem.
Even when I clear the userKey in appropriate scooter the function responsible for reservation can be still executed. For example when I simulate the server and I send feedback command to previously reserved scooter below code can be still executed even if the userKey is empty string. It doesn't happened to scooters that was not reserved yet. The main code goes like this:
func fetchScooters(){
ref?.child("service").child("scooters").observe(.value, with: { (snapshot) in
if snapshot.exists(){
let array:NSArray = snapshot.children.allObjects as NSArray
self.skutery = []
for child in array{
let snap = child as! DataSnapshot
if let dictionary = snap.value as? [String: Any] {
let skuter = ScooterInformation()
skuter.name = dictionary["name"] as? String
skuter.state = dictionary["state"] as? String
skuter.latitude = dictionary["latitude"] as? String
skuter.longitude = dictionary["longitude"] as? String
skuter.battery = dictionary["battery"] as? String
skuter.engine = dictionary["engine"] as? String
skuter.start = dictionary["start"] as? CLong
skuter.userKey = dictionary["userKey"] as? String
self.skutery.append(skuter)
if skuter.userKey == self.userID{
self.hideAllScootersIfUserReserved(scooterInfo: self.skutery)
// THERE IS SOMETHING WRONG....
self.handle = snap.ref.child("feedback").observe(.value, with: { (feedback) in
if let feedback = feedback.value as? Bool{
if feedback && skuter.userKey == self.userID{
print("Feedback received")
self.coundDown.invalidate()
self.timeToConnect = 20
snap.ref.child("feedback").removeValue()
snap.ref.child("check").removeValue()
if self.wantToRunScooter {
self.startScooter()
} else if self.userIsReservedScooter{
self.setStatusAsReserved()
self.userIsReservedScooter = false
}
}
}
})
By hitting the cancel button this is executed:
ref?.child("service").child("scooters").child("\(scooterNumber!)").child("state").setValue("*oF&")
ref?.child("service").child("scooters").child("\(scooterNumber!)").child("userKey").setValue("")
The handle function can be still executed even if the skuter.userKey is not equal to User ID because skuter.userKey should be empty string.
I am struggling with this second day and have no idea what is wrong with this..
I will be very grateful for any help.
Thanks

When the user hits the cancel button, your code only changes the state and the userKey but it does nothing to change the feedback value that is going to come back as true. You need to add something to change the feedback:
ref?.child("service").child("scooters").child("\(scooterNumber!)").child("feedback").setValue(false)
Plus you have:
if feedback && skuter.userKey == self.userID{ .... }
Isnt feedback a Bool and skuter.userKey a String? How can a Bool && String == String ?

Related

How to get a specific string value out of a json response from firebase

I have this data structure and I can't extract the right value:
users
private
userID
birthday: "birthdayValue"
username: "nathan"
firstName: "Nathan"
etc...
I'm making a search feature in my app to search for users via their username through the firebase realtime database:
let reference = Database.database().reference()
if(searchText != ""){
reference.child("users").child("private").queryOrdered(byChild: "username").queryStarting(atValue: searchText).queryEnding(atValue: searchText + "\u{f8ff}").observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.value is NSNull{
//handles errors
return
}
else{
if let user = snapshot.value as? NSDictionary {
for child in user{
print(child.key)
print(child.value)
}
}
else{
//null
}
}
})
at the moment the two print statements are printing these two results in the console every time I search:
wnszfmHilqNl6PG9khWtWkKUPtF3
{
birthday = 100;
dateCreated = "1579543450313.94";
description = nil;
email = "email#email.com";
firstName = Nathan;
instagramLink = nil;
lastLogin = "1579543450313.988";
lastName = Ellis;
profilePicURL = "url";
twitchLink = nil;
username = nathan;
youtubeLink = nil;
}
Which is expected, it prints the usersID (the key) and the value of the snapshot as a NSDictonary. I'm only interested in getting the username, nothing else. How would I extract the username out of this firebase snapshot so I can add their username as a string to an array for my search controller?
Obviously it needs to be dynamic as the userID will always be different.
Would I need to change my data model?
Your child.value seems to be a dictionary as well, so you can access it by:
if let valueDict = child.value as? [String: AnyObject] {
if let username = valueDict["username"] as? String {
// append username to results
print(username)
}
}
To print just the username, the smallest possible change is:
print(resultsLocalArray["username"])
This will fine, but will still retrieve the entire user node to the client, which uses more bandwidth than strictly needed.
If you find yourself frequently needing just the username of a user, or maybe even a list of username values across users, you might want to consider storing a node with just user names. So something like:
users
userID: "nathan"
But in your current setup you only retrieve the node for a single user, so I doubt the bandwidth savings are worth the additional complexity.

Firebase - nested observeSingleEvent methods inside for loop Swift

I have been trying to fetch data from Firebase using Realtime database. I want to check the contacts in iPhone and then if any contact number matches with that of any number in "numbers" table in db, then I have to get the user_key from it and then using that key, I have to obtain the corresponding details from users table.
for number in numbers {
Database.database().reference().child("numbers/\(number)").observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists() {
let userKey = snapshot.value as! String
// We found this user, no determine there name, (TODO has_image?)
Database.database().reference().child("users/\(userKey)/public/name").observeSingleEvent(of: .value, with: { (namesnapshot) in
if namesnapshot.exists() {
let name = namesnapshot.value as! String
print("FOUND \(name)")
complete(.success((userID: userKey, name: name)))
}
})
} else {
if numbers.index(of: number) == numbers.count - 1 { // Last Number checked and not found yet, so fail
complete(.failure(UserApiError.UserNotFound))
}
}
})
}
numbers is the array of contact numbers for a particular contact. For a contact having single number, this works fine. But for contacts having multiple numbers, the
Database.database().reference().child("users/\(userKey)/public/name").observeSingleEvent(of: .value, with: { (namesnapshot) in
will call after some time due to which the next index in the for loop gets called. So even if I have the data in first number in a contact, it will return failure because the next number will be iterated before the success of the observeSingleEvent.
I have been sitting for hours now, no ideas left with me. Please help!
I think a better approach is:
1 - Get all numbers from DB.
2 - Get all contact numbers that exists on DB.
3 - Finally, get the name of that contacts.(Exactly the way you are doing).
OBS: To do that you must change your DB. Your numbers must be saved as key-value pair. For exemple "555-0000" : true.
Database.database().reference().child("numbers").observeSingleEvent(of: .value, with: { (snapshot) in
guard let numbersFromDB = snapshot.value as? [String: Any] else{
print("Fail get numbers from db")
}
let numbersMatchedOnDB = numbersFromDB.keys.filter{ numbers.contains($0) }//get numbers from contact that exist on DB.
if numbersMatchedOnDB.isEmpty{
complete(.failure(UserApiError.UserNotFound))
}
else{
//For each contact number that exist on DB. it gets its name.
numbersMatchedOnDB.forEach{ numberMatchedOnDB in
Database.database().reference().child("numbers/\(numberMatchedOnDB)").observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists() {
let userKey = snapshot.value as! String
// .... nothing changed here ....
}
})
}
}
})

Pagination in firebase with identical child values

My data structure is as follows:
users:
user1:
-carModel: evo x
-username: importguy
-region: north east
user2:
-carModel: evo x
-username: evoguy
-region: north east
user3:
-carModel: mustang gt
-username: muscleguy
-region: south east
I want the user to be able to search for a car, say evo, and display results of users who own those particular cars. I need to paginate these results for my app. The problem is I can't figure out how to properly query this. Here is what i have so far.
func fetchUsersBy(car: String) {
if self.carCurrentKey == nil {
let ref = USER_REF.queryOrdered(byChild: "carModel").queryStarting(atValue: car).queryLimited(toFirst: 3)
ref.observeSingleEvent(of: .value, with: { (snapshot) in
guard let snap = snapshot.children.allObjects as? [FIRDataSnapshot] else { return }
guard let last = snapshot.children.allObjects.last as? FIRDataSnapshot else { return }
snap.forEach({ (snapshot) in
guard let userDict = snapshot.value as? Dictionary<String, AnyObject> else { return }
guard let carModel = userDict["carModel"] as? String else { return }
if carModel.contains(car) {
print(snapshot)
}
})
self.carCurrentKey = last.key
self.carCurrentValue = last.childSnapshot(forPath: "carModel").value as? String
})
} else {
// where to start next query?
let ref = USER_REF.queryOrder(byChild: "carModel").queryStarting(atValue: self.carCurrentValue)
}
}
I have to order the query by carModel, in order to group all of the users with that particular car type together in a snapshot. Since all the car models are the same value, I cannot figure out where to start or end the next query for the pagination. Using the reference i have in the else block starts the query at the same place as the block above. Any help or advice would be much appreciated.
I considered doing a fan out, and making a separate structure for car types. This would be difficult though.
For both startAt() and endAt(), you can pass a second value, childKey as shown here.
So your query will look something like this:
let ref = USER_REF.queryOrdered(byChild: "carModel").queryStarting(atValue: self.carCurrentValue, childKey: self.carCurrentKey).queryLimited(toFirst: 3+1)
Note that I used toFirst: 3+1. That's because, annoyingly, startAt() is inclusive and there's no way to skip the first record. So, since we started from the last record retrieved on the previous page, you will want to query for one extra record and discard the first result.
Here's a more complete example in JavaScript. Not familiar enough to translate this to Swift, but it should give you the algorithm in completion.
class Cursor {
constructor(baseRef, pageSize) {
this.baseRef = baseRef;
this.lastKey = null;
this.lastValue = null;
this.pageSize = pageSize;
}
next() {
let ref = this.baseRef;
if( this.lastValue !== null ) {
// a previous page has been loaded so get the next one using the previous value/key
// we have to start from the current cursor so add one to page size
ref = ref.startAt(this.lastValue, this.lastKey).limitToFirst(this.pageSize+1);
}
else {
// this is the first page
ref = ref.limitToFirst(this.pageSize);
}
return ref.once('value').then(snap => {
const keys = [];
const data = []; // store data in array so it's ordered
snap.forEach(ss => {
data.push(ss.val());
keys.push(ss.key);
});
if( this.lastValue !== null ) {
// skip the first value, which is actually the cursor
keys.shift();
data.shift();
}
// store the last loaded record
if( data.length ) {
const last = data.length - 1;
this.lastKey = keys[last];
this.lastValue = data[last].author;
}
return data;
});
}
}
And here's a working fiddle.
Keep in mind that this is a realtime data stream. So pagination is tricky. It's generally easier to just do infinite scroll than to try and maintain a realistic cursor on a moving data set (records can reorder when data changes, get deleted, added in the middle, etc).

Couchbase lite, Search query taking very long time

When I try to search the couchbase documents of size around 10K, the searching is taking very long time. Below are the code snippet. Can anyone optimize it or suggest me any alternative approach. Thank you.
1) Search function
func search(keyword:String) -> [[String:AnyObject]] {
var results:[[String:AnyObject]]=[]
let searchView = database.viewNamed(AppConstants().SEARCH)
if searchView.mapBlock == nil {
startIndexing()
}
let query = searchView.createQuery()
var docIds = Set<String>()
let result = try query.run()
while let row = result.nextRow() {
let key = "\(row.key)"
let keyArr = keyword.characters.split(" ")
for (index, element) in keyArr.enumerate() {
let keyItem = String(element)
if key.lowercaseString.containsString(keyItem.lowercaseString) {
let value = row.value as! [String:AnyObject]
let id = value["_id"] as? String
if id != nil && !docIds.contains(id!) {
results.append(value)
docIds.insert(id!)
}
}
}
}
}
2) Indexing
func startIndexing() {
let searchView = database.viewNamed(AppConstants().SEARCH)
if searchView.mapBlock == nil {
searchView.setMapBlock({ (doc, emit) in
let docType = doc[AppConstants().DOC_TYPE] as! String
if AppConstants().DOC_TYPE_CONTACT.isEqual(docType) {
self.parseJsonToKeyValues(doc)
for value in self.fields.values {
emit(value, doc)
}
self.fields.removeAll()
}
}, version: "1")
}
}
self.parseJsonToKeyValues(doc) will return me the key value store of my documents to index.
You're emitting the entire document along with every field for your view. This could easily cause your queries to be slow. It also seems unlikely you want to do this, unless you really need to be able to query against every field in your document.
It's considered best practice to set your map function right after opening the database. Waiting until right before you query may or may not slow you down.
See https://developer.couchbase.com/documentation/mobile/current/guides/couchbase-lite/native-api/view/index.html for more, especially the section labeled "Development Considerations".

Xcode internal error when a certain line of code is insert

I've been hunting down the source of this bug for a couple hours and I've found the line that causes it. The error is Xcode saying "An internal error occurred. source editor functionality limited" and all the syntax highlighting stops working. The error occurs whenever I typed about five or six keys or hit enter a couple of times.
After commenting and uncommenting code to find the problem, it turns out to be this line:
snapshot.value["string here"]
The weirdest thing is that I'm using that code here
// query server for a snapshot of current user's schedule
FirRef.child("\(user!.school)/students").queryOrderedByChild("email")
.queryEqualToValue("(userEmail!)").observeSingleEventOfType(.Value, withBlock: { snapshot in
if let data = snapshot.value as? [String: AnyObject]
{
// create new user model
let newUser = UserModel()
newUser.email = data["email"] as! String
newUser.school = data["school"] as! String
newUser.userId = data["userId"] as! String
newUser.title = ""
newUser.firstName = data["firstName"] as! String
newUser.lastName = data["lastName"] as! String
newUser.firstPeriod = data["firstPeriod"] as! String
newUser.secondPeriod = data["secondPeriod"] as! String
newUser.thirdPeriod = data["thirdPeriod"] as! String
newUser.fourthPeriod = data["fourthPeriod"] as! String
newUser.fifthPeriod = data["fifthPeriod"] as! String
newUser.sixthPeriod = data["sixthPeriod"] as! String
newUser.seventhPeriod = data["seventhPeriod"] as! String
}
})
and theres no issue but when I use this
// query server for snapshot of any teachers in the user's school
FirRef.child("\(user!.school)/teachers").observeSingleEventOfType(.Value, withBlock: { snapshot in
// iterate through all teachers found
for teacher in snapshot.children
{
// as soon as this line is put in
// Xcode wants to break itself
let thisBreaksStuff = teacher.value["exampleValue"]
}
The code runs perfectly well and retrieves the data I want from the server with no problem. But Xcode just doesn't like it.
Yes I have cleaned the project multiple times, restarted Xcode, restarted my computer. I've even deleted the derived data from Xcode as some people say to do

Resources