Append element to Firebase Array - ios

How I could append an element to an array like that:
Using this code I'm overriding the old data:
let toUpdate = [book.id]
self.refUsers.child(localUser.key!).child("booksPurchased").setValue(toUpdate, withCompletionBlock: { (error, _) in

You could use this method: firebase.firestore.FieldValue.arrayUnion()
Example with angularfire2:
this.afs.collection('collection').doc(id).update( {
array: firebase.firestore.FieldValue.arrayUnion( 'newItem' )
});
For more information: https://firebase.google.com/docs/reference/js/firebase.firestore.FieldValue#arrayunion

In this case, you will have to read the existing data, then write it back with the new value added. Arrays like this are not always the best way to store lists of data if you want to perform a lot of append operations. For that, you're better off pushing data into a location using childByAutoId.

Reading and writing lists
Append to a list of data
Use the childByAutoId method to append data to a list in multiuser applications. The childByAutoId method generates a unique key every time a new child is added to the specified Firebase reference. By using these auto-generated keys for each new element in the list, several clients can add children to the same location at the same time without write conflicts. The unique key generated by childByAutoId is based on a timestamp, so list items are automatically ordered chronologically.
You can use the reference to the new data returned by the childByAutoId method to get the value of the child's auto-generated key or set data for the child. Calling getKey on a childByAutoId reference returns the auto-generated key.
You can use these auto-generated keys to simplify flattening your data structure. For more information, see the data fan-out example.
-https://firebase.google.com/docs/database/ios/lists-of-data

You could set the values of the keys in the array to true, and then set the value directly in an update.
So if 'newId' is the new item to add, maybe something like:
const update = {
[`/users/${localUser.key}/booksPurchased/${newId}`]: true]
}
firebase.db.ref().update(update);
Firebase docs example of an update:
https://firebase.google.com/docs/database/web/read-and-write

Related

Remove an entire child from Firebase using Swift

I'd like to write a function to remove an entire child from Firebase. I'm using XCode 10 and swift 3+.
I have all the user info of the child I'd like to delete so I assume the best call would be to iterate through every child and test for the matching sub child value but it would be great if there was a faster way.
Thanks for the help!
Heres what I'd like to delete
I assume testing for epoch time then removing the whole node would be ideal. Also not sure how to do this
I understand you don't have access to the key of the node you want do delete. Is that right? Why not? If you use the "observe()" function on a FIRDatabaseQuery object, each returned object should come with a key and a value.
Having a key it is easy to remove a node, as stated in the Firebase official guides.
From the linked guide,
Delete data
The simplest way to delete data is to call removeValue on a reference
to the location of that data.
You can also delete by specifying nil as the value for another write
operation such as setValue or updateChildValues. You can use this
technique with updateChildValues to delete multiple children in a
single API call.
So, you could try:
FirebaseDatabase.Database.database().reference(withPath: "Forum").child(key).removeValue()
or
FirebaseDatabase.Database.database().reference(withPath: "Forum").child(key).setValue(nil)
If you can't get the key in any way, what you said about "iterating" through the children of the node could be done by using a query. Here's some example code, supposing you want all forum posts by Jonahelbaz:
return FirebaseDatabase.Database.database().reference(withPath: "Forum").queryOrdered(byChild: "username").queryEqual(toValue: "Jonahelbaz").observe(.value, with: { (snapshot) in
if let forumPosts = snapshot.value as? [String: [String: AnyObject]] {
for (key, _) in forumPosts {
FirebaseDatabase.Database.database().reference(withPath: "Forum").child(key).removeValue()
}
}
})
Here you create a sorted query using as reference "username" then you ask only for the forum posts where "username" are equal to Johanelbaz. You know the returned snapshot is an Array, so now you iterate through the array and use the keys for deleting the nodes.
This way of deleting isn't very good because you might get several posts with the same username and would delete them all. The ideal case would be to obtain the exact key of the forum post you want to delete.

Firebase Swift equivalent of push()

In Javascript, calling push on a firebase ref would generate a random ID. Then you can populate the stuff below.
However, I can't find push in Swift. Do I have to generate random IDs myself?
I think you're looking for childByAutoId():
childByAutoId generates a new child location using a unique key and returns a FIRDatabaseReference to it. This is useful when the children of a Firebase Database location represent a list of items.
The unique key generated by childByAutoId: is prefixed with a client-generated timestamp so that the resulting list will be chronologically-sorted.
Declaration
func childByAutoId() -> FIRDatabaseReference
Also see: https://firebase.google.com/docs/database/ios/read-and-write#update_specific_fields

When I try to save data to Firebase it only updates the existing data, Swift, Firebase

The way I set up my database structure was like this:
It starts with Lists then there is a child that shows the users UID then inside that there is one item.
The one item inside the UID gets updated every time I attempt to save new data. Instead of adding another item the same one just keeps changing. I was wondering how I could instead of update the same one item every time add more items.
The way that I save my data is with this line of code.
let user = FIRAuth.auth()?.currentUser
let item: String = self.ItemTextField.text!
self.ref.child("Lists").child(user!.uid).setValue(["Items": item])
More idiomatic is to store the list of items with so-called push ids:
Lists
twEymn...
-Km....: "Yoghurt"
You'd do this with:
self.ref.child("Lists").child(user!.uid).childByAutoId().setValue(item)
The childByAutoId() generates a unique, sequential ID. It's similar to an array index, but this one works reliably in multi-user environments and when users can be offline. Read this blog post about these so-called push ids.
Alternatively you can use the name of the item as the key (if the item has to be unique in the list):
Lists
twEymn...
"Yoghurt": true
In that case the code becomes:
self.ref.child("Lists").child(user!.uid).child(item).setValue(true)
One thing you'll note is that both of these approaches only deal with the newly added item, instead of the list of items as a whole. This is a general pattern you'll see when using Firebase. By isolating your modifications, your app will be more scalable without users getting into each other's way.
The problem is that you are setting a key-value pair ("Items" : item) so that each time it is updating the value for the same key. What you could do instead is ("Items" : [your array of items here]), which will update a list of items for the same key each time.
You could also fetch the current list of items, append your new item locally, and then update.

Adding a Value to Collection Items

I want to load data from many files. Each file is named with a date and I need to inject this date to each of the fetched Entries of my file.
I know I could do this with an foreach - loop before inserting the data into the collection, but I think there should be a better solution.
Content of one file
[{"price":"95,34","isin":"FR0000120073"},{"price":"113,475","isin":"CA13645T1003"}]
The Code I use to move the data into a collection.
$collection= collect(json_decode(File::get($file)));
I tried for example the "map" method, however I don't know how to pass an additional variable to the anonymous function.
The content of my collection should look like this:
[{"price":"95,34","isin":"FR0000120073","date":"2016-06-23"},{"price":"113,475","isin":"CA13645T1003","date":"2016-06-23"}]
Is there any simple solution using the collections or do I have to use a foreach-loop?
May be this will help
$collection = collect(json_decode(File::get($file)));
$collection = $collection->each(function ($item, $key) {
//First iteration of $item will be {"price":"95,34","isin":"FR0000120073"}
$item->date = "2016-06-23"; //Insert key, value pair to the collection
});

Writing a list of objects to realm

I just start thinking about implementing Realm and have some newbie doubts.
For example I receive a list of objects which I transform into realm objects. How can I write the whole list directly to Realm, without writing each object separately?
A typical workaround comes into my mind, which would be defining a bigger object which contains this list as a property and writing that bigger object to the DB. But is it possible to write directly the obtained list of objects to DB without something that encapsulates them?
And also when preparing this list of Realm objects, I basically have a list of dictionaries. What's the best approach into transforming all of them directly into a list of Realm objects?
Just to confirm. When you mention your list of objects at the top and then mention that you have a list of dictionaries, are they the same thing?
If they are, and your data is coming down as a set of dictionaries, as long as the key names match the properties in your Realm Object models, then you can simply loop through each dictionary and pass each one to Realm to create it as a new entry in the database:
let realm = try! Realm()
try! realm.write {
for dictionary in dictionaries {
realm.create(MyObject.self, value: dictionary, update: false)
}
}
More information on that API can be found on Realm Swift's documentation page.
If your objects don't map directly to the properties in your Realm file, then you're going to need to manually reformat the structure of your list of objects until either could be inserted as a dictionary, or you can manually create your own Realm Object instances off them.

Resources