Write data to with userID and read data with userID - swift

I want to write data to firebase with a userID, because later I only want to retrieve the data the user saved. The first problem is when I read data to firebase only the values change, but I want that the values append to firebase with the userID.
I only want to retrieve the data with the UserId of the current user, but when I load it I get all informations from all users.
I already tried to change the childadded or valuetype.
I already tried to set after the first child "SavedWoman" an other child with the current userID but it don't work.
let userID = Auth.auth().currentUser?.uid
let SavedProduct : [String: Any] = ["ImageURL": FImage1[picturenumber], "Price" : Price[picturenumber]]
Database.database().reference().child("SavedWoman").child(userID!).setValue(SavedProduct)
I also tried:
refData = Database.database().reference().child("SavedWoman") /*here I already tried to set the userID */
refData.observeSingleEvent(of: .value, with: { (snapchot) in
if snapchot.childrenCount>0 {
for data in snapchot.children.allObjects as![DataSnapshot] {
let DataObject = data.value as? [String: AnyObject]
let ImageObject = DataObject?["ImageURL"]
let PriceObject = DataObject?["Price"]
let data1 = DataModel(ImageURL: ImageObject as! String?, Price: PriceObject as! String?)
self.DataList.append(data1)
}
self.tableView.reloadData()
}
})

Related

Swift Retrieve Firebase Value from childByAutoID

I've read about a hundred posts on here about dealing with the value of "childByAutoId" children from Firebase's Realtime Database... but I haven't exactly found anything that would explain what I'm trying to do so I figured I'd finally break down and ask.
First off here's the database structure:
let database = Database.database().reference(withPath: "messages")
let db1 = database.child("sender").child("receiver").childByAutoId()
Pretty straightforward. I then wanted to retrieve the value of that autoID.
db1.observeSingleEvent(of: .value, with: { snapshot in
guard let value = snapshot.value as? [String: Any] else{
completion(.failure(DatabaseError.failedToFetch))
print("GetAll Failed")
return
}
...returns the "failedToFetch" error, while:
database.child("sender").child("receiver").observeSingleEvent(of: .value, with: { snapshot in
guard let value = snapshot.value as? [String: Any] else{
completion(.failure(DatabaseError.failedToFetch))
print("GetAll Failed")
return
}
...which is the same thing only excluding childByAutoId returns:
"-MrdAxlUKHvJWjtSQe7X": {
body = "cookies";
createdat = 1640294767943;
msgId = "-MrdAxlUKHvJWjtSQe7X";
name = glynis;
receiverUid = LKJHdhkjhkjsh;
senderUid = LAKSjksljlkajlk;
}
So now the data is coming in... but when I try to get the value of "-MrdAxlUKHvJWjtSQe7X" (the auto-generated key):
let things: [Thing] = value.compactMap({ dictionary in
guard let name = value["name"] as? String,
let msgId = value["msgId"] as? String,
let body = value["body"] as? String,
let receiverUid = value["receiverUid"] as? String,
let senderUid = value["senderUid"] as? String,
let createdat = value["createdat"] as? String,
let dated = value["dated"] as? String,)else {
return nil
}
And I do a:
guard !things.isEmpty else {
print("thing are empty")
return
}
They come up empty (even though "value" is certainly populated.) So my question is how would I properly retrieve the value of the generated key (childByAutoId)?
Some of the problems I spot:
Most of the fields in your value.compactMap( don't have a matching property in your snapshot just above it. E.g. createdat is not the same as value["created"], and there is no property tId in the snapshot.
The types need to match up in order to make the as? String cast work. Your createdat value is a long number (probably the number of milliseconds since the epoch), so casting that to a string leads to nil. You should cast it to a long/number value, or convert by calling the String function, as shown here: Convert Int to String in Swift
Based on your edit...
This code:
database.child("sender").child("receiver").observeSingleEvent(of: .value, with: { snapshot in
Reads the entire sender/receiver node from your database, which contains one or more child nodes with auto-generated keys, and then properties under each of those child nodes.
When you do:
value = snapshot.value as? [String: Any]
This sets value to be a dictionary/map with the top-level key(s) being the childByAutoId. When you then access value["-MrdAxlUKHvJWjtSQe7X"] you get a map/dictionary with the properties of that child node.
You can also loop over the child nodes of the snapshot with something like:
for child in snapshot.children {
let snap = child as! DataSnapshot //downcast
let dict = snap.value as! [String: Any] // get properties of child node
let msg = dict["body"] as! String
}

Firebase query retrieves data in a random order. The data is organised by autoID

I've got the following database structure:
PVV
-- AutoID
- Data1
- Data2
- Status: Active
- ImageName: Path\FirebaseStorageImage.jpg
I'd like to retrieve the data in chronological order, and then sort the data in a descending manner (most recent first).
I think autoID does use a combination of date and time, and Firebase does normally retrieve the data in a fixed order. I am using the same function as below to retrieve text data (that does not have an imageName), and that works fine.
However, the function below returns data in a random order:
func LoadDataFromImageTest() {
self.ImageList.removeAll()
self.ImageTestFromFBTableView.reloadData()
databaseReference = Database.database().reference()
let refPVV = Database.database().reference(withPath: "PVV").queryOrdered(byChild: "Status").queryEqual(toValue: "Active")
refPVV.observeSingleEvent(of: .value, with: { [weak self] (snapshot) in
//if the reference have some values
if snapshot.childrenCount > 0 {
//clearing the list
self?.ImageList.removeAll()
//iterating through all the values
for PVV in snapshot.children.allObjects as! [DataSnapshot] {
//getting values
let PVVObject = PVV.value as? [String: AnyObject]
// let PVVText = PVVObject?["ImageString"]
let PVVName = PVVObject?["Name"]
let PVVBodyText = PVVObject?["BodyText"]
let PVVValue = PVVObject?["PVVValue"]
let Key = PVV.key
let PVVImageName = PVVObject?["ImageName"] as! String?
let imageURL = Storage.storage().reference().child(PVVImageName!)
imageURL.downloadURL(completion: { (url, error) in
if error != nil {
print(error?.localizedDescription as Any)
return
}
PVVurlName = url
let PVV = ImageModel(Name: PVVName as!String?, BodyText: PVVBodyText as! String?, PVVValue: PVVValue as! String?, Key: Key as String?, ImageName: PVVurlName as URL?)
self!.ImageList.insert(PVV, at: 0)
self?.ImageTestFromFBTableView.reloadData()
})
}
}
}
)}
I set a debug point right before I start downloading the URL. Each time I run, it returns values for PVVObject in a different order.
I have another tree like this:
Challenges
- AutoID
- Data1
- Data 2
- Status: Active
I've recycled the function above to retrieve data from the above tree, and I always get the data in the same order, when setting a debug point in the same place.
What am I doing wrong?
As per Firebase documentation the downloadURL method is asynchronous. It means that the order in which the downloaded files are retrieved is not guaranteed. When you are in the completion block of the downloadURL method, you have no idea to which PPV object the image belongs to.
I think the best is to change the architecture of your code. Create an object model class for PPV, with a imageUrl property (which is attached to each instance), and trigger the download job when you observe a change in value of this property (in the didSet method for instance). This way you will be sure that the downloaded file belongs to the instance.

Order a Firebase database and then iterate through and update an item with Xcode

I have a question regarding Firebase. It's about to set up a leaderboard database.
So my question is: Is it possible to first order a database by a child "score", then iterate through this specific order and simultaneously update the child "ranking" in the database?
Here's the scheme of the database:
https://i.imgur.com/LxRymVx.png
Can't find any solution so far. Thanks
First you need to declare your database reference:
let ref = Database.database().reference(withPath: "Leaderboard")
Then you need to do a simple query here and access elements on the query:
ref.queryOrdered(byChild: "Score").observe(.value, with: { (snapshot) in
for (index, child) in snapshot.children.enumerated {
if let leaderboardSnapshot = child as? DataSnapshot, let leaderBoardValues = leaderboardSnapshot.value as? [String: Any] {
// here you should update your children
let values = ["Name": (leaderBoardValues["Name"] as? String) ?? " ", "Ranking": index, "Score": (leaderBoardValues["Score"] as? Int) ?? 0]
ref.child(leaderboardSnapshot.key).updateChildValues(values, withCompletionBlock: { (error, ref) in
// check for error here if you want
})
}
}
}

Retrieve data from an unknown child name with Firebase and Swift

I'm using Firebase as the database for my IOS app. I want to retrieve data of several users from the database.
Here is my database organization:
Users
UserID1
Location: value1
UserID2
location: Value2
...
I want to retrieve the location data of all the users of the database.
The basic snapshot fonction
ref.child("Users").child("").child("Location").observe(.value, with: {
snapshot in
for Location in snapshot.children {
self.locationsArray.append((Location as AnyObject).key)
}
print(self.locationsArray)
})
My question is: how can I retrieve all the Locations even if I don't specify (I can't) the userID that is the name of a child before? Thanks.
Here's a code snippet that retrieves and prints the uid of each user and their location.
We can further refine our results with a .query
let usersRef = ref.child("users")
usersRef.observeSingleEvent(of: .value, with: { (snapshot) in
for snap in snapshot.children {
let userSnap = snap as! FIRDataSnapshot
let uid = userSnap.key //the uid of each user
let userDict = userSnap.value as! [String:AnyObject] //child data
let location = userDict["Location"] as! String
print("key = \(uid) is at location = \(location)")
}
})
It is not possible with Firebase to retrieve just the Location.
There are a lot of option, but I see two that fits better in your case:
Get everything from all users, and then manage in your code what you want;
Create another dictionary only with users id and location.
The second option would be like:
UsersLocation
UserID1:Location1
UserID2:Location2
So every time you change your main array you need to change this too.

Bypass unique auto ID created by Firebase. Swift 3.0

When I upload an Image, it is automatically update to database child(user.uid).child(newImage).childAutoID.setValue()
This is my json node
Users
WOhpPzlHCzYximWyBXas3Prg2rL2
NewImage
-KSE4ZcvqhFeWmuRQjjI
-KSEGLazKbfPePkCBFbe
-KSEGNVimH7GACRyXbo4
-KSEGO4gfLGOv0erTXA5
-KSENPeq7Oa5cru7gKum
Inside one autoID, This is the Json tree
-KSE4ZcvqhFeWmuRQjjI
DownloadUrl: "abcd.com"
timestamp: 21-09-2016
When I try to get value from DownloadUrl, I use child("Users") but can't child through user uid, anyways I can't child through autoUID which is created by Firebase. Does anyone have any experience. Thank you
Swift 2:
FIRDatabase.database().reference().child("Users/\(FIRAuth.auth()!.currentUser!.uid)/NewImage").observeSingleEventOfType(.Value , withBlock : {(imagesSnap) in
if let imagesDict = imagesSnap.value as? [String:AnyObject] {
for each in imagesDict{
print(each.0) // your autoId
print(each.1) // your imageDetails for that autoId
}
}
})
Swift 3:
FIRDatabase.database().reference().child("Users/\(FIRAuth.auth()!.currentUser!.uid)/NewImage").observeSingleEvent(of: .value, with : {(imagesSnap) in
if let imagesDict = imagesSnap.value as? [String:AnyObject] {
for each in imagesDict{
print(each.0) // your autoId
print(each.1) // your imageDetails for that autoId
}
}
})
But if you are looking for the autoID that contains a specific key value pair Read last part of this answer