Here is my scenario:
1) datasource to my tableView is an array called books - var books:[BookItem]
2) I populate books anytime some data in db has changed
3) I create BookItem by joining data from two tables, since BookItem consists of some user-specific data and some general data about the book itself.
I wrote a method called createDataSource that takes care of populating books array with BookItems
func createDataSource()
{
self.books.removeAll()
let userId:String = UserDefaults.standard.value(forKey: "UserId") as! String
let bookForUserRef = self.ref!.child("users").child(userId).child("userbooks")
bookForUserRef.observe(.value, with: { snapshot in
var i = 0
for element in snapshot.children
{
i = i + 1
let item:FIRDataSnapshot = element as! FIRDataSnapshot
let postDict = item.value as! [String : AnyObject]
let key = item.key
let booksRef = self.ref!.child("books").child(key)
booksRef.observe(.value, with: { snapshot in
let bookItem = (snapshot as! FIRDataSnapshot).value as! [String : AnyObject]
let id = booksRef.key
print(id)
let record = [bookItem["title"] as! String, bookItem["author"] as! String, "0", "\(bookItem["pagesCount"] as! Int)"]
let item = BookItem(title: bookItem["title"] as! String, author: bookItem["author"] as! String, pagesCount: bookItem["pagesCount"] as! Int, currentPage: postDict["currentPage"] as! Int, language: bookItem["language"] as! String, year: nil, ISBN: nil, id: id, added: postDict["added"] as? String ?? "21")
self.books.append(item)
self.tableView.reloadData()
})
}
})
}
Anytime I call this method, all existing elements gets duplicated in books array.
I thought it might have something to do with for loop, but after debugging it seems alright.
I suppose there is something specific with observers in firebase that duplicates the elements. I am rather new to firebase and I suppose my approach might be a bit messed and confusing.
What should be modified in the createDataSource() method, to get rid of elements duplication?
Also, is my approach to working with firebase and tableView correct?
Thanks to #ntoonio comment I got it solved
func createDataSource()
{
let userId:String = UserDefaults.standard.value(forKey: "UserId") as! String
let bookForUserRef = self.ref!.child("users").child(userId).child("userbooks")
bookForUserRef.observe(.value, with: { snapshot in
var i = 0
self.books.removeAll()
for element in snapshot.children
{
i = i + 1
let item:FIRDataSnapshot = element as! FIRDataSnapshot
let postDict = item.value as! [String : AnyObject]
let key = item.key
let booksRef = self.ref!.child("books").child(key)
booksRef.observe(.value, with: { snapshot in
let bookItem = (snapshot as! FIRDataSnapshot).value as! [String : AnyObject]
let id = booksRef.key
print(id)
let record = [bookItem["title"] as! String, bookItem["author"] as! String, "0", "\(bookItem["pagesCount"] as! Int)"]
let item = BookItem(title: bookItem["title"] as! String, author: bookItem["author"] as! String, pagesCount: bookItem["pagesCount"] as! Int, currentPage: postDict["currentPage"] as! Int, language: bookItem["language"] as! String, year: nil, ISBN: nil, id: id, added: postDict["added"] as? String ?? "21")
self.books.append(item)
self.tableView.reloadData()
})
}
})
}
Related
When user doubletap on the image,the number of likes(heart) will increase for each post.
I tried this code but it does not work as expected(For eg when i double tap,it will load one post and duplicate it with different number of likes(Eg Post 1-1like,Post 1-2like,Post 1-3like).How do i display the updated number of likes without duplicating the post?(Post 1- Displaying the X number of likes where X=Incremental)
func loadPosts() {
let ref = Database.database().reference()
ref.child("posts").observe(.childAdded) { (snapshot: DataSnapshot) in
if let dict = snapshot.value as? [String: Any] {
guard let titleText = dict["title"] as? String else{return}
let locationDetails = dict["location"] as! String
let captionText = dict["caption"] as! String
let photoUrlString = dict["photoUrl"] as! String
let priceText = dict["price"] as! String
let categoryText = dict["category"] as! String
let usernameLabel = dict["username"] as! String
let profileImageURL = dict["pic"] as! String
let heartInt = dict["heart"] as! Int
let timestampString = dict["timestamp"] as! String
let post = Post(titleText: titleText, captionText: captionText, locationDetails: locationDetails, photoUrlString: photoUrlString, priceText: priceText,categoryText: categoryText, usernameLabel: usernameLabel, profileImageURL: profileImageURL, heartInt: heartInt, timestampString: timestampString)
//append(post) to array
self.posts.append(post)
print(self.posts)
self.collectionView.reloadData()
}
}
}
func delayCompletionHandler(completion:#escaping (() -> ())) {
let delayInSeconds = 0.5
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delayInSeconds) {
completion()
}
}
//CollectionView
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell{
cell.reloadEvents = {
self.delayCompletionHandler {
self.loadPosts()
}
}
}
To detect the heartTapped which is in another file:
#objc func heartTapped(){
print(" I heart u")
let ref = Database.database().reference()
heartInt1 += 1
ref.child("posts").child(timestamp).observeSingleEvent(of: .value, with: { (snapshot) in
if let dic = snapshot.value as? [String : AnyObject]{
var heartString = dic["heart"] as! Int
heartString += 1
ref.child("posts").child(self.timestamp).updateChildValues(["heart" : heartString])
}
})
reloadEvents?()
}
}
Silly me I should have added self.posts.removeAll()in loadPosts()
Solved.
Your solution is bad practice. When you deal with databases you have to think at scale, if Kim Kardashian that gets a heart every second uses your app, whats going to happen then, load and delete everything for every heart? Does that sound efficient?
You need to find a way to load only the number of hearts not everything in order to get the number of hearts.
A solution would be to add an extra child ref.child("hearts") and then have a loadHearts() where you will get just the number of hearts.
In order to populate my tableView, I append items (created from a struct) to a local array:
func loadList() {
var newAnnotations: [AnnotationListItem] = []
if let uid = Auth.auth().currentUser?.uid {
uidRef.child(uid).child("annotations").queryOrderedByKey().observeSingleEvent(of: .value, with: {snapshot in
for item in snapshot.children {
let annotationItem = AnnotationListItem(snapshot: item as! DataSnapshot)
newAnnotations.append(annotationItem)
}
annotationList = newAnnotations
self.tableView.reloadSections([0], with: .fade)
})
}
}
When I click a specific row, I am taken to a DetailViewController where it is only a large UITextView (named notes). The UITextView.text displayed is based on the selected indexPath.row and the "notes" value is retrieved from the array. Now the user is able to type some text and when they are done, the textViewDidEndEditing function is called:
func textViewDidEndEditing(_ textView: UITextView) {
notes.resignFirstResponder()
navigationItem.rightBarButtonItem = nil
let newNotes = self.notes.text
print(newNotes!)
}
Now I'd like to updateChildValues to newNotes to the child node "notes" in my JSON:
"users" : {
"gI5dKGOX7NZ5UBqeTdtu30Ze9wG3" : {
"annotations" : {
"-KuWIRBARv7osWr3XDZz" : {
"annotationSubtitle" : "1 Cupertino CA",
"annotationTitle" : "Apple Infinite Loop",
"notes" : "Does it work?!",
}
How can I access the selected autoID so I can update the specific notes node. So far the best I have is:
guard let uid = Auth.auth().currentUser?.uid else { return }
uidRef.child(uid).child("annotations").(somehow access the specific childID).updateChildValues(["notes": newNotes])
Any help will be greatly appreciated. Thanks in advance
UPDATE
The annotationListItem struct is created:
struct AnnotationListItem {
let key: String?
var annotationTitle: String?
let annotationSubtitle: String?
let notes: String?
let ref: DatabaseReference?
init(key: String = "", annotationTitle: String, annotationSubtitle: String, notes: String) {
self.key = key
self.annotationTitle = annotationTitle
self.annotationSubtitle = annotationSubtitle
self.notes = notes
self.ref = nil
}
init(snapshot: DataSnapshot) {
key = snapshot.key
let snapshotValue = snapshot.value as! [String: AnyObject]
annotationTitle = snapshotValue["annotationTitle"] as? String
annotationSubtitle = snapshotValue["annotationSubtitle"] as? String
notes = snapshotValue["notes"] as? String
ref = snapshot.ref
}
init(Dictionary: [String: AnyObject]) {
self.key = Dictionary["key"] as? String
self.annotationTitle = Dictionary["annotationTitle"] as? String
self.annotationSubtitle = Dictionary["annotationSubtitle"] as? String
self.notes = Dictionary["notes"] as? String
self.ref = nil
}
func toAnyObject() -> Any {
return [
"annotationTitle": annotationTitle as Any,
"annotationSubtitle": annotationSubtitle as Any,
"notes": notes as Any
]
}
}
UPDATE
This is how the annotationListItem is created to be stored in Firebase:
// Using the current user’s data, create a new AnnotationListItem that is not completed by default
let uid = Auth.auth().currentUser?.uid
guard let email = Auth.auth().currentUser?.email else { return }
let title = placemark.name
let subtitle = annotation.subtitle
let notes = ""
// declare variables
let annotationListItem = AnnotationListItem(
annotationTitle: title!,
annotationSubtitle: subtitle!,
notes: notes)
// Add the annotation under their UID
let userAnnotationItemRef = uidRef.child(uid!).child("annotations").childByAutoId()
userAnnotationItemRef.setValue(annotationListItem.toAnyObject())
I think you only need to do this:(since you have declared the note as global)
guard let uid = Auth.auth().currentUser?.uid else { return }
uidRef.child(uid).child("annotations").(note.key).updateChildValues(["notes": newNotes])
inside the method where you change the notes
If I am not mistaken you are creating an array of a custom object?
var newAnnotations: [AnnotationListItem] = []
You could do something like: var newAnnotations: [(key: String, value: [String : Any])] = [] (Any only if you are going to have Strings, Integers, ect. If it'll only be String then specify it as a String.
Accessing the key would be: newAnnotations[indexPath.row].key in your cellForRowAtIndex of your tableView. Accessing values would be: newAnnotations[indexPath.row].value["NAME"].
You can have a separate array that holds the key and just append it at the same time as your population:
for item in snapshot.children {
guard let itemSnapshot = task as? FDataSnapshot else {
continue
}
let id = task.key //This is the ID
let annotationItem = AnnotationListItem(snapshot: item as! DataSnapshot)
newAnnotations.append(annotationItem)
}
Another thing you could do is go up one more level in your firebase call:
if let uid = Auth.auth().currentUser?.uid {
uidRef.child(uid).child("annotations").observeSingleEvent(of: .value, with: {snapshot in
if snapshot is NSNull{
//Handles error
} else{
if let value = snapshot.value as? NSDictionary{ //(or [String: String]
//set localDictionary equal to value
}
}
self.tableView.reloadSections([0], with: .fade)
})
}
And then when you select a row: let selectedItem = localDictionary.allKeys[indexPath.row] as! String //This is the ID you pass to your viewController.
I am using firebase to load data into tableview cells, and here is the structure of my data.
struct postStruct {
let title : String!
let author : String!
let bookRefCode : String!
let imageDownloadString : String!
let status : String!
let reserved : String!
let category : String!
let dueDate : String!
}
Now I have the posts sorted alphabetically using
self.posts.sort(by: { $0.title < $1.title })
but I do not know how to place the cells that start with A in a section "A", and "B", and so on.
class DirectoryTableView: UITableViewController {
var posts = [postStruct]()
override func viewDidLoad() {
let databaseRef = Database.database().reference()
databaseRef.child("Books").queryOrderedByKey().observe(.childAdded, with: {
snapshot in
var snapshotValue = snapshot.value as? NSDictionary
let title = snapshotValue!["title"] as? String
snapshotValue = snapshot.value as? NSDictionary
let author = snapshotValue!["author"] as? String
snapshotValue = snapshot.value as? NSDictionary
let bookRefCode = snapshotValue!["bookRefCode"] as? String
snapshotValue = snapshot.value as? NSDictionary
let status = snapshotValue!["status"] as? String
snapshotValue = snapshot.value as? NSDictionary
let reserved = snapshotValue!["reserved"] as? String
snapshotValue = snapshot.value as? NSDictionary
let category = snapshotValue!["category"] as? String
snapshotValue = snapshot.value as? NSDictionary
let dueDate = snapshotValue!["dueDate"] as? String
snapshotValue = snapshot.value as? NSDictionary
self.posts.insert(postStruct(title: title, author: author, bookRefCode: bookRefCode, status: status, reserved: reserved, category: category, dueDate: dueDate) , at: 0)
self.tableView.reloadData()
})
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "cell")
let databaseRef = Database.database().reference()
let label1 = cell?.viewWithTag(1) as! UILabel
label1.text = posts[indexPath.row].title
let label2 = cell?.viewWithTag(2) as! UILabel
label2.text = posts[indexPath.row].author
let label3 = cell?.viewWithTag(3) as! UILabel
label3.text = posts[indexPath.row].bookRefCode
let label4 = cell?.viewWithTag(4) as! UILabel
label4.text = posts[indexPath.row].status
let label5 = cell?.viewWithTag(5) as! UILabel
label5.text = posts[indexPath.row].category
let image1 = cell?.viewWithTag(6) as! UILabel
image1.text = posts[indexPath.row].imageDownloadString
let label6 = cell?.viewWithTag(7) as! UILabel
label6.text = posts[indexPath.row].reserved
let label9 = cell?.viewWithTag(9) as! UILabel
label9.text = posts[indexPath.row].dueDate
return cell!
}
Any ideas, please help! I have tried to sort them with different methods, but I'm confused!
First, you'll need a new structure to save your posts grouped by it's first title letter: let postsAlphabetically: [String:[Post]]
You didn't specify the Swift version, so assuming you'll migrate to Swift 4, you can sort the data and group it using a single line:
let postsAlphabetically = Dictionary(grouping: self.posts) { $0.title.first! }
// E.g: ["A" : ["A book", "Another book"], "B" : ["Blue Book", "Black Book"]]
Later on, you'll use postsAlphabetically instead of self.posts in your cellForRowAt method.
P.S: Type names in Swift are written in upper camel case (PostSimple not postSimple). And the type itself is omitted (Author instead of AuthorClass).
I have the following hierarchy in my Firebase:
func invitedEvents() {
DataService.ds.REF_EVENTS.queryOrdered(byChild: "eventParticipant").observe(.childAdded, with: { (snap) in
print("KEEEY:\(snap.key)")
let value = snap.value as? NSDictionary
if snap.exists() , value?["eventParticipant"] != nil{
print("eventParticipant:\(value?["eventParticipant"])")
var valueArray = value?["eventParticipant"] as! [[String:AnyObject]]
for (index, element) in valueArray.enumerated() {
print("valueArray\(index): \(element)")
//self.selectedContacts.append(element as! Participant)
if element["partPhone"] as! String == "00 11 111 1111" {
print("KEY:\(snap.key) PHONE:\(element["partPhone"])")
self.eventKey = snap.key
print("snap.key:\(snap.key)")
//child("eventItem").child("eventOrganized").queryOrdered(byChild: snap.key).queryEqual(toValue: "1")
DataService.ds.REF_USERS.child("eventItem").child("eventOrganized").child(snap.key).observe(.value, with: { (snapshot) in
print("With Snapshot Key: \(snapshot)")
})
//DataService.ds.REF_USERS.queryOrdered(byChild: "provider").queryEqual(toValue: "email").observe(.value, with: { (snapshot) in
// print("With Provider:\(snapshot.value)")
//})
DataService.ds.REF_EVENTS.child(self.eventKey).observe(.value, with: { (snap) in
print(snap)
if snap.value is NSNull {
print("Event is not found!")
}
else {
let value = snap.value as? NSDictionary
print(value?["eventLocation"] ,
value?["eventCurrency"],
value?["eventDesc"],
value?["eventBeginDate"],
value?["eventEndDate"],
value?["eventCurrency"],
value?["eventLocation"],
value?["eventName"],
value?["eventDesc"],
value?["eventPrice"])
if
let eventAddress = value?["eventLocation"] ,
let eventCurrency = value?["eventCurrency"],
let eventDesc = value?["eventDesc"],
let eventBeginDate = value?["eventBeginDate"],
let eventEndDate = value?["eventEndDate"],
let eventImg = value?["eventCurrency"],
let eventLocation = value?["eventLocation"],
let eventName2 = value?["eventName"],
let eventNote = value?["eventDesc"],
let eventPrice = value?["eventPrice"]
{
let eventDetails = Event(
eventAddress: value?["eventLocation"] as! String,
eventCurrency: value?["eventCurrency"] as! String,
eventDesc: value?["eventDesc"] as! String,
eventBeginDate: value?["eventBeginDate"] as! String,
eventEndDate: value?["eventEndDate"] as! String,
eventImg: value?["eventCurrency"] as! String,
eventLikes: 1,
eventLocation: value?["eventLocation"] as! String,
eventName: value?["eventName"] as! String,
eventNote: value?["eventDesc"] as! String,
eventPrice: value?["eventPrice"] as! String,
eventCreated: value?["eventCreated"] as! String,
eventStatus: 0 as! Int?
)
//print(eventDetails)
let eventName = value?["eventName"] as! String
self.userPastEventNameList.append(eventName)
self.events.append(eventDetails)
print(eventAddress,eventCurrency,eventDesc, eventBeginDate, eventEndDate, eventImg, eventLocation, eventName2, eventNote, eventPrice)
}
else {
print("NULL FOUND")
}
}
DispatchQueue.main.async{
self.tableView.reloadData()
}
self.removeLoadingScreen()
})
//})
}
}
}
})
}
and snap.keys are in print("snap.key:(snap.key)"):
snap.key:-KePXAuTADuTLTsZ7qbe
snap.key:-KePjqMKrARQmp-gTss-
snap.key:-KePsCS5rB4lTwy6GWea
snap.key:-KePwdRQPFFX30_GDAOK
snap.key:-KePyNVghX4MkSxI_1fx
snap.key:-KePz200QsJp6CSb3bVN
If I'm using this, then I get a snapshot:
print("snap.key:\(snap.key)")
DataService.ds.REF_USERS.queryOrdered(byChild: "provider").queryEqual(toValue: "email").observe(.value, with: { (snapshot) in
print("With Provider:\(snapshot.value)")})
Result:
With Provider:Optional({
JIbrGLdfCDUpMUOHEu7KzRye8ZK2 = {
provider = email;
};
})
If I'm using this, then I don't get any result:
print("snap.key:\(snap.key)")
DataService.ds.REF_USERS.child("eventItem").child("eventOrganized").queryOrdered(byChild: snap.key).queryEqual(toValue: 1).observe(.value, with: { (snapshot) in ("With Snapshot Key:\(snapshot.value)")})
Result:
With Snapshot Key:Optional(<null>)
Somebody could help me? What am I doing wrong?
Right,
DataService.ds.REF_USERS.child("eventItem").child("eventOrganized").queryOrdered(byChild: snap.key).queryEqual(toValue: 1).observe(.value, with: { (snapshot) in ("With Snapshot Key:\(snapshot.value)")})
That doesn't work because you're missing a reference to the V9T3cEgEGPRmlkMQb32hxa5gG7L2 node.
If you want all the eventOrganized nodes with a value of 1 under that key then you need to use the following query.
DataService.ds.REF_USERS.child("V9T3cEgEGPRmIkMQb32hxa5gG7L2").child("eventItem").child("eventOrganized").queryOrderedByValue().queryEqual(toValue: 1).observe(.value, with: { (snapshot) in
print("Snapshot Value:\(snapshot.value)")
})
Obviously you don't want to reference V9T3cEgEGPRmlkMQb32hxa5gG7L2 directly, you should store this key in a variable for reusability's sake (maybe you have, I can't tell in your code). I think that's what you wanted.
First, I have checked these answers that do not help me :
Swift JSON error, Could not cast value of type '__NSArrayM' (0x507b58) to 'NSDictionary' (0x507d74)
Get data from Firebase
When retrieving data from Firebase (3.x), I have an error that occurs which is :
Could not cast value of type '__NSArrayM' (0x10ca9fc30) to 'NSDictionary' (0x10caa0108).
with this code and tree :
Tree :
Retrieving function :
func retrievePlanes() {
print("Retrieve Planes")
ref = FIRDatabase.database().reference(withPath: "results")
ref.observe(.value, with: { snapshot in
var newItems: [Planes] = []
for item in snapshot.children {
let planesItem = Planes(snapshot: item as! FIRDataSnapshot)
newItems.append(planesItem)
}
self.planes = newItems
self.tableView.reloadData()
})
}
Planes.swift - To manage the data
import Foundation
import Firebase
import FirebaseDatabase
struct Planes {
let key: String!
let name: String!
let code:String!
let flightRange: Int?
let typicalSeats: Int?
let maxSeats: Int?
let wingSpan: String!
let takeoffLength: Int?
let rateClimb: Int?
let maxCruiseAltitude: Int?
let cruiseSpeed: String!
let landingLength: Int?
let engines: String!
let votes: Int?
let data: String!
let imagePlane:String!
let imageTakenFrom: String!
let ref: FIRDatabaseReference?
init(name: String, code: String, flightRange: Int, typicalSeats: Int, maxSeats: Int, wingSpan: String, takeoffLength: Int, rateClimb: Int, maxCruiseAltitude: Int, cruiseSpeed: String, landingLength: Int, engines: String, votes: Int, data: String, imagePlane: String, imageTakenFrom: String, key: String = "") {
self.key = key
self.name = name
self.code = code
self.flightRange = flightRange
self.typicalSeats = typicalSeats
self.maxSeats = maxSeats
self.wingSpan = wingSpan
self.takeoffLength = takeoffLength
self.rateClimb = rateClimb
self.maxCruiseAltitude = maxCruiseAltitude
self.cruiseSpeed = cruiseSpeed
self.landingLength = landingLength
self.engines = engines
self.votes = votes
self.data = data
self.imagePlane = imagePlane
self.imageTakenFrom = imageTakenFrom
self.ref = nil
}
init(snapshot: FIRDataSnapshot) {
ref = snapshot.ref
key = snapshot.key
let snapshotValue = snapshot.value as! [String:AnyObject]
name = snapshotValue["name"] as! String
code = snapshotValue["code"] as! String
flightRange = snapshotValue["intFlightRange"] as? Int
typicalSeats = snapshotValue["intTypicalSeats"] as? Int
maxSeats = snapshotValue["intMaxSeats"] as? Int
wingSpan = snapshotValue["wingSpan"] as! String
takeoffLength = snapshotValue["intTakeoffLength"] as? Int
rateClimb = snapshotValue["intRateClimb"] as? Int
maxCruiseAltitude = snapshotValue["intMaxCruiseAltitude"] as? Int
cruiseSpeed = snapshotValue["cruiseSpeed"] as! String
landingLength = snapshotValue["intLandingLength"] as? Int
engines = snapshotValue["engines"] as! String
votes = snapshotValue["votes"] as? Int
data = snapshotValue["data"] as! String
imagePlane = snapshotValue["planeImage"] as! String
imageTakenFrom = snapshotValue["imageTakenFrom"] as! String
}
on the line : let snapshotValue = snapshot.value as! [String:AnyObject]
I suppose that is due to the snapshot value that can't be retrieved under [String:AnyObject] because of the Int below.
(It is working when I only have String in another case).
I also know that Firebase "converts" the JSON tree to these objects [link]:
NSString
NSNumber
NSArray
NSDictionnary
but I can't figure out what has to be changed in the snapshot.value line to make it work.
Thanks for your help.
EDIT : I just sent a troubleshooting request. Will post updates.
EDIT 2: See Jay's answer. In my case the tree was wrong.
I took your code and shrunk it down a bit for testing, and it's working. (note Firebase 2.x on OS X and Swift 3 but the code is similar)
Firebase structure:
"what-am" : {
"results" : [ {
"code" : "738/B738",
"data" : "Boeing",
"engines" : "Rolls"
}, {
"code" : "727/B727",
"data" : "Boeing",
"engines" : "Pratt"
} ]
}
Here's the Planes struct
struct Planes {
var code:String!
var data: String!
var engines: String!
init(code: String, data: String, engines: String ) {
self.code = code
self.data = data
self.engines = engines
}
init(snapshot: FDataSnapshot) {
let snapshotValue = snapshot.value as! [String:AnyObject]
code = snapshotValue["code"] as! String
data = snapshotValue["data"] as! String
engines = snapshotValue["engines"] as! String
}
}
and then the code that reads in two planes, populates and array and then prints the array.
let ref = self.myRootRef.child(byAppendingPath: "what-am/results")!
ref.observe(.value, with: { snapshot in
if ( snapshot!.value is NSNull ) {
print("not found")
} else {
var newItems: [Planes] = []
for item in (snapshot?.children)! {
let planesItem = Planes(snapshot: item as! FDataSnapshot)
newItems.append(planesItem)
}
self.planes = newItems
print(self.planes)
}
})
and finally the output
[Swift_Firebase_Test.Planes(code: 738/B738, data: Boeing, engines: Rolls),
Swift_Firebase_Test.Planes(code: 727/B727, data: Boeing, engines: Pratt)]
Key and name are nil as I removed then from the Planes structure.
The line you asked about
let snapshotValue = snapshot.value as! [String:AnyObject]
is valid as the snapshot contains a series of key:value pairs so String:AnyObject works.
This line changed due to Swift 3
for item in (snapshot?.children)!
but other than that, the code works.
Try this to ensure you are reading the correct node. This reads the above structure and prints out each engine type. (tested and works)
let ref = self.myRootRef.child(byAppendingPath: "what-am/results")!
ref.observe(.value, with: { snapshot in
if ( snapshot!.value is NSNull ) {
print("not found")
} else {
for child in (snapshot?.children)! {
let snap = child as! FDataSnapshot
let dict = snap.value as! [String: String]
let engines = dict["engines"]
print(engines!)
}
}
})
I think you are having an extra array in your results key-value on the firebase data.
You should try removing that array or
You may retrieve dictionary from first index of the array like;
// .. your code
let snapshotValue = (snapshot.value as! [AnyObject])[0] as! [String:AnyObject];
// .. your code
In your struct class make sure of these things:-
Avoid declaring your variables as :Int? because that's practically nil, change them to :Int!
Your key in your firebase is an Int and you are declaring your key in struct as let key: String!, Change it to let key: Int!
Prefer your snapshot dictionary declaration as let snapshotValue = snapshot.value as! [AnyHashable:Any] (as per swift 3)
Then your init function to :-
Just change the line
let snapshotValue = snapshot.value as! [String:AnyObject]
To
let snapshotValue = (snapshot.value as! NSArray)[0] as! [String:AnyObject]
update FIRDataSnapshot to DataSnapshot Swift 4
Below is an example for Swift 4. Where you need to change FIRDataSnapshot to DataSnapshot
func fetchChats(chatId: String) {
ref.child("chats").child("SomeChildId").observe(.value) { (snapshot) in
for child in snapshot.children {
let data = child as! DataSnapshot //<--- Update this line
let dict = data.value as! [String: AnyObject]
let message = dict["message"]
print(message!)
}
}
}