Getting an array from Firebase Database - swift

I've a [String] Array under the "urls" key. How can i get it out? Here is my structure:

You can read it by using an observer to the urls reference and initializing an array with its value.
ref = Database.database().reference()
ref.child("sectionList").child("name of").child("urls")observe(.value, with: { (snapshot:FIRDataSnapshot) in
var urls : [String] = snapshot.children
}
"This function takes two parameters: an instance of FIRDataEventType and a closure.
The event type specifies what event you want to listen for. The code listens for a .value event type, which in turn listens for all types of changes to the data in your Firebase database—add, removed, and changed.
When the change occurs, the database updates the app with the most recent data.
The app is notified of the change via the closure, which is passed an instance of FIRDataSnapshot. The snapshot, as its name suggests, represents the data at that specific moment in time. To access the data in the snapshot, you use the value property."
Source:(https://www.raywenderlich.com/139322/firebase-tutorial-getting-started-2)

I got it like this
let ref = Database.database().reference(withPath: "sectionList")
ref.child("name of").child("urls").observeSingleEvent(of: .value, with: { (dataSnapshot:DataSnapshot) in
for object in dataSnapshot.children.allObjects as! [DataSnapshot] {
for obj in object.value as! NSArray {
print("value = \(obj)")
}
}
})

Basicly, I highly recomend you to take a look at this tutorial, especially the part about retrieving Data. To devide your project in standard MVC model will be very useful. I hope my answer will help you ;)

Related

What does the snapshot/observer code do in firebase?

When retrieving data from Firebase, we typically use the code below (or some reiteration of it)
...observeSingleEvent(of: .value, with: { snapshot in
var tempPosts = [PostModel]()
for child in snapshot.chidren{
}
return tempPosts
})
but I don't exactly get what this code is doing? Can someone explain it on a high level? I've tried printing data on mulitple spots but the only data I'm getting is: Snap post or [App.PostModel]
This code is used for observing data change in your database. You don't need to send requests from time to time for getting the latest data.
When the data changes, it will trigger the closure you given so that you can do things. For more reference, you could read this doc.
You could try this to covert snapshot into dictionary:
for child in snapshot.children {
let dataS = child as! DataSnapshot
let dict = dataS.value as? [String : AnyObject]
// handle the data
}
The code in your question uses .observeSingleEvent. What that means is that it's requesting data from Firebase immediately one time and will not observe any future changes or fire any other events.
The data is returned in in the closure as a 'snapshot' and is a 'picture' of what that data looks like at a point in time. (snapshot...picture? Pretty snappy huh)
Firebase data is only valid within the closure; any code following the closure will execute before Firebase has time to retrieve data from the server, so ensure you work with Firebase data inside that closure.
The for loop iterates over the child nodes within the snaphot one at a time. For example, the snapshot could contain child snapshots of each user in a /users node. You can then get the users data from each child snapshot.
The return statement should never be used within a asynchronous closure as you cannot return data (in that fashion) from a closure, so that line should be removed. You could however leverage an completion handler like this
func getUser(with userID: String, completion: #escaping ((_ user: UserClass) -> Void)) {
//get the user info from the snapshot, create a user object and pass it back
// via the completion
completion(user)
}
to work with the data outside the closure.

Do I need multiple Firebase database references?

Lets say I have some code like this
let ref = Database.database().reference()
let refTwo = Database.database().reference()
func getPosts() {
ref.child("posts").queryOrderedByKey().observeSingleEvent(of: .value, with: { snap in
// get some posts
})
}
func getOtherStuff() {
refTwo.child("child").queryOrderedByKey().observeSingleEvent(of: .value, with: { snap in
// get some other data
})
refTwo.removeAllObservers()
}
And I call getPosts() and getOtherStuff() in viewDidLoad() do I need to use two different references or can I just use one ref for all of my queries?
I know if you have the same ref using .observe in two different locations the data is only returned once. So you wouldn't want to re-use that ref? However, here I am just using .observeSingleEvent so I'm not sure. Additionally, would it matter if they were on the same child?
Firebase database references are just lightweight references to locations in the database. Nothing happens until you either attach a listener or write to them.
There is no need to use separate listeners in the scenario you shared. I would remove the call to removeAllObservers: since you're calling observeSingleEvent, the observers are automatically removed after the first time they fire.
in Firebase 4.4 you need just use
var ref = Database.database().reference()
also you can see "Read and Write Data" in left list for basic structs.
example:
self.ref.child("users").child(user!.uid).setValue(["mentionName": ""])
self.ref.child("users").child(user!.uid).child("email").setValue(self.emailField.text)
reference: https://firebase.google.com/docs/database/ios/start

Retrieving NSOrderedSet from Core Data and casting it to entity managedObjectSubclasss

Im making a Fitness app to learn Core data, and I have found that I need to let the user store every performed workout as a WorkoutLog item, and in there, there should be a series of ExerciseLogs which represents performances of that exercise (it contains each lift and also a reference to the actual exercise design).
Problem is that after a while i realize that i need to have these ordered, so that the next time i want to show the user their workout, the order that the exercisese were performed should be the same.
So I checked "ordered" in the top right of the image there, and now my code is in dire need of an update. I have tried to read as much as I could about working with NSOrderedSet and how to fetch them from core data and then manipulate them, but I havent really found much of use to me. (I have no experice in objective-c)
For example my code that used to be:
static func deleteWorkoutLog(_ workoutLogToDelete: WorkoutLog) {
guard let exerciseLogsToDelete = workoutLogToDelete.loggedExercises as? Set<ExerciseLog> else {
print("error unwrapping logged exercises in deleteWorkoutLog")
return
}
I get the error: .../DatabaseFacade.swift:84:77: Cast from 'NSOrderedSet?' to unrelated type 'Set' always fails
So what ive learned about sets and core data no longer seems applicable.
Im far from an expert in programming, but im very eager to learn how to get access to the loggedExercises instances.
TLDR; Is there a way to cast NSOrderedSet to something I can work with? How do we usually work with NSManagedSets from core data? Do we cast them to Arrays or MutableSets? I would very much appreciate an example or two on how to get started with retrieving and using these ordered sets!
Thanks
For anyone else wondering how to get started with orderedSets in core data:
After setting my the WorkoutLog.loggedExercises "to-many" relationship to be ordered, I managed to access them through the mutableOrderedSetValue function like this:
static func deleteWorkoutLog(_ workoutLogToDelete: WorkoutLog) {
let orderedExerciseLogs: NSMutableOrderedSet = workoutLogToDelete.mutableOrderedSetValue(forKey: "loggedExercises")
let exerciseLogsToDelete = orderedExerciseLogs.array
for exerciseLog in exerciseLogsToDelete {
guard let exerciseLog = exerciseLog as? ExerciseLog else {
return
}
Works great so far.
And to rearrange the NSOrderedSet I ended up doing something like this:
// Swap the order of the orderedSet
if let orderedExerciseLogs: NSOrderedSet = dataSourceWorkoutLog.loggedExercises {
var exerciseLogsAsArray = orderedExerciseLogs.array as! [ExerciseLog]
let temp = exerciseLogsAsArray[indexA]
exerciseLogsAsArray[indexA] = exerciseLogsAsArray[indexB]
exerciseLogsAsArray[indexB] = temp
let exerciseLogsAsOrderedeSet = NSOrderedSet(array: exerciseLogsAsArray)
dataSourceWorkoutLog.loggedExercises = exerciseLogsAsOrderedeSet
}

Firebase: observing childAdded returns existing / old records?

I have a query (written in swift):
FIRDatabase.database().reference(withPath: "\(ORDERS_PATH)/\(lId)")
.child("orders")
.observe(.childAdded, with: { firebaseSnapshot in
let orderObject = firebaseSnapshot.value as! [String: AnyObject]
let order = AppState.Order(
title: orderObject["name"] as! String,
subtitle: orderObject["item_variation_name"] as! String,
createdAt: Date(timeIntervalSince1970: TimeInterval(orderObject["created_at"] as! Int / 1000)),
name: "name",
status: AppState.Order.Status.Pending
)
f(order)
})
My database looks like this:
I want it to just listen all NEW incoming orders. However, every time it initially loads it fetched a bunch of existing orders with it, which isn't what I want.
I do have a created_at (an int that represents that time e.g. 1478637444000) on each of the orders, so if there's a solution that can utilize that that works too.
Is there something wrong with my query?
Observers always fire once and read in "all the data".
A .value observer reads in everything in the node at once, where as a .childAdded observer (from the docs)
This event is triggered once for each existing child and then again
every time a new child is added to the specified path.
If you think about your question, what you are actually looking for is a sub-set of all of your data. This is achieved via a query.
Your query will be to observe all .childAdded events that occur after a certain timestamp. So that's what you need to do!
Craft an observe query leveraging queryStartingAtValue(the timestamp to start at) which will return all children added after that timestamp.
Swift3 Solution:
Just write multiple observers.
You can retrieve your previous data through the following code:
queryRef?.observeSingleEvent(of: .value, with: { (snapshot) in
//Your code
})
observeSingleEvent of type .value just calls once and retrieves all previous data
and then observe the new data through the following code.
queryRef?.queryLimited(toLast: 1).observe(.childAdded, with: { (snapshot) in
//Your Code
})
queryLimited(toLast: 1) with bserving of type .childAdded is being called every time that a new data is available

Delete all data from specific Realm Object Swift

Before i get too far into my question. My goal, which may influence your answers, is to remove Object data if it is no longer in the cloud.
So if I have an array ["one", "two", "three"]
Then in my server I remove "two"
I want my realm to update the change.
I figure the best way to do this is to delete all data in the specific Object, then call my REST API to download the new data. If there is a better way, please let me know.
Okay so here is my problem.
I have an Object Notifications()
every time my REST API is called, before it downloads anything I am running this:
let realm = Realm()
let notifications = Notifications()
realm.beginWrite()
realm.delete(notifications)
realm.commitWrite()
I get this error after running: Can only delete an object from the Realm it belongs to.
so i tried something like this:
for notification in notifications {
realm.delete(notification)
}
realm.commitWrite()
The error I get within xcode is this: "Type Notifications does not conform to protocol 'SequenceType'
Not really sure where to go from here.
Just trying to figure out realm. Completely new to it
Note: realm.deleteAll() works, but I don't want all of my realm deleted, just certain Objects
You're looking for this:
let realm = Realm()
let deletedValue = "two"
realm.write {
let deletedNotifications = realm.objects(Notifications).filter("value == %#", deletedValue)
realm.delete(deletedNotifications)
}
or perhaps this:
let realm = Realm()
let serverValues = ["one", "three"]
realm.write {
realm.delete(realm.objects(Notifications)) // deletes all 'Notifications' objects from the realm
for value in serverValues {
let notification = Notifications()
notification.value = value
realm.add(notification)
}
}
Although ideally, you'd be setting a primary key on Notifications so that you can simply update those existing objects rather than taking the extreme approach of nuking all your local objects simply to recreate them all (or almost).