Crash while retrieving data from Firebase [closed] - swift

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
My Code
let ref = Database.database().reference().child("PostInfo")
let query = ref.queryOrdered(byChild: "post_title").queryEqual(toValue: self.retrieve_title)
query.observeSingleEvent(of: .value) { (snapshot) in
print((snapshot.childSnapshot(forPath: "status").value as? String)!)
}
}
jSon Data
{"PostInfo":{
"-KyjkkEAZeHLjdRLg20w" : {
"postImage" : "https://firebasestorage.googleapis.com/v0/b/hobbyquest-ee18d.appspot.com/o/8C40BA04-6D8D-4A23-B8BB-E1B3AC64E66F.png?alt=media&token=3f0f10e3-a64b-4187-9259-3c25bfc4a9e5",
"post_title" : "hahahahah",
"status" : "a banana an",
"threadForHobby" : "Bowling",
"userID" : "ccuvHt6feYVIO6GUXKo3OpO6VUn2"}
}
I am trying to get the status data from firebase but the app keeps crashing. Please help!

The problem is this line
(snapshot.childSnapshot(forPath: "status").value as? String)!
Your code is reading the data by .value.
.value returns ALL nodes within the given node and you will need to iterate over them. For example. Suppose our database looks like the following and you are querying for posts with a post_title of title_0
PostInfo
post_0
postImage = "www.thing.com"
status = "status_0"
post_title = "title_0"
post_1
postImage = "www.yipee.com"
status = "status_1"
post_title = "title_1"
post_2
postImage = "www.dude.com"
status = "status_2"
post_title = "title_0"
When running your query, both post_0 and post_2 will be returned because they both have title_0
You would need to iterate over the snapshot to get the results.
let ref = self.ref.child("PostInfo")
let query = ref.queryOrdered(byChild: "post_title").queryEqual(toValue: "title_0")
query.observeSingleEvent(of: .value) { (snapshot) in
for child in snapshot.children {
let snap = child as! DataSnapshot
let x = (snap.childSnapshot(forPath: "status").value as? String)!
print(x)
}
}
If you notice - your line of code works in this scenario because it's examining each child as a separate snapshot.
On the other hand, if you only want to return the first match, you can use .childAdded, which will return just an individual node:
let ref = self.ref.child("PostInfo")
let query = ref.queryOrdered(byChild: "post_title").queryEqual(toValue: "title_0")
query.observeSingleEvent(of: .childAdded) { (snapshot) in
let x = (snapshot.childSnapshot(forPath: "status").value as? String)!
print(x)
}

I don't know why but you could do it a different way but changing the layout of your data.
The data would be like this:
{"PostInfo":{
"searchidentifier" : { //whatever identifier you want, I think you're trying to use the post title
"object" : {
"postImage" : "https://firebasestorage.googleapis.com/v0/b/hobbyquest-ee18d.appspot.com/o/8C40BA04-6D8D-4A23-B8BB-E1B3AC64E66F.png?alt=media&token=3f0f10e3-a64b-4187-9259-3c25bfc4a9e5",
"post_title" : "hahahahah",
"status" : "a banana an",
"threadForHobby" : "Bowling",
"userID" : "ccuvHt6feYVIO6GUXKo3OpO6VUn2"}
}
}
And you would retrieve your data like this:
let ref = Database.database().reference().child("PostInfo").child("\(self.retrieve_title)")
ref.observe(.value, with: { (snapshot) in
if snapshot.childrenCount > 0 {
for classes in snapshot.children.allObjects as![FIRDataSnapshot] {
let classesObject = classes.value as? [String: AnyObject]
let postImage = classesObject?["postimage"]
//Retrieve all the other objects here as well
}
}
})

Related

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.

Grab data saved in Firebase and present it on view controller [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
Edit (I hope this is more specific):
I can't seem to get the data from firebase to show up the page when i run the app:
When a notification is saved under a child node on firebase, an 'objectId' is printed - I want to grab the data under the node that matches this objectId under another node called 'pinion', a snippet of the JSON firebase structure is:
"notification" : {
"Gmg1ojNoBiedFPRNSL4sBZz2gSx2" : {
"-L_xNVcs7f3RhuLAcg7j" : {
"from" : "Gmg1ojNoBiedFPRNSL4sBZz2gSx2",
"objectId" : "-L_xNVcfZavjGFVv6iGs",
"timestamp" : 1552586771,
"type" : "pinion"
},
"pinions" : {
"Gmg1ojNoBiedFPRNSL4sBZz2gSx2" : {
"-L_xNVcfZavjGFVv6iGs" : {
"option A" : "Aaaa",
"option B" : "Cccc",
"question" : "Four",
"selectedId" : "FoFQDAGGX9hntBiBdXYCBHd8yas2",
"uid" : "Gmg1ojNoBiedFPRNSL4sBZz2gSx2"
},
"users" : {
"Gmg1ojNoBiedFPRNSL4sBZz2gSx2" : {
"email" : "eeee#gmail.com",
"fullname" : "Eeee",
"profileImageUrl" : "https://firebasestorage.googleapis.com/v0/b/pinion-4896b.appspot.com/o/profile_image%2FGmg1ojNoBiedFPRNSL4sBZz2gSx2?alt=media&token=209e57ca-b914-4023-8f85-fadfae7b7407",
},
Would appreciate any help and let me know if you need any other information - thank you in advance :)
UPDATE:
It's working for the question and answers but I am getting an error "Unable to infer closure type in the current context" when calling the image:
#IBOutlet weak var senderProfileImage: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
showImageOfSender()
}
var ref = Database.database().reference()
var userId = "" //this was made equal to the autoId under pinion, under the users ID in another view controller
func showImageOfSender() {
var senderPhoto = [String]()
guard let uid = Auth.auth().currentUser?.uid else {
return
}
let senderId = ref.child("pinions").child(uid).child(userId).child("uid")
ref.child("users").child(senderId).observeSingleEvent(of: .value, with: { snapshot in
//error is in the above line
let senderImage = snapshot.childSnapshot(forPath: "profileImageUrl").value as! String
senderPhoto.append(senderImage)
let senderImageUrl = URL.init(string: senderImage)
self.senderProfileImage.sd_setImage(with: senderImageUrl)
})
}
I think this is what you mean, first import Firebase:
import Firebase
then get the database reference:
class PinionNotificationsViewController: UIViewController {
var ref: DatabaseReference!
...
}
then your function can look like this if you know the UID:
func showQuestionAndAnswers() {
let uid = userId
ref = ref.child("pinions").child(uid)
ref.observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
let value = snapshot.value as? NSDictionary
let optionA = value?["option A"] as! String
let optionB = value?["option B"] as! String
print(optionA) // <- prints: "Bbbb"
print(optionB) // <- prints: "Dddd"
})
}
If you don't know the uid you can use
ref = ref.child("pinions")
ref.observe(.childAdded) { (snapshot) in
// Get user value
let value = snapshot.value as? NSDictionary
let optionA = value?["option A"] as! String
// then you can append these values into an array it will go through all of them eg:
self.answersArray.append(optionA) // answers array will contain all optionA values from all children in the database under `"pinions"`
}
This way in my experience would match the database order so in your array all options would be in the order of their children as they are in the database.
Hope this helps!

Swift child is returning Null when nested or ObserveSingleEvent, returns fine when not nested

I am trying to retrieve a child of a child. the entire snapshotValue returns null. When I retrieve the same data as a child (not nested) it retrieves fine.
I'm Using XCode 10 and Swift 4
To troubleshooting purposes, I have two nodes called 'Promoters'. One at the root and one nested inside a 'Partners' child (preferred). I will remove the top level node when I get the nested node working.
Here is the data structure:
"Partners" : {
"Acts" : [hidden],
"Promoters" : [ null, {
"Cell" : hidden,
"Contact Name" : “hidden”,
"Email" : “hidden”,
"Facebook" : “hidden“,
"Title" : "CHORD Productions"
} ]
},
"Promoters" : {
"chord" : {
"Title" : "Chord Productions"
}
}
This retrieves the data I'm looking for (a list of Titles to populate a picker):
let promotersDB = Database.database().reference().child("Promoters")
promotersDB.observe(.childAdded) { (snapshot) in
let snapshotValue = snapshot.value as! Dictionary<String, String>
let promoterName = snapshotValue["Title"]!
let promoter = PromoterClass()
promoter.promoterName = promoterName
self.promoterArray.append(promoter)
let isSuccess = true
completion(isSuccess)
}
This returns nil:
let promotersDB = Database.database().reference().child("Partners").child("Promoters")
promotersDB.observe(.childAdded) { (snapshot) in
let snapshotValue = snapshot.value as! Dictionary<String, String>
let promoterName = snapshotValue["Title"]!
let promoter = PromoterClass()
promoter.promoterName = promoterName
self.promoterArray.append(promoter)
let isSuccess = true
completion(isSuccess)
}
I'd prefer observeSingleEvent, but this also returns nil:
let promotersDB = Database.database().reference().child("Promoters")
promotersDB.observeSingleEvent(of: .value, with: { (snapshot) in
let snapshotValue = snapshot.value as! Dictionary<String, String>
let promoterName = snapshotValue["Title"]!
let promoter = PromoterClass()
promoter.promoterName = promoterName
self.promoterArray.append(promoter)
let isSuccess = true
completion(isSuccess)
})
The error is:
Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
**I am using force unwrapping for now to be reviewed at a later date after investigating how much of the data integrity can be done with backend rules :)
Any assistance would be greatly appreciated.
I think firebase usually suggests the Fan Out method when dealing with data trees, so you shouldn't nest array like "Promoters" anyways.
You'll also want to identify each of the Promoters by a uid instead of by their name (incase you have to change their name in the system).
So if you want, try to restructure your data like this:
let uid = Database.database().reference().child("Promoters").childByAutoId()
// uid will be some super long string of random letters and numbers, for example like: 1hjK2SCRV2fhCI0Vv3plGMct3mL2
"Promoters" : {
"1hjK2SCRV2fhCI0Vv3plGMct3mL2" : {
"Title" : "Chord Productions"
}
}
And then when you want to query values within that promoter's branch:
let titleRef = Database.database().reference().child("Promoters").child(uid)
titleRef.observeSingleEvent(of: .value) { (snapshot) in
if let dict = snapshot.value as? [String: AnyObject] {
let promoter = PromoterClass()
promoterName = dict["Title"] as? String
promoter.promoterName = promoterName
}
}

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)")
}
}