Querying Firebase Data by Child - swift

I am now long working with swift and Firebase but face the issue that one of my queries doesn't work properly.
Each of my messages has a creation Date assigned and I can call it from the data when I load the messages but I am not able to load it by the child "creationDate" which is my node for the timestamp.
My message is sent like in a chat app and there is no issue, following is my code for querying the data.
As I mentioned the path is correct and the code is just for testing purposes
Note : this is just a code snippet in order to test if the path is correct
func checkMessages() {
guard let currentUser = API.User.CURRENT_USER?.uid else {return}
API.Message.REF_MESSAGES_CHAT.child(currentUser).queryOrdered(byChild: "creationDate").observeSingleEvent(of: .value) { (snapshot) in
if let dict = snapshot.value as? [String:Any] {
for key in dict.keys {
if let value = dict[key] as? [String:Any] {
let creationDate = value["creationDate"] as! Double
let text = value["text"] as! String
print("creation:" , creationDate)
print("text:", text)
}
}
}
}
}
the print out would be
creation: 1514041174.13297
text: 8
creation: 1514041177.54951
text: 10
creation: 1514041171.24212
text: 6
creation: 1514041172.35241
text: 7
creation: 1514041168.06832
text: 3
creation: 1514041166.98511
text: 2
creation: 1514041170.10783
text: 5
creation: 1514041165.88657
text: 1
creation: 1514041169.09632
text: 4
creation: 1514041175.60879
text: 9
I sent them ordered properly ( 1,2,3,4,5,6,7,8,9,10).
Any idea?
I am really struggling at this point because I have this feature implemented over 30 times in my app and nowhere else its an issue just in querying this.
Edit: upload of a message
let creationDate = Date().timeIntervalSince1970
let timestamp = ["creationDate": creationDate, "text": text] as [String : Any]
API.Message.REF_MESSAGES_CHAT.child(currentUser).child(newMessageID).setValue(timestamp)

API.Message.REF_MESSAGES_CHAT.child(currentUser).queryOrdered(byChild: "creationDate").observeSingleEvent(of: .value) { snapshot in ... }

Related

How to get an array from Firestore document on Swift 5?

Please help me understand the logic reading data from the Firestore document, if one of the values is an array. I tried other answers here and sources but never came to a simple working way and understand clearly. Firestore document structure — example. And Swift class targets for saving (conditional):
struct MyStruct {
var name: String
var pages: Int
}
let part1 = [MyStruct]()
let name1 = ""
let pages1 = 0
let part2 = [MyStruct]()
let name2 = ""
let pages2 = 0
func readFirestore() { }
What should the document reader function look like to add data to existing targets in the class? Thanks in advance for any help in improving my understanding!
They helped to deal with familiar, thank you for what they are. As expected, everything is simple. But for the beginner there is nothing more difficult than simplicity 😁
func readFirestore() {
self.db.collection("example").document("book").getDocument { (document, error) in
if error == nil {
if document != nil && document!.exists {
//get all document data
guard let documentData = document!.data() else {return}
//get value-array for key "part1"
let element = documentData["part1"] as? [Any] //print -> Optional([name1, 100])
//get first element in array
guard let nameDB = element?[0] as? String else {return} //print -> name1
guard let pagesDB = element?[1] as? String else {return} //print -> 100
//append in class
part1.append(MyStruct(name: nameDB, pages: pagesDB))
name1 = nameDB
pages1 = pagesDB
}
}
}
}

Instead of running a snapshot for all users, how do you set up multiple queries to limit the number of users sent to the device?

What I have: A snapshot of all users with a bunch of if statements that eventually returns an array of users that get displayed.
What I need: The array of end users to be used in a .query in the line preceding the snapshot.
Why do I need this: This line is so that the entire database of users is not run on the client.
More specifically, what do I need to query for: A) Users who have a child "caption"(timestamp) with a timestamp that is in today, AND, B) who are 3000 miles from the current user.
JSON of DB
"people" : {
"02PdiNpmW3MMyJt3qPuRyTpHLaw2" : {
"Coordinates" : {
"latitude" : -25.809620667034363,
"longitude" : 28.321706241781342
},
"PhotoPosts" : "https://firebasestorage.googleapis.com/v0/b/daylike-2f938.appspot.com/o/images%2F02PdiNpmW3MMyJt3qPuRyTpHLaw2%2FPhotoPosts?alt=media&token=24fee778-bcda-44e3-aa26-d7c2f8509740",
"caption" : 1602596281762, /// this is timestamp
"postID" : "02PdiNpmW3MMyJt3qPuRyTpHLaw2"
},
"e1" : “cvvvv666",
"e2" : "aol.com",
" "postID" : "0RnqWV7Gd9Z0bUW9nUvizMQOjK73",
"users" : "cvvvv666#aol.com"
},
.
var dict = CLLocation()
...
dict = CLLocation(latitude: lat, longitude: lon)
...
let thisUsersUid = Auth.auth().currentUser?.uid
//this line below is where the refArtists2 query should go. in other words send all users to device that meet the 2 if statements, which is represented by self.people.append(peopl)//
let refArtists2 = Database.database().reference().child("people").queryOrdered(byChild: "caption").queryEqual(toValue: ANY Timestamp in today).queryOrdered(byChild:Coordinates). queryEqual(toValue:ThoseCoordinates which make the distance to current user less than 3000 miles)
refArtists2.observe(DataEventType.value, with: { snapshot in
if snapshot.childrenCount>0{
self.people.removeAll()
for people in snapshot.children.allObjects as! [DataSnapshot] {
if people.key != thisUsersUid {
let peopleObject = people.value as? [String: AnyObject]
let peopleCoordinates = peopleObject?["Coordinates"] as? String
let peoplecaption = peopleObject?["caption"] as? Int //is timestamp
let peoplepostID = peopleObject?["postID"] as? String
let coordSnap = people.childSnapshot(forPath: "Coordinates")
guard let lat = coordSnap.childSnapshot(forPath: "latitude").value as? CLLocationDegrees else { return }
guard let lon = coordSnap.childSnapshot(forPath: "longitude").value as? CLLocationDegrees else { return }
let locCoord = CLLocation(latitude: lat, longitude: lon)
let coordSnap12 = people.childSnapshot(forPath: "caption").value as? Int ?? 0
let date = Date(timeIntervalSince1970: TimeInterval(coordSnap12)/1000.0)
//let secondsInDay = 86400
**if Calendar.current.isDateInToday(date)** {
let distance = locCoord.distance(from: self.dict)
print(distance, "distancexy")
**if distance/1609.344 < 3000**{
let peopl = Userx(Coordinates: peopleCoordinates, distance:distance, caption: peoplecaption, postID: peoplepostID)
self.people.append(peopl)
let d = people.key as! String
self.printPersonInfo(uid:d) ///////This is used to reload the data
} else {
print ("w")
}
} else {
print ("alphaaa")
}
}
print("aaaaaaaa", self.people.map {$0.distance})
}
self.people.sort { ($0.distance ?? 0) < ($1.distance ?? 0) } ////////This sorting with distance is used with returning the cell. people is used as uid array to return the cell.
}
})
} else {
print("no")
}
})
Ancillary caveat: the self.people.sort { ($0.distance ?? 0) < ($1.distance ?? 0) }sorting is important, so the queries should not impede that. I am a bit concerned with using queryOrdered in that it orders the array of users in the wrong order. If it does, a C) query should be: The order of the users must be with the closest users to the logged in user first. The furthest from the logged in user must go last in the array.
Another way of asking this would be: Instead of running a snapshot of all users, how do you query the snapshot's 'end result sort' when making the snapshot?
The timestamp is seconds since 1970
My attempt at the date query below. I took the code and tried to put the code that gets the date before the actual query(currently the code that gets the date is after the snapshot of all users).
var ppp: String! ////this should be the uid of all users in db
let people = Database.database().reference().child("people").child(self.ppp).child("captions")
people.observe(DataEventType.value, with: { snapshot in
let captionss = snapshot.value as? Int ?? 0
let date = Date(timeIntervalSince1970: TimeInterval(captionss)/1000.0)
let query1 = Database.database().reference().child("people").queryOrdered(byChild: "caption").where?(isDateInToday(date))
Edit: This answer is in Firestore, not Realtime Database. However, the concepts are the same.
The question is several questions in one; asking about distance, compound queries and how to query Firebase in general. I will post this answer to address the second two and distance queries are addressed in the comment to the question.
Once the query pattern is understood, they become easier and most importantly; it becomes more obvious that how your data is structured depends on what queries you want to run against that data.
Suppose we have a users collection with user documents - each documentId is the users uid
users
uid_0
name: "Leroy"
and then we have the posts for the users - each post contains some text, a timestamp of the post, the uid of the user that posted it, what the topic is and a url of a picture that appears in the post. Notice I am storing posts in a separate collection; why read in a bunch of user data when we only want to know about their post.
posts
post_id
postText: "pretty flowers"
postDate: "20201103"
postUrl: "www....."
postUid: "uid_0"
postTopic: "flowers"
Let suppose we want to get posts from today that are about flowers, and then also get the posters name and output who posted the message and what they said.
To do this we will need a compound query and then a subquery to retrieve the posters name as well.
func getTodaysPostsAboutFlowers() {
let postsCollection = self.db.collection("posts")
let query = postsCollection.whereField("postDate", isEqualTo: "20201103").whereField("postTopic", isEqualTo: "flowers")
query.getDocuments(completion: { snapshot, error in
if let err = error {
print(err.localizedDescription)
return
}
guard let docs = snapshot?.documents else { return }
for doc in docs {
let postText = doc.get("postText") as? String ?? "No text"
guard let postersUid = doc.get("postUid") as? String else { return }
self.outputPostTextAndUserName(withText: postText, andUid: postersUid)
}
})
}
The above performs a compound query on both the postDate field as the postTopic field.
The above then calls another function to retrieve the users name and output both the name and what they said
func outputPostTextAndUserName(withText: String, andUid: String) {
let usersCollection = self.db.collection("users")
let theUserDoc = usersCollection.document(andUid)
theUserDoc.getDocument(completion: { documentSnapshot, error in
if let err = error {
print(err.localizedDescription)
return
}
if let doc = documentSnapshot {
let postersName = doc.get("name") as? String ?? "No Name"
print("\(postersName) posted: \(withText)")
}
})
}
and the output
Leroy posted: pretty flowers
As you can see, there's no need to load all of the users, no need to iterate over results etc. Even if you have a billion users, this will only return a subset of that data which is a best practice when working with huge data sets; only get the data you're interested in.
Edit. The OP is asking about querying for nodes containing today. The simple solution is to have one child node containing a timestamp which would contains specific date data and then another child node just containing today data in YYYYMMDD format.
people
uid_x
timetamps: 9023490823498 //Date(timeIntervalSince1970:
todaystamp: "20201106" // yyyymmdd format
that makes querying for nodes that contain today very simple.

Populate a Chart from firebase database

I've been stuck all day, I'm trying to implement a chart on my app that has to collect some data from my firebase database to use them to populate the chart.
I've try this code to get the data from the database:
let userID = Auth.auth().currentUser?.uid
let bslChecksDB = Database.database().reference().child("BSL Checks").child(userID!)
bslChecksDB.observe((.childAdded)) { (snapshot) in
print(snapshot)
let snapshotValue = snapshot.value as! [String : [String: String]]
print(snapshotValue)
self.arrayOfDic = Array(snapshotValue.values)
for i in 0...self.arrayOfDic.count - 1 {
self.snapshotDictionary = self.arrayOfDic[i]
let date = self.snapshotDictionary["Date"]!
let time = self.snapshotDictionary["Time"]!
let bsl = Double(self.snapshotDictionary["BSL"]!)
let unit = self.snapshotDictionary["Unit"]!
let dateTime = "\(date) at: \(time)"
self.dateTimeArray.append(dateTime)
self.bslDataArray.append(bsl!)
self.uuuuDataArray.append(unit)
}
I try to print the various arrays to see if they were made and they were, so no problem (i thought). then when I proceed to upload those data on the chart, they disappear.... It seems like my arrays are wiped out as soon as they are made! What is wrong?
for i in 0...self.dateTimeArray.count - 1 {
ChartFormatter().array.append(self.dateTimeArray[i])
}
print("array")
print(ChartFormatter().array)
}
var lineChartEntry = [ChartDataEntry] ()
print(dateTimeArray.count)
for i in 0...dateTimeArray.count - 1 {
let value = ChartDataEntry(x: Double(i), y: bslDataArray[i])
lineChartEntry.append(value)
}
let line1 = LineChartDataSet(values: lineChartEntry, label: "BSL")
line1.colors = [NSUIColor.blue]
let data = LineChartData ()
data.addDataSet(line1)
chtChart.data = data
chtChart.chartDescription?.text = "My graph"
Since this is the first time I implement a chart on my app, could you please explain how to populate the chart with data from a firebase database? thanks

Reading Firebase child by auto ID swift

This is a quick output of my Firebase tree, id like to access people_on_this_planit / userID node. My only issue is that node is within an auto ID; please any help would be appreciated. Thanks
planits
-LEmgxuG_13KNA5inRaB
Planit Title: "Some title"
people_on_planit
-LEmh6IxqguVBJEwghZv (auto ID)
userID: "kkdiEW0D9"
senderId: "39FdLfdIO8832"
Right now the code i am using is as follows, but i am getting nil when i print peopleonplanit.
ref = Database.database().reference()
let userID = Auth.auth().currentUser?.uid
let planitsRef = ref.child("planits")
for child in snapshot.children {
let childSnap = child as! DataSnapshot
let dict = childSnap.value as! NSDictionary
let planKey = childSnap.key
//print(planKey)
print(dict)
let title = dict["Planit Title"] as! String
let senderID = dict["senderId"] as! String
let peopleOnPlanit = dict["people_on_planit"] as? String
print(peopleOnPlanit)
}
Here's the code to read the Firebase structure proposed in the question
let planitsRef = self.ref.child("planits")
planitsRef.observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children {
let planSnap = child as! DataSnapshot
let planDict = planSnap.value as! [String: Any]
let title = planDict["plan_title"] as! String
let senderId = planDict["sender_id"] as! String
print("plan title: \(title) senderId: \(senderId)")
let peopleOnPlanitSnap = planSnap.childSnapshot(forPath: "people_on_planit")
for peopleChild in peopleOnPlanitSnap.children {
let peopleChildSnap = peopleChild as! DataSnapshot
let userSnapDict = peopleChildSnap.value as! [String: Any]
let userId = userSnapDict["user_id"] as! String
print(" userId: \(userId)")
}
}
})
and the output
plan title: Some title senderId: 39FdLfdIO8832
userId: uid_0
userId: uid_1
-- A couple of notes --
Since the associated users appear to be stored by their user Id and that's the only data stored within that node, you may want to change this node
people_on_planit
-LEmh6IxqguVBJEwghZv (auto ID)
userID: "kkdiEW0D9"
to look like this
people_on_planit
uid_x: true //uid_x would be kkdiEW0D9 etc
uid_y: true
it's a bit shallower and cleaner and requires less code to read it. Also, you can query it more easily if that ever comes up.
also note that I changed the naming convention slightly; you are welcome to format keys however you want but I went with keys like plan_title instead of PlanIt Title and sender_id and user_id instead of senderId and userId. My practice is for actual firebase keys I use all lowercase, underscore instead of spaces, and in code, I use lower/uppercase with no spaces. So if you copy paste my code, you'll need to change those.

Firebase/Swift queryOrder then match value

I asked a question yesterday that was marked as a duplicate, and when I updated the question it was not unmarked. So I am asking again here (as per stackoverflow's recommendation).
I am trying to sort by multiple values in firebase. I understand that is not possible, but i was given an example in another language which is only half helpful as how to go about doing it the right way. In any case i tried to follow the example given here Query based on multiple where clauses in firebase .
This is the structure of my firebase
room
-KJe22sduQMz1DIs_DH6
allowedParticipants:
14
createdBy:
"Mr Tester"
members:
"nmeMYnnSatRch5qKPJKIe7jEOLy2"
participating:
true
status:
"seedling"
theme:
"Cats"
totalNumberOfMembers:
1
and this is the code that I am trying to get to work
ref.queryOrderedByChild("status").queryStartingAtValue("active").queryEndingAtValue("active").observeEventType(.Value) { (snapshot: FIRDataSnapshot) in
let themeOfEvent = snapshot.value
if themeOfEvent?.value == pickedTheme {
print("foo")
}
}
Could somebody please post a useful comment or answer to help me?
Thank you
I was able to get help
This works
func listOfPossibleCompetitionsFromFirebase(){
let createdRoomRef = firebase.child("room")
createdRoomRef.queryOrderedByChild("status").queryStartingAtValue("active").queryEndingAtValue("active").observeEventType(.Value) { (snapshot: FIRDataSnapshot) in
var themeCount = 0
self.listOfOpenComps.removeAll()
if let tmp = snapshot.value as? [String:AnyObject] {
let keys = tmp.keys
for key in keys {
if let roomDetails = (tmp[key] as? [String:AnyObject]) {
if let themeOfEvent = roomDetails["theme"] as? String where themeOfEvent == pickedTheme {
themeCount += 1
self.listOfOpenComps.append(key)
}
}
}
}
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
print("rooms count: \(themeCount)")
}
}