Firebase Swift .exist() not working - swift

Hello I am using firebase on swift and having an issue with .exist()
I am trying to do a query and check for a value, if its there I do nothing, if it isn't I add it to the list. I am just trying to avoid duplicating data this way. Heres the code:
InfoCenter.ref.child("users/\(InfoCenter.userId)/following").queryOrderedByValue()
.queryEqualToValue(firstTextField.text)
.observeEventType(.Value, withBlock: { snapshot in
if snapshot.exists(){
self.displayAlert("You already follow that person!", Title: "Whoops")
print(snapshot.value!)
} else {
InfoCenter.ref.child("users/\(InfoCenter.userId)/following").childByAutoId().setValue(TheId)
InfoCenter.ref.child("users/\(TheId)/followers").childByAutoId().setValue(InfoCenter.userId)
print(snapshot.value!)
}
})
so to me everything looks right, but when it runs snapshot.exist() always returns false but when I print snapshot.value! I get null with arrows around it (I couldn't type the arrows because SO thought it was a tag then) . So I'm confused.. how is null considered exists? Can someone please show me what to change to fix this? Thanks!!
Edit:
To be clear following is a list of users. So with in following are autoId's that have links to other users. The whole purpose of the above query is to go through the autoId's and make sure that this person does not already follow that person. Here is a snapshot of the data structure I am trying to explain:

Can I suggest an alternative? This solution reads the value as the location in question. Big benefit here is that there's no query overhead.
Assume we want to see if we follow frank and if not, follow him.
let ref = InfoCenter.ref.child("users/\(InfoCenter.userId)/following")
ref.observeSingleEventOfType(.Value, withBlock: { snapshot in
if let person = snapshot.value as? String {
if person == "frank" {
print("you are following frank: creeeeepy")
} else {
print("you are not following frank, follow him")
}
} else {
print("node doesnt exist")
}
})
this will directly read the value at
users/some_user_id/following: "the value that's read (frank in this case)"
Edit: based on an updated question, the 'following' node should look like
users
your_uid
following
some_user_you_are_following_uid: true
another_user_you_are_following_uid: true
then you are simply checking to see if the path exists
let ref = InfoCenter.ref.child("users/\(InfoCenter.userId)/following")
let theUserRef = ref.child("some_user_you_are_following_uid")
theUserRef.observeSingleEventOfType(.Value, withBlock: { snapshot in

I think your query might not be be working user InfoCenter.userId might be an optional force unwrap and see if that return the snapshot.

Related

Firebase look in child(uid) for child(currentUid) swift

I want to look into a child to look for another child. I want to find my current Uid in someone elses uid. How do I do that?
guard let currentUid = Auth.auth.currentUser?.uid else { return }
guard let (this is missing) = ... else { return }
Database.database().reference().child("users-following").child(" ... ").child(currentUid)
The "this is missing" and child("...") indicate, where I am not getting anywhere. Can someone help me?
I want to look in this child("…") to check and see if my currentUid is in that list.
______ UPDATE _______
"user-followers : {
"YsBqvPlRGMfx..." : {
"iFjaXB7lI..." : 1 
}
},
iFjaXB... is the user, that deletes the account. All his data is being deleted as it's supposed to. But the id stays within the YsBq child in the "user-followers". So, I want to delete the child "iFjaXB" in the child of "YsBq". How do I do this?
I got things going. This is how:
Database.database().reference().child("here you put the one, that stores your currentUid".child(currentUid).observe(.childAdded) { (snapshot) in
let key = snapshot.key
Database.database().reference().child("user-followers).child(key).child(currentUid).removeValue()

Annoying issue regarding indexOn in Firebase rules

I have read practically every StackOverflow answer and none of them worked for my scenario since this is a frequent issue. My Xcode console is giving a very common warning when querying for data in Firebase. That warning is Using an unspecified index. Your data will be downloaded and filtered on the client. Consider adding ".indexOn": "username" at /users to your security rules for better performance
What I have tried was to first read the Firebase documentation understanding exactly what I am doing along with other answers as stated such as Why does this Firebase ".indexOn" not work?. Below, I have provided my security rules doing exactly as the message prompt; adding indexOn at /users but to no success. I have also provided my database users node and one function below.
{
"rules": {
".read": true,
".write": true,
"users":{
".indexOn": "username"
}
}
}
My Firebase database at /users in JSON format
"users":{
"5LYUynelLTcL8Bg9WNWGXV34YIq2" {
"email": "user1#gmail.com"
"username": "user1"
}
"9srk307kzxOW7j6dNmMaac9eYPu2" {
"email": "user2#gmail.com"
"username": "user2"
}
My function that I use in Swift
let ref = Database.database().reference().child("users").queryOrdered(byChild: "username").queryEqual(toValue: passedInFriendString)
ref.observeSingleEvent(of: .value) { (snapshot) in
print(snapshot)
}
I'm not sure where else to turn to. Any insight on if this is even the correct format to query for what I want would be great!
I solved it!
The thing I did not realize was that the above code in my question DOES print the snapshot successfully after adding the appropriate index definition on /users. My issue was realizing you need to loop through the snapshot if you want to further access data of the username snapshot I was querying for accordingly. That would look something like this
let ref = Database.database().reference().child("users").queryOrdered(byChild: "username").queryEqual(toValue: passedInFriendString)
ref.observeSingleEvent(of: .value) { (snapshot) in
for child in snapshot.children {
guard let snap = child as? DataSnapshot else {return}
if snap.hasChild("email") {
print("It has an email")
}
}
}
Thanks to Frank above for guiding me and confirming that I was on the right track. I'm happy to have learned something new and super efficient whenever I need to grab data.

Firebase Checking If Username is Taken [duplicate]

Okay I am reading from a database and when I print the individual variables they print out correctly. However it seems like the data refuses to append to the array. Anyone know why? I can't figure it out at all.
let commuteBuilder = Commutes()
Database.database().reference().child("Users").child(user).child("Trips").observe(DataEventType.childAdded, with: { (snapshot) in
//print(snapshot)
if let dict = snapshot.value as? NSDictionary {
commuteBuilder.distance = dict["Distance"] as! Double
commuteBuilder.title = dict["TripName"] as! String
commuteBuilder.transportType = (dict["Transport"] as? String)!
}
commuteArray.append(commuteBuilder)
})
print("helper")
print(commuteArray.count)
return commuteArray
The data is correctly added to the array, just not at the time that you print the array's contents.
If you change the code like this, you can see this:
let commuteBuilder = Commutes()
Database.database().reference().child("Users").child(user).child("Trips").observe(DataEventType.childAdded, with: { (snapshot) in
if let dict = snapshot.value as? NSDictionary {
commuteBuilder.distance = dict["Distance"] as! Double
commuteBuilder.title = dict["TripName"] as! String
commuteBuilder.transportType = (dict["Transport"] as? String)!
}
commuteArray.append(commuteBuilder)
print("added one, now have \(commuteArray.count)")
})
print("returning \(commuteArray.count)")
return commuteArray
You'll see it print something like this:
returning 0
added one, now have 1
added one, now have 2
etc.
This is likely not the output you expected. But it is working as intended. Firebase loads data from its database asynchronously. Instead of blocking your code, it lets the thread continue (so the user can continue using the app) and instead calls back to the code block you passed to observe when new data is available.
This means that by the time this code returns the array it is still empty, but it later adds items as they come in. This means that you cannot return data from a function in the way you are trying.
I find it easiest to change my way of thinking about code. Instead of "First get the data, then print it", I frame it as "Start getting the data. When data comes back, print it".
In the code above, I did this by moving the code that prints the count into the callback block. Instead of doing this, you can also create your own callback, which is called a completion handler or closure in Swift. You can find examples in this article, this article, this question Callback function syntax in Swift or of course in Apple's documentation.

How can I read the value of a Firebase database dictionnary created using .childByAutoId() in Swift?

I have a dictionary of dictionary of Strings stored in a Firebase database. It can be seen below.
As you can see, each entry is created using .childByAutoId() and contains two variables: text and tag.
I wish to be able to go through all of the entries, and compare the value of text with a variable saved locally in my app. I have tried many ways, but cannot find any solution that works. How should I proceed?
Thank you in advance for your help.
You need to observe database at specific reference and then convert a snapshot that will be send to you. The snapshot represents a fragment of your database at given path
let dbRef = Database.database().reference().child("messages")
dbRef.observeSingleEvent(of: .value) { (snapshot) in
for message in snapshot.children{
let msg = (message as! DataSnapshot).value //message as snapshot
//now you need to cast it to your structure([String:String])
let projectObj = Message(snapshotChild: msg as! [String:String])
//and do your comparison
}
}

Upvote/Downvote system within Swift via Firebase

I've looked over hours of code and notes and I'm struggling to find any documentation that would help me with upvoting and downvoting an object in a swift app with firebase.
I have a gallery of photos and I'm looking to add an instagram style upvote to images. The user has already logged with firebase auth so I have their user ID.
I'm just struggling to figure the method and what rules need to be set in firebase.
Any help would be awesome.
I will describe how I implemented such a feature in social networking app Impether using Swift and Firebase.
Since upvoting and downvoting is analogous, I will describe upvoting only.
The general idea is to store a upvotes counter directly in the node corresponding to an image data the counter is related to and update the counter value using transactional writes in order to avoid inconsistencies in the data.
For example, let's assume that you store a single image data at path /images/$imageId/, where $imageId is an unique id used to identify a particular image - it can be generated for example by a function childByAutoId included in Firebase for iOS. Then an object corresponding to a single photo at that node looks like:
$imageId: {
'url': 'http://static.example.com/images/$imageId.jpg',
'caption': 'Some caption',
'author_username': 'foobarbaz'
}
What we want to do is to add an upvote counter to this node, so it becomes:
$imageId: {
'url': 'http://static.example.com/images/$imageId.jpg',
'caption': 'Some caption',
'author_username': 'foobarbaz',
'upvotes': 12,
}
When you are creating a new image (probably when an user uploads it), then you may want to initialize the upvote counter value with 0 or some other constant depending on what are you want to achieve.
When it comes to updating a particular upvotes counter, you want to use transactions in order to avoid inconsistencies in its value (this can occur when multiple clients want to update a counter at the same time).
Fortunately, handling transactional writes in Firebase and Swift is super easy:
func upvote(imageId: String,
success successBlock: (Int) -> Void,
error errorBlock: () -> Void) {
let ref = Firebase(url: "https://YOUR-FIREBASE-URL.firebaseio.com/images")
.childByAppendingPath(imageId)
.childByAppendingPath("upvotes")
ref.runTransactionBlock({
(currentData: FMutableData!) in
//value of the counter before an update
var value = currentData.value as? Int
//checking for nil data is very important when using
//transactional writes
if value == nil {
value = 0
}
//actual update
currentData.value = value! + 1
return FTransactionResult.successWithValue(currentData)
}, andCompletionBlock: {
error, commited, snap in
//if the transaction was commited, i.e. the data
//under snap variable has the value of the counter after
//updates are done
if commited {
let upvotes = snap.value as! Int
//call success callback function if you want
successBlock(upvotes)
} else {
//call error callback function if you want
errorBlock()
}
})
}
The above snipped is actually almost exactly the code we use in production. I hope it helps you :)
I was very surprised, but this code from original docs works like a charm. There is one disadvantage with it: the json grows pretty big if there are a lot of likes.
FirebaseService.shared.databaseReference
.child("items")
.child(itemID!)
.runTransactionBlock({ (currentData: MutableData) -> TransactionResult in
if var item = currentData.value as? [String : AnyObject] {
let uid = SharedUser.current!.id
var usersLikedIdsArray = item["liked_who"] as? [String : Bool] ?? [:]
var likesCount = item["likes"] as? Int ?? 0
if usersLikedIdsArray[uid] == nil {
likesCount += 1
usersLikedIdsArray[uid] = true
self.setImage(self.activeImage!, for: .normal)
self.updateClosure?(true)
} else {
likesCount -= 1
usersLikedIdsArray.removeValue(forKey: uid)
self.setImage(self.unactiveImage!, for: .normal)
self.updateClosure?(false)
}
item["liked_who"] = usersLikedIdsArray as AnyObject?
item["likes"] = likesCount as AnyObject?
currentData.value = item
return TransactionResult.success(withValue: currentData)
}
return TransactionResult.success(withValue: currentData)
}) { (error, committed, snapshot) in
if let error = error {
self.owner?.show(error: error)
}
}
Not a Swift fella myself (pun!) but I think this stackoverflow question has most of your answers.
Then you would simply use a couple of if statements to return the correct value from the transaction based on whether you want to up vote or down vote.