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

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

Related

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.

Data in my iOS app vanishes after a couple of days

I have an iPhone app that stores data locally in an array. I was testing the app on my actual iPhone and it works fine except that the data vanishes after a couple of days. I am curious to see if this has happened to anyone else and if a solution was found. Here is a sample of the code that stores the data:
var doctorsArray = [Doctors]()
func storeDoctorsArray() {
doctorsArray.sort(by: {(a: Doctors, b: Doctors) -> Bool in
return a.firstName < b.firstName
})
let defaults = UserDefaults.standard
let data = NSKeyedArchiver.archivedData(withRootObject: doctorsArray)
defaults.set(data,forKey: "stored_doctors_data")
defaults.synchronize()
}
And to get data:
func loadDoctorsArray() {
if let storedArray = UserDefaults.standard.object(forKey: "stored_doctors_data") as? Data {
doctorsArray = NSKeyedUnarchiver.unarchiveObject(with: storedArray) as! [Doctors]
Again as mentioned, the app works fine and the data stores and displays as expected but vanishes after a couple of days.
If you delete the app it clears the contents of UserDefaults. Other than that, or resetting the simulator, UserDefaults should persist between runs.
How/when are you calling your loadDoctorsArray() function

Firebase not taking all arguments

My code goes as followed.
Once I get to the part where I am writing to the database it seems to skip over ContestName. Then it will record the contestdescription and user to the database. If I put contestDescription first before contestName then the contestDescription is missed but the name gets stored. Really funky bug. Any help would be appreciated.
NOTE: I tried a sleep but that didn't help at all.
#IBAction func SubmitContest(_ sender: Any) {
//Convert to text
let contesttitle = ContestName.text;
let contestdescript = ContestDescription.text;
//Some Firebase Stuff
let userID = Auth.auth().currentUser?.uid
let contestRef = ref.child("craftType").child("Custom")
let thisContest = contestRef.childByAutoId()
//Store to firebase
//Whatever one is first is not making it to firebase database
thisContest.setValue(["ContestName": contesttitle])
thisContest.setValue(["ContestDescription": contestdescript])
thisContest.child("User").setValue(userID)
}
Calling setValue on a location replaces all existing data at that location. So in this snippet:
thisContest.setValue(["ContestName": contesttitle])
thisContest.setValue(["ContestDescription": contestdescript])
The second line is replacing whatever the first line writes.
You should either combine the two:
thisContest.setValue(["ContestName": contesttitle, "ContestDescription": contestdescript])
Or use updateChildValues (which doesn't replace the entire data at the location, but only at the properties you specify):
thisContest.updateChildValues(["ContestName": contesttitle])
thisContest.updateChildValues(["ContestDescription": contestdescript])

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.

How to append without unique auto id firebase swift

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

Resources