Currently I'm having some problems with this bit of code that is loading data from firebase database into an array. Since this is inside of viewDidLoad I have to empty my array food = [] before loading the data into it, if I don't then it will duplicate all the objects and I will have double duplicates the second time it loads, triple the third time and etc... However this was not a good fix for multiple reasons so what I would like is that it would only add new objects from the database with .childAdded however if I just switch out .value with .childAdded it will crash, I get a Thread 1: signal SIGABRT on this line: let dict = user_snap.value as! [String: String?]. I am pretty new to swift and don't know how to fix this, would really appreciate some help.
let parentRef = Database.database().reference().child("Recipes")
let storage = Storage.storage()
parentRef.observe(.value, with: { snapshot in
if ( snapshot.value is NSNull ) {
// DATA WAS NOT FOUND
print("– – – Data was not found – – –")
} else {
//Clears array so that it does not load duplicates
food = []
// DATA WAS FOUND
for user_child in (snapshot.children) {
let user_snap = user_child as! DataSnapshot
let dict = user_snap.value as! [String: String?]
//Defines variables for labels
let recipeName = dict["Name"] as? String
let recipeDescription = dict["Description"] as? String
let downloadURL = dict["Image"] as? String
let storageRef = storage.reference(forURL: downloadURL!)
storageRef.getData(maxSize: 1 * 1024 * 1024) { (data, error) -> Void in
let recipeImage = UIImage(data: data!)
food.append(Element(name: recipeName!, description: recipeDescription!, image: recipeImage!))
self.tableView.reloadData()
}
}
}
})
let dict = user_snap.value as! [String: String?]
Instead of
let dict = snapshot.value as! Dictionary<String, String>
and maybe you can do null test :
let dict = snapshot.value as! Dictionary<String, String>
if let recipeName = dict["Name"] as String!, let recipeDescription = dict["Description"] as String!, let downloadURL = dict["Image"] as String! {
let storageRef = storage.reference(forURL: downloadURL)
storageRef.getData(maxSize: 1 * 1024 * 1024) { (data, error) -> Void in
let recipeImage = UIImage(data: data!)
food.append(Element(name: recipeName, description: recipeDescription, image: recipeImage!, downloadURL: downloadURL))
self.tableView.reloadData()
}
}else {
print("Error! Could not decode data")
}
try this. It should work
for child in snapshot.children.allObjects as! [FIRDataSnapshot] {
let dict = child.value as! Dictionary<String, Any>
//.....
}
Related
Hello i am creating Chat App Using Swift And Firebase i am fetching messages and populating in tableview but when i send new message then tableview dislplaying multiple entry i am useing below code for fetching messages
func fetchAllMessage(){
guard let uid = Auth.auth().currentUser?.uid else { return }
let fetchMsgGroup = Database.database().reference().child("user-messages_group_iOS").child(uid).child(self.chatID)
fetchMsgGroup.observe(.value, with: { (snapshot) in
if snapshot.exists(){
if let dictonary = snapshot.value as? [String:AnyObject]{
self.groupMessageData.removeAll()
if let userMessages = dictonary["userMessages"] as? [String:AnyObject]{
for (key, _) in userMessages{
let messagesFrtchRef = Database.database().reference().child("messages_iOS").child(key)
messagesFrtchRef.observe(.value, with: { (snapshot1) in
if snapshot1.exists(){
if let dict = snapshot1.value as? [String:AnyObject]{
let fromId = dict["fromId"] as! String
let messageUID = dict["messageUID"] as! String
let seen = dict["seen"] as! Bool
let status = dict["status"] as! String
let text = dict["text"] as! String
let timestamp = dict["timestamp"] as! Double
let told = dict["told"] as! String
let messages = GroupMessage(fromId: fromId, messageUID: messageUID, seen: seen, status: status, text: text, timestamp: timestamp, told: told)
self.groupMessageData.insert(messages, at: 0)
}
self.tblListView.reloadData()
}else{
}
}, withCancel: nil)
}
}
}
}else{
}
}, withCancel: nil)
}
i have tried everything like clearing removing also clearing observer when needed but its not work enough for me is their anyone have any solution for this then please help me
can anyone help me to solve this out
I am trying to convert items in an optional dictionary into individual strings so I can loop through them and convert them into URLs. But have been unable to do so.
Here is function which I use to fetch images from firebase which returns this optional dictionary which is also included below:
func fetchAllUsersImages() {
print("inside func")
self.ref.child("Posts").child(self.userID).child(self.postNum).observe(.childAdded, with: { snapshot in
print("inside closure")
// print(URL(string: snapshot.value as! String))
// let postSnap = snapshot.childSnapshot(forPath: self.postNum)
// let imageUrlSnap = postSnap.childSnapshot(forPath: "ImageUrl")
print(snapshot.value, "value")
// guard let allImages = imageUrlSnap.children.allObjects as? [DataSnapshot] else { return print("the code failed here")}
guard let allImages = snapshot.value as? [DataSnapshot] else { return print("the code failed here")}
// let snapshotVal = snapshot.value
// let snapshotValValue = snapshotVal as! String
// print(snapshotValValue, "snapshot as string value")
for image in allImages {
print(image, "image")
}
print(snapshot.key, "key")
print(snapshot.value, "value")
print(snapshot.children, "cjildren")
print(allImages)
print()
})
}
Output of snapshot.value:
Optional({
image1 = "https://firebasestorage.googleapis.com/v0/b/base.appspot.com/o/ijzAnEdyKNbhPsQVH6a8mOa1QpN2%2Fpost1%2Fimage1?alt=media&token=c2f396fd-717d-4192-909a-db390dd23143";
image2 = "https://firebasestorage.googleapis.com/v0/b/atabase.appspot.com/o/ijzAnEdyKNbhPsQVH6a8mOa1QpN2%2Fpost1%2Fimage2?alt=media&token=359b8527-f598-4f9a-934e-079cee21fd15";
})
Based on the answer provided I did the followoing:
func fetchAllUsersImages() {
print("inside func")
self.ref.child("Posts").child(self.userID).child(self.postNum).observe(.childAdded, with: { snapshot in //error here
var images: [URL] = []
if let snapShotValue = snapshot.value as? [String: String] {
for (_, value) in snapShotValue {
if let imageURL = URL(string: value) {
print(imageURL, "image url here")
let imageAsData = try Data(contentsOf: imageURL)
let image = UIImage(data: imageAsData)
let ImageObject = Image()
ImageObject.image = image
self.arrayOfImgObj.append(ImageObject)
self.tableView.reloadData()
}
}
}
})
}
However on the 3rd line I get
Unable to infer closure type in the current context
Edit:
To fix this error put the code, at the deepest part of the code, in a do block amd include a catch block also. This will fix the error.
Well first you need to check if the optional Dictionary exists then loop the dictionary for each key-value pair. Here is a way to do it:
var imageURLs: [URL] = []
if let snapShotValue = snapshot.value as? [String: String] { // Cast optional dictionary to a Dictionary of String keys and String values
// Cast would fail if snapshot.value is nil or has a different Dictionary setup.
for (key, value) in snapShotValue { // you can change key to _ since we are not using it
if let imageURL = URL(string: value) { // Get URL value from string
imageURLs.append(imageURL) // Add new URL to parsed URLs
}
}
}
So once the process is finished you'll have the images in imageURLs variable.
My code makes calls to my firebase database, but the order in which it receives the data is incorrect in terms of the function call. It calls the data from ref3 then ref2 then ref4 and I would like for it to retrieve the data in order of ref2, ref3, ref4 of course. No matter what it will always do it in this order.
var ref2: DatabaseReference?
var ref3: DatabaseReference?
var ref4: DatabaseReference?
ref2 = Database.database().reference().child("User data").
ref3 = Database.database().reference().child("User Info").child("Name")
ref4 = Database.database().reference().child("User Info").child("Address")
ref2?.observe(DataEventType.value, with:{(DataSnapshot) in
if DataSnapshot.childrenCount > 0{
for data in DataSnapshot.children.allObjects as![DataSnapshot]{
let proObj = data.value as? [String: AnyObject]
let p: String = proObj?["Username"] as! String
let n: String = proObj?["User login"] as! String
}
}
})
ref3?.observe(DataEventType.value, with:{(DataSnapshot) in
if DataSnapshot.childrenCount > 0{
for data in DataSnapshot.children.allObjects as![DataSnapshot]{
let proObj = data.value as? [String: AnyObject]
let p: String = proObj?["User first name"] as! String
let n: String = proObj?["User last name"] as! String
}
}
})
ref4?.observe(DataEventType.value, with:{(DataSnapshot) in
if DataSnapshot.childrenCount > 0{
for data in DataSnapshot.children.allObjects as![DataSnapshot]{
let proObj = data.value as? [String: AnyObject]
let p: String = proObj?["User email"] as! String
}
}
})
When querying data from your firebase database, you are performing an asynchronous call. To put things in simple terms, your code is executed on a different thread and, subsequently, performs parallel operations. This is exactly what is happening in your case.
You are observing data from three different references, and even though you have defined their sequence programmatically, nothing guarantees that the code within the completion handler blocks of your observers will run in that exact same sequence.
If you want to run them sequentially, then you have to nest your observers so that the next database query is executed only after the previous one has finished.
The below should hypothetically work
ref2?.observe(DataEventType.value, with: { (DataSnapshot) in
if DataSnapshot.childrenCount > 0 {
for data in DataSnapshot.children.allObjects as! [DataSnapshot] {
let proObj = data.value as? [String: AnyObject]
let p: String = proObj?["Username"] as! String
let n: String = proObj?["User login"] as! String
}
}
ref3?.observe(DataEventType.value, with: { (DataSnapshot) in
if DataSnapshot.childrenCount > 0 {
for data in DataSnapshot.children.allObjects as! [DataSnapshot] {
let proObj = data.value as? [String: AnyObject]
let p: String = proObj?["User first name"] as! String
let n: String = proObj?["User last name"] as! String
}
}
ref4?.observe(DataEventType.value, with: { (DataSnapshot) in
if DataSnapshot.childrenCount > 0 {
for data in DataSnapshot.children.allObjects as![DataSnapshot] {
let proObj = data.value as? [String: AnyObject]
let p: String = proObj?["User email"] as! String
}
}
}) // ref4 observer
}) // ref3 observer
}) // ref2 observer
Please find my code below. How can we append filter data on array from Firebase?
var childrenList = [DatabaseList]()
let ref = Database.database().reference(withPath: "Messages")
let query = ref.queryOrdered(byChild: "VideoID").queryEqual(toValue: "12345").observe(.value, with: { (snapshot) in
for childSnapshot in snapshot.children{
print(childSnapshot)
self.childrenList.append(snapshot)
}
})
DispatchQueue.main.async {
self.tableView.reloadData()
}
let ref = Database.database().reference(withPath: "Messages")
let query = ref.queryOrdered(byChild: "VideoID").queryEqual(toValue: "12345").observe(.value, with: { (snapshot) in
print(snapshot)
for (childSnapshotId, childSnapshotValue) in snapshot {
if let dataListDict = childSnapshotValue as? [String: AnyObject] {
//Init you newModel with the dataListDict here
let newModel = DatabaseList(dict: dataListDict)
print(childSnapshot)
self.childrenList.append(newModel)
}
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
})
class DatabaseList : NSObject {
var messageBody : String?
var name : String?
var videoID : String?
init(dict: [String: AnyObject]) {
messageBody = dict["MessageBody"]
name = dict["Name"]
videoID = dict["videoID"]
}
}
Your query is correct but there are few mistakes in finishing block.
self.childrenList.append(snapshot) snapshot is an instance of DataSnapshot not a DatabaseList so you can not append it like this.
for childSnapshot in snapshot.children {
/// childSnapshot is an instance of DataSnapshot not a dictionary but its value will be
guard let data = (childSnapshot as! DataSnapshot).value else {continue}
let dataDict = data as! Dictionary<String, Any>
/// Initializing the new object of DatabaseList and passing the values from data
let list: DatabaseList = DatabaseList()
list.messageBody = dataDict["MessageBody"] as? String
list.name = dataDict["Name"] as? String
list.videoID = dataDict["VideoID"] as? String
/// This is correct, and now you can append it to your array.
childrenList.append(list)
}
Apart from this you will have to reload the tableView inside the finishing block not below the block because this is an asynchronous request and data will come later.
Also its always better to check the data existence. snapshot.exists().
One more suggestion if you want to fetch the data just once then do not use .observe use .observeSingleEvent instead. .observe will fire the block every time there is any change at this node.
Here is the full code snippet.
let query = ref.queryOrdered(byChild: "VideoID").queryEqual(toValue: "12345").observe(.value, with: { (snapshot) in
if !snapshot.exists() {
// Data doesn't exist
return
}
for childSnapshot in snapshot.children {
guard let data = (childSnapshot as! DataSnapshot).value else {continue}
let dataDict = data as! Dictionary<String, Any>
let list: DatabaseList = DatabaseList()
list.messageBody = dataDict["MessageBody"] as? String
list.name = dataDict["Name"] as? String
list.videoID = dataDict["VideoID"] as? String
childrenList.append(list)
}
/// Reload your tableView here
DispatchQueue.main.async {
self.tableView.reloadData()
}
})
And expecting the class model like below:
class DatabaseList: NSObject {
var messageBody: String?
var name: String?
var videoID: String?
}
In this order detail array i am having 10 dictionaries but i need to display only first dictionary can any one help me how to implement this ?
http://www.json-generator.com/api/json/get/bUKEESvnvS?indent=2
here is my code shown below
func downloadJsonWithURL() {
let url = NSURL(string: self.url)
URLSession.shared.dataTask(with: (url as URL?)!, completionHandler: {(data, response, error) -> Void in
if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary {
self.orderdetailsArray = (jsonObj!.value(forKey: "Orders detail") as? [[String: AnyObject]])!
for array in self.orderdetailsArray {
let key = "OrderId"
let value = "#1000501"
for (key,value) in array{
if let addressDict = array as? NSDictionary{
if let orderid = addressDict.value(forKey: "OrderId"){
self.orderid.append(orderid as! String)
}
if let orderdate = addressDict.value(forKey: "OrderDate"){
self.orderdate.append(orderdate as! String)
}
if let subtotal = addressDict.value(forKey: "SubTotal"){
self.subTotal.append(subtotal as! Int)
}
if let Shipping = addressDict.value(forKey: "Shipping"){
self.shippingPrice.append(Shipping as! Int)
}
if let tax = addressDict.value(forKey: "Tax"){
self.tax.append(tax as! Int)
}
if let grandtotal = addressDict.value(forKey: "GrandTotal"){
self.grandTotal.append(grandtotal as! Int)
}
if let shippingAddress = addressDict.value(forKey: "ShippingAddress"){
self.shippingAddress.append(shippingAddress as AnyObject)
}
if let shippingMethod = addressDict.value(forKey: "ShippingMethod"){
self.shippingMethod.append(shippingMethod as AnyObject)
}
if let billingAddress = addressDict.value(forKey: "BillingAddress"){
self.billingAddress.append(billingAddress as AnyObject)
}
if let paymentMethod = addressDict.value(forKey: "PayMentMethod"){
self.paymentMethod.append(paymentMethod as AnyObject)
}
self.itemsArray = addressDict.value(forKey: "Items detail") as! [[String : AnyObject]]
}
}
}
OperationQueue.main.addOperation({
self.tableDetails.reloadData()
})
}
}).resume()
}
Do this. :
let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary
guard let Ordersdetail = jsonObj["Orders detail"] as? [NSDictionary] else {
print("Cannot find key 'Orderdetails' in \(jsonObj)")
return
}
To access the contents of the first dictionary do this:
var orderid = Ordersdetail[0]["OrderId"]!
var shippingadress = Ordersdetail[0]["ShippingAddress"]!
var total = Ordersdetail[0]["GrandTotal"]!
var subtotal = Ordersdetail[0]["SubTotal"]!
var tax = Ordersdetail[0]["Tax"]!
var shipping = Ordersdetail[0]["Shipping"]!
Hi if you want first dictionary of that
self.orderdetailsArray
then
if let firstDictInfo = self.orderdetailsArray.first as? [String:Any] {
// Do your stuff here
print(firstDictInfo["OrderId"])
}
Instead of looping through the whole dictionary is dictionaries, you should just take the first dictionary and only parse that. There was also quite a few other conceptual problems with your code. In Swift, don't use NSDictionary, but use the native Swift version, Dictionary, which keeps the type information of its contents. Also, use conditional casting to make sure your program doesn't crash even if the received data is wrong/unexpected and don't use force unwrapping of optionals.
Also, when parsing a JSON response in Swift, in general it is not necessary and not a good idea to iterate through the key-value pairs of the dictionaries in the response. You should know what data structure you expect, otherwise you can't parse it properly and since you can directly access dictionary values in Swift if you know the key it corresponds to, there's no need to iterate through the dictionary in a loop.
func downloadJsonWithURL() {
let url = URL(string: self.url)
URLSession.shared.dataTask(with: url!, completionHandler: {(data, response, error) -> Void in
if let jsonObj = (try? JSONSerialization.jsonObject(with: data!, options: .allowFragments)) as? [String:Any] {
guard let self.orderdetailsArray = jsonObj["Orders detail"] as? [[String: AnyObject]] else {return}
guard let firstOrderDetails = self.orderdetailsArray.first else {return}
let key = "OrderId"
let value = "#1000501"
if let ordered = firstOrderDetails["OrderId] as? String {
self.orderid.append(orderid)
}
if let orderdate = firstOrderDetails["OrderDate"] as? String{
self.orderdate.append(orderdate)
}
if let subtotal = firstOrderDetails["SubTotal"] as? Int{
self.subTotal.append(subtotal)
}
if let shipping = firstOrderDetails["Shipping"] as? Int{
self.shippingPrice.append(shipping)
}
if let tax = firstOrderDetails["Tax"] as? Int{
self.tax.append(tax)
}
if let grandtotal = firstOrderDetails["GrandTotal"] as? Int{
self.grandTotal.append(grandtotal)
}
if let shippingAddress = firstOrderDetails[ "ShippingAddress"] as? AnyObject{ //why don't you store it as a String?
self.shippingAddress.append(shippingAddress)
}
if let shippingMethod = firstOrderDetails[ "ShippingMethod"] as? AnyObject{
self.shippingMethod.append(shippingMethod)
}
if let billingAddress = firstOrderDetails[ "BillingAddress"] as? AnyObject {
self.billingAddress.append(billingAddress)
}
if let paymentMethod = firstOrderDetails ["PayMentMethod"] as? AnyObject{
self.paymentMethod.append(paymentMethod)
}
guard let itemDetails = firstOrderDetails["Items detail"] as? [[String : AnyObject]] else {return}
self.itemsArray = itemDetails
}
}
}
OperationQueue.main.addOperation({
self.tableDetails.reloadData()
})
}
}).resume()
}
I haven't compiled and run the code, so make sure you check for any typos/inconsistencies. Also, make sure you change the types of the objects you store are AnyObjects to specific types.