How to append without unique auto id firebase swift - ios

I am trying to append data to my database, everytime a user is clicking a like button, if the user hasn't already clicked it. The problem is, when I am appending to the likesForPost, a unique id is showing. How can I get rid of that? Otherwise it is not possible for me to check if the user has already clicked the button?
Here is my firebase structure:
It is the green parts that are unique id, the black zones under the unique id are the user id, that I want to be able to check if has already clicked the button.
This is my code for appending to likesForPost
let quoteString = [userId: true]
let refPhotos = FIRDatabase.database().reference().child("feed-items").child(dataPathen).child("likesForPost")
let refBase54 = refPhotos.childByAutoId()
refBase54.setValue(quoteString)
Hope you guys can help me :-)

Use :-
let prntRef = FIRDatabase.database().reference().child("feed-items").child(dataPathen).child("likesForPost")
prntRef.observeSingleEventOfType(.Value, withBlock: { (snap) in
if let favDict = snap.value as? NSMutableDictionary{
favDict.setObject("true",forKey : uid)
prntRef.setValue(favDict)
} else {
prntRef.setValue(["true":uid])
}
})
Or But this is just a HACK!
FIRDatabase.database().reference().child("feed-items").child(dataPathen).child("likesForPost").updateChildValues(quoteString)
For more options check:- https://stackoverflow.com/a/39458044/6297658

Related

Updating all current user's posts works, but after signing up with a new account, it updates a different account I made earlier. iOS Firebase

In my app, I have users choosing premade avatars. In Firebase, each user’s avatar has two properties. These are image and image-color. The images are project assets, and the colors are saved as integers that get switched and converted to colors. I save the user’s image in users/uid/image, and image color in users/uid/image-color. When I read a post and add each value to my Post model, the image and image color persist even if the user changes their image or color. I fixed this by making this query:
guard let user = UserService.currentUserProfile else { return }
let ref = Database.database().reference().child("posts").queryOrdered(byChild: "author/uid").queryEqual(toValue: user.uid)
ref.observeSingleEvent(of: .value) { snapshot in
if snapshot.exists() {
print(snapshot)
for i in snapshot.children {
guard let childSnapshot = i as? DataSnapshot else { return }
let key = childSnapshot.key
let postRef = Database.database().reference().child("posts").child(key)
postRef.child("author").updateChildValues(["image": self.chosenAvatar, "image-color": self.chosenColor as Any])
}
}
}
This query works perfectly, but my problem is that when I sign the current user out, and sign up with a new account, I change the new user’s profile image and color, and it updates the other account’s posts that I was signed into before. A print statement will even show the current user’s uid is not the one in the post it updates. Here is my Firebase data structure for a post with relevant nodes :
"posts": {
"[post_id]": {
"author": {
"image": "avatar-3",
"image-color": 3,
"uid": "[uid]",
"username": "[username]"
},
Please let me know if you know why this is happening, or if I need to include something else.
It sounds like you're not updating your user variable, when signing a user in or out, which then leads to user.uid referring to the user who was signed in when the app started.
To fix this, you'll want to implement an auth state listener as shown in the first code snippet in the documentation on getting the current user.
handle = Auth.auth().addStateDidChangeListener { auth, user in
// ...
}
By using a listener your callback will be called whenever the auth state changes (including when a new user signs in), so this is the perfect time to update your user variable.

Retrieving user info from firebase

I know this question is asked a lot, but none of the solutions seem to be working for me(I have been trying multiple solutions from threads like Read data from firebase swift but it doesnt print anything to my console).
I am trying to retrieve the type of user from my database, but I dont know how to.
func pushUserInfo(){
let ref = Database.database().reference()
let infoDict = ["First name": firstName.text!, "Last name": lastName.text!, "hours": 0, "isUser" : "user"] as [String : Any]
let users = ref.child("users").child(username)
users.setValue(infoDict)
}
The part that says ["type": "user"] has two options, either "user" or admin
The screenshot above is of the Firebase realtime database.
I am trying to retrieve the type of the user, but I have no idea how. Please help me figure this out, and if possible, explain the code, because I dont really understand too much about Firebase in general. I tried reading their firebase docs, but I still dont really get it.
It looks like you're setting the data fine except that your username property appears to be a concatenated string of two optionals (maybe firstName.text and lastName.text. So this will make it impossible to query. The first step is to unwrap these into a string:
let username = "\(firstName.text!) \(lastName.text!)"
Once you've done that, you can query for that data like this:
let username = "\(firstName.text!) \(lastName.text!)"
let ref = Database.database().reference().child("users/\(username)")
ref.observeSingleEvent(of: .value, with: { (snapshot) in
// Now you can access the type value
let value = snapshot.value as? NSDictionary
let type = value?["type"] as? String ?? ""
}) { (error) in
print(error.localizedDescription)
}
You may also want to reconsider having spaces in your property names (maybe use lastName instead of last name). It will be easier for your code later on.

Swift Firebase -How to generate different .childByAutoId keys when using Fan Out

I have a chat system inside my app. When the user presses send to send the message data to different nodes inside the database -it works fine. The issue I'm having is since I'm using fan out I generate the .childByAutoIdkey before the data is sent. The user presses a send button to start the process but it's always the same exact .childByAutoId key so I'm just overwriting the previous message data. If the user pops the vc and comes back to it then a new key is created but obviously that's terrible ux for a messaging system?
How can I generate different .childByAutoId keys every time the user presses send to fan out?
#obj func sendMessageButtonPressed() {
// ***here's the problem, every time they press send, it's the same exact childByAutoId().key so I'm just overwriting the previous data at the messages/messageId path
guard let messageId = FirebaseManager.Database.database().reference().child("messages")?.childByAutoId().key else { return }
var messageIdDict: [String: Any] = [messageId: "1"]
var messageDict = [String: Any]() // has the fromId, toId, message, and timeStamp on it
let messageIdPath = "messages/\(messageId)"
let fromIdPath = "user-messages/\(currentUserId)"
let toIdPath = "user-messages/\(toId)"
var fanOutDict = [String: Any]()
fanOutDict.updateValue(messageDict, forKey: messageIdPath)
fanOutDict.updateValue(messageIdDict, forKey: fromIdPath)
fanOutDict.updateValue(messageIdDict, forKey: toIdPath)
let rootRef = Database.database().reference()
rootRef?.updateChildValues(fanOutDict)
}
The problem wasn't a new key was not getting generated. #FrankvanPuffelen pointed out in th comments that a new key should get generated every time which is exactly what was happening.
The problem was the fanout was overwriting what was originally written at these 2 paths:
let fromIdPath = "user-messages/\(currentUserId)"
let toIdPath = "user-messages/\(toId)"
It appeared the key was the same because the data kept getting overwritten.
The way I was generating the key works fine

Retrieving my posts from firebase and display it on my collectionView Cells? ios

override func viewDidLoad() {
super.viewDidLoad()
getImageUrl()
}
func getImageUrl(){
ref.child("posts").queryOrderedByKey().observeSingleEvent(of: .value) { (snapchot) in
let postsss = snapchot.value as! [String : AnyObject]
for (_,posst) in postsss {
if let uid = posst["userID"] as? String{
if uid == Auth.auth().currentUser?.uid{
if let myPostURL = posst["pathToImage"] as? String{
self.imageURLs.append(myPostURL)
}
}
}
}
}
}
I want my code to go through all the posts on Firebase and then check if their userID matches the currentusers uid, if they matched which means they are my images. then send the URL in pathToImage to an array in my code called imageURLs()[ "" ].but I don't know how to to that??. I am using SDWebImage to display my images to the collectionView cell. i have tested it and it works fine if i copy and paste a random URL in the array called imageURLs[ "URL here" ]
I am very new to Swift and Firebase, so any help would be greatly appreciated!! :)
HERE IS AN IMAGE OF MY FIREBASE FILES.
https://ibb.co/bXLMcb
Psst! posts/pathToImage holds the URL so that's the one i want to retrieve.
users/urlToImage is just a profile picture. i don't really need it right now
To locate specific data within a node a Query is used.
While iterating over the nodes works, if there's a million nodes it's going to take a long time and tie up the UI while waiting for that to happen.
The bottom line is; if you know what you are looking for - in this case a uid - then a query is a super simple solution.
let postsRef = self.ref.child("posts")
let query = postsRef.queryOrdered(byChild: "userID").queryEqual(toValue: "uid_0")
query.observeSingleEvent(of: .value) { (snapshot) in
if snapshot.exists() {
for child in snapshot.children { //.value can return more than 1 match
let snap = child as! DataSnapshot
let dict = snap.value as! [String: Any]
let myPostURL = dict["urlToImage"] as! String
self.imageURLs.append(myPostURL)
}
} else {
print("no user posts found")
}
}
With the above code, we first create a reference to our posts node. Then a query that will only return this users (uid_0) posts.
We then iterate over the snapshot of posts and add them to the array.
This is much more efficient that loading and evaluating every node in the database.
See Sorting And Filtering data for more information.
You should think about storing an index of all your users posts somewhere in your database. That way you don't need to observe all posts every time. This is called denormalization. Firebase has an article in their docs about organizing your database.
Here's some information regarding firebase filtering in swift.
The best solution is to add a separate node that has a list of "post IDs" organized by user. Then you could observe that node, and only download each post specifically by the returned ID. Here's a link about flattening data structures in firebase. It would look something like this.
"posts":{
"somePostID":{
"timestamp": "0200231023",
"postContent": "here's my post content",
"authorUID" : "0239480238402934"
} ...
},
"postsByGivenUID":{
"someAuthorID":{
"somePostID": "true",
"somePostID": "true",
},
"someOtherAuthorID":{
"somePostID": "true",
"somePostID": "true",
"somePostID": "true"
}
}
This is actually a much bigger problem than just changing how you structure your code. For scalability sake, you're going to have to reevaluate how you structure the data in firebase altogether.
Then, you can nest your firebase query, sort of like this:
ref.child("postByGivenUID").child("Auth.auth().currentUser?.uid").observe(.childAdded) { (snapshot) in
ref.child("posts").child(snapshot.value).observeSingleEventOfType(of: .value) { (snap) in
// your actual post data will be here
// that way you won't be downloadin ALL posts EVERY time
}
}

iOS Swift: retrieve an entry from the database for the user currently logged in

How do I retrieve a value (other than username and user id, which seem easier to get) for the current user from the database.
Ironically, I can set the value as follows and that works just fine:
let databaseRef = FIRDatabase.database().reference()
userID = (FIRAuth.auth()?.currentUser?.uid)! as String
databaseRef.child("users").child(userID!).child("TermCond").setValue("Yes")
But for the life of me I cannot work out what to put instead of setValue if I simply want to retrieve the current TermCond value. I thought just using value as for example in
let DesiredValue = databaseRef.child("users").child(userID!).child("TermCond").value as? String
Would suffice, but nothing works. I am confused why retrieving the value should be more difficult than setting it.
To "read" a value from Firebase, you need to add a reference listener that gets called every time that value changes.
In your case, that could be something like:
let databaseRef = FIRDatabase.database().reference()
databaseRef.child("users").child(userID!).child("TermCond").observe(FIRDataEventType.value, with: { (snapshot) in
let desiredValue = snapshot.value as? String
})
This block of code will get triggered every time your value changes. If you only want to read it once, you can use observeSingleEvent:of:with instead of observe:with.
This is as described in the Firebase documentation: https://firebase.google.com/docs/database/ios/read-and-write
I recommend you read their entire Documentation to get an idea of how Firebase works, as it is very different from traditional databases.
I can also recommend the following tutorial if you'd like to learn a bit more about the Firebase Database and how it works: https://www.raywenderlich.com/139322/firebase-tutorial-getting-started-2
I've solved this now (based on Aleksander's reply). The way I did it is as follows.
databaseRef.child("users").child(userID!).observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
self.desiredValue = value?["TermCond"] as? String ?? ""
self.LabelToShow.text = self.desiredValue!
}) { (error) in
print(error.localizedDescription)
}
This works absolutely fine and shows the value of TermCond in the LabelToShow on my iOS screen.

Resources