I added refreshing capabilities for my tableView when the user pulls down on it. This works most of the time but occasionally I'll get a fatal error, Index out of range. The callback thats called to refresh the table does the following:
1) Empty's out data structures holding data to be displayed
2) Fetch data from Firebase database
3) Parse data into individual parts and insert into data structures
4) Refresh the table
However, in the cellForRowAt indexPath function of my tableView I'm getting the fatal error on the first line of the function:
let eventsOnDay = eventsForDate[allDates[indexPath.section]]!
Here's my code for the refresh callback:
var allDates = [DateStruct]() // Holds all unique dates of events
var eventsForDate = [DateStruct : [PetEvent]]() // Holds all events for each day
/// Read events from db, split into individual dates
///
func readEventsFromDb() {
// 1. Empty out data structures
eventsForDate.removeAll()
allDates.removeAll()
let dbRef = FIRDatabase.database().reference().child("pets").child(currentPet).child("events")
// 2. Fetch data from db
dbRef.observeSingleEvent(of: .value, with: { snapshot in
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {
// 3. Split data into individual components
for child in snapshots{
if let data = child.value as? [String: Any] {
if let c = data["comment"] as? String, let p = data["user"] as? String, let t = data["type"] as? Int, let d = data["date"] as? UInt64 {
let event = PetEvent(comment: c, person: p, type: t, time: self.timeFromEpoch(time: Double(d)))
let eventDate = self.dateFromEpoch(time: Double(d))
if (self.eventsForDate[eventDate] != nil) {
self.eventsForDate[eventDate]!.append(event)
} else {
self.eventsForDate[eventDate] = [event]
}
}
}
}
self.allDates = Array(self.eventsForDate.keys).sorted {d1,d2 in
d2 < d1
}
// 4. Reload table
self.feedTable.reloadData()
self.refreshControl.endRefreshing()
}
})
}
I'm having a hard time figuring out why this is working most of the time but occasionally fails. Does anyone have an idea?
If your goal is to avoid the crash you can safely check for your eventOnDelay item by doing something like this:
if allDates.count > indexPath.section {
guard let eventsOnDay = eventsForDate[allDates[indexPath.section]] else {
//handle error here...
return
}
//Do whatever you need with eventsOnDay here
} else {
//Check your numberOfSections method, as it's not set up correctly
}
You also definitely need to make sure you're calling tableView.reloadData() and endRefreshing on the main thread as your Firebase callback likely comes back on a bg thread.
check wether the array(containing data to display) is not empty in your tableview's delegate and datasource method.
i.e. in cellForRow method
check if(yourArray.count > 0){
// Do your code
}
else{
// Dont
}
You can use different approach to update the table view content. I think there is delay getting data from firebase you are using callback dbRef.observeSingleEvent. you are removing the previous data at the beginning of the function call. you can try following two approaches to fix the issue
1. Remove empty data in dbRef.observeSingleEvent callback
var allDates = [DateStruct]() // Holds all unique dates of events
var eventsForDate = [DateStruct : [PetEvent]]() // Holds all events for each day
// Read events from db, split into individual dates
func readEventsFromDb() {
let dbRef = FIRDatabase.database().reference().child("pets").child(currentPet).child("events")
// 1. Fetch data from db
dbRef.observeSingleEvent(of: .value, with: { snapshot in
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {
// 2. Empty out data structures
eventsForDate.removeAll()
allDates.removeAll()
// 3. Split data into individual components
for child in snapshots{
if let data = child.value as? [String: Any] {
if let c = data["comment"] as? String, let p = data["user"] as? String, let t = data["type"] as? Int, let d = data["date"] as? UInt64 {
let event = PetEvent(comment: c, person: p, type: t, time: self.timeFromEpoch(time: Double(d)))
let eventDate = self.dateFromEpoch(time: Double(d))
if (self.eventsForDate[eventDate] != nil) {
self.eventsForDate[eventDate]!.append(event)
} else {
self.eventsForDate[eventDate] = [event]
}
}
}
}
self.allDates = Array(self.eventsForDate.keys).sorted {d1,d2 in
d2 < d1
}
// 4. Check if this is on UI/Main thread. Reload table
self.feedTable.reloadData()
self.refreshControl.endRefreshing()
}
})
}
2. Use temporary arrays to hold the data and then update the main data with temporary array
//Read events from db, split into individual dates
func readEventsFromDb() {
let dbRef = FIRDatabase.database().reference().child("pets").child(currentPet).child("events")
// 1. Fetch data from db
dbRef.observeSingleEvent(of: .value, with: { snapshot in
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {
var tempAllDates = [DateStruct]() // Holds all unique dates of events
var tempEventsForDate = [DateStruct : [PetEvent]]()
// 2. Split data into individual components
for child in snapshots{
if let data = child.value as? [String: Any] {
if let c = data["comment"] as? String, let p = data["user"] as? String, let t = data["type"] as? Int, let d = data["date"] as? UInt64 {
let event = PetEvent(comment: c, person: p, type: t, time: self.timeFromEpoch(time: Double(d)))
let eventDate = self.dateFromEpoch(time: Double(d))
if (self.tempEventsForDate[eventDate] != nil) {
self.tempEventsForDate[eventDate]!.append(event)
} else {
self. tempEventsForDate[eventDate] = [event]
}
}
}
}
self. tempAllDates = Array(self.eventsForDate.keys).sorted {d1,d2 in
d2 < d1
}
// 3. Empty out data structures
eventsForDate.removeAll()
allDates.removeAll()
// 4. Fill data with temporary array. check the size
if tempEventsForDate.count > 0{
eventsForDate = tempEventsForDate
}
if tempAllDates.count > 0{
allDates = tempAllDates
}
// 5. Check if this is on UI/Main thread. Reload table
self.feedTable.reloadData()
self.refreshControl.endRefreshing()
}
})
}
Hope this approaches will help to solve the issue :)
Related
I'm getting this error on the line let itemToAdd = snapshot.childSnapshot(forPath: "Shopa function that retrieves data from Firebase.
the output of the console in Could not cast value of type 'NSNull' (0x1118c8de0) to 'NSString' (0x10dda45d8)..
What I'm trying to do is to filter database ordering by one value
opening Timeand than get another value Shop Namefrom the returned entries in the snapshot.
here's the function:
func filterOpenShops(enterDoStuff: #escaping (Bool) -> ()) {
ref = Database.database().reference().child("Continent").child("Europe").child("Country").child("Italy").child("Region").child("Emilia-Romagna").child("City").child("Bologna").child("Shops").child("Shops Opening Times")
let query = ref?.queryOrdered(byChild: "Opening Time").queryStarting(atValue: openingTimeQueryStart).queryEnding(atValue: openingTimeQueryEnd)
query?.observe(.value, with: { (snapshot) in
for childSnapshot in snapshot.children {
// new modification
if childSnapshot is DataSnapshot {
let itemToAdd = snapshot.childSnapshot(forPath: "Shop Name").value as! String // gets the open shop from snapshot
self.availableShopsArray.append(itemToAdd)
print(snapshot.children)
print(" Open Shops are \(self.availableShopsArray)")
}
}
// still asynchronous part
enterDoStuff(true)
// call next cascade function filterClosedShops only when data
})
// Sychronous part
print("opening query start is \(openingTimeQueryStart) and opening query end is \(openingTimeQueryEnd)")
} // end of filterOpenShops()
EDIT:
I rewrote the function as:
func filterOpenShops(enterDoStuff: #escaping (Bool) -> ()) {
// get from Firebase snapshot all shops opening times into an array of tuples
//shopOpeningTimeArray:[(storeName: String, weekdayNumber: String, opening1: Sring, closing1: String, opening2:String, closing2: String)]
ref = Database.database().reference().child("Continent").child("Europe").child("Country").child("Italy").child("Region").child("Emilia-Romagna").child("City").child("Bologna").child("Shops").child("Shops Opening Times")
let query = ref?.queryOrdered(byChild: "Opening Time").queryStarting(atValue: String(describing: openingTimeQueryStart)).queryEnding(atValue: String(describing :openingTimeQueryEnd))
query?.observe(.value, with: { (snapshot) in // original is ok
// guard let data = snapshot.value as? [String:String] else { return }
for childSnapshot in snapshot.children {
print("snapshot is: \(childSnapshot)")
print("snapshot.childrend is: \(snapshot.children)")
guard let data = snapshot.value as? [String:String] else { return }
let itemToAdd = data["Shop Name"]
self.availableShopsArray.append(itemToAdd!)
print("Open Shop is: \(String(describing: itemToAdd))")
print(" Open Shops are \(self.availableShopsArray)")
}
// still asynchronous part
enterDoStuff(true)
// call next cascade function filterClosedShops only when data
print(" Open Shops are \(self.availableShopsArray)")
})
print("opening query start is \(openingTimeQueryStart) and opening query end is \(openingTimeQueryEnd)")
} // end of filterOpenShops()
but I still get a null object and not a [String:String] as expected.
The function that created the entries in Firebase is:
func postOpeningTime() {
// if shopNameTextfield.text != nil && openingTimeTextfield.text != nil && closingTimeTextfield.text != nil {
let shopName = shopNameTextfield.text!
let openingTime = openingTimeTextfield.text!
let closingTime = closingTimeTextfield.text!
// } else {return}
let post: [String:String] = [
"Shop Name" : shopName ,
"Opening Time" : openingTime ,
"Closing Time" : closingTime
]
var ref: DatabaseReference!
ref = Database.database().reference()
ref?.child("Continent").child("Europe").child("Country").child("Italy").child("Region").child("Emilia-Romagna").child("City").child("Bologna").child("Shops").child("Shops Opening Times").childByAutoId().setValue(post)
}
Now I have two behaviours:
1st: When querying for entries and finds values that are Int: completion get called but I get no snapshot print.
2nd: When querying for entries and find values that are String: completion doesn't get called but snapshot prints the right entries with values.
Can anyone please spot what's going on here?
I found the problem to bee the way I was casting query result.
Casting it as [String:String] produced to return because upshot was actually [String[String:String]] when all the values for entry's parameter were String, but as I changed Opening Time and Closing time to be Int, than I have to read the snapshot as [String[String:Any]].
So the final function is:
func filterOpenShops(setCompletion: #escaping (Bool) -> ()) {
// Empty the array for beginning of the search
self.availableShopsArray.removeAll()
var ref = Database.database().reference()
ref.child("Continent").child("Europe").child("Country").child("Italy").child("Region").child("Emilia-Romagna").child("City").child("Bologna").child("Shops").child("Shops Opening Times").queryOrdered(byChild: "Opening Time").queryStarting(atValue: openingTimeQueryStart).queryEnding(atValue: openingTimeQueryEnd).observe(.value) { (snapshot) in
print(snapshot)
if let data = snapshot.value as? [String : [String : Any]] {
for (_, value) in
data {
let shopName = value["Shop Name"] as! String
let active = value["Active"] as! String
if active == "true" {
self.availableShopsArray.append(shopName)
print("Shop_Name is :\(shopName)")
print("self.availableShopsArray is: \(self.availableShopsArray)")
}
}
} else {
print("No Shops")
}
// still asynchronous part
setCompletion(true)
// call next cascade function filterClosedShops only when data retrieving is finished
self.filterClosedShops(setCompletion: self.completionSetter)
print(" 1 Open Shops are \(self.availableShopsArray)")
}
} // end of filterOpenShops()
I have two functions that successfully retrieve integers from Firebase. I'd like a third function that does some simple subtraction from the integers gathered in the first two functions.
However, I'm very new to this, so can't get it to work correctly.
The output of the two functions that gather data from Firebase are:
let pointsRedeemedAsInt:Int = Int(Points_Redeem)!
and
let sumOfPointsCompleted = self.challengeList.reduce(0) {$0 + $1.Points}
What I'd like is a third function that looks like this:
let pointsBalance = sumOfPointsCompleted - pointsRedeemedAsInt
However, the third function doesn't recognise sumOfPointsCompleted, nor pointsRedeemedAsInt.
// First Function:
func loadPointsRedeemed() {
databaseReference = Database.database().reference()
let userID = Auth.auth().currentUser?.uid
databaseReference.child("Users").child(userID!).observe(DataEventType.value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
// let Points_Earn = value?["Points_Earned"] as? String ?? ""
let Points_Redeem = value?["Points_Redeemed"] as? String ?? ""
// self.Points_Earned.text = Points_Earn
self.Points_Redeemed.text = Points_Redeem
let pointsRedeemedAsInt:Int = Int(Points_Redeem)!
// Do any additional setup after loading the view.
}
)}
//Second Function:
func LoadPointsCompleted() {
self.challengeList.removeAll()
databaseReference = Database.database().reference()
let userID = Auth.auth().currentUser?.uid
let refChallenges = Database.database().reference(withPath: "Challenges").child(userID!).queryOrdered(byChild: "Status").queryEqual(toValue: "Complete")
refChallenges.observeSingleEvent(of: .value, with: { (snapshot) in
//if the reference have some values
if snapshot.childrenCount > 0 {
//clearing the list
self.challengeList.removeAll()
//iterating through all the values
for Challenges in snapshot.children.allObjects as! [DataSnapshot] {
//getting values
let challengeObject = Challenges.value as? [String: AnyObject]
let Points = challengeObject?["Points"] as! Int
//creating challenge object with model and fetched values
let challenge = pointsModel(Points: (Points as Int?)!)
//appending it to list
self.challengeList.append(challenge)
let sumOfPointsCompleted = self.challengeList.reduce(0) {$0 + $1.Points}
let sumOfPointsCompletedString = String(sumOfPointsCompleted)
self.Calc_Earned.text = sumOfPointsCompletedString
}
}
}
)}
// Third Function (which does not work):
func BalanceOfPoints(){
let balance = sum - pointsRedeemedAsInt
}
The error is:
Use of unresolved identifiers sum and pointsRedeemedAsInt
Furthermore, how do I ensure that everything is executed in the right order? ie, the loadPointsCompleted function must run (and complete) first, followed by the loadPointsRedeemed function, and finally the BalanceOfPoints function.
Actually, the problem is that you are not considering that retrieving data from remote sources is asynchronous.
This means that you have to wait for data to be retrieved before calling the other functions.
To achieve this result, you should use swift closure (callback in other languages) with completion handler. Check this documentation.
Change your functions this way:
First Function
func loadPointsRedeemed(completion: #escaping (_:Int)->()){
databaseReference = Database.database().reference()
let userID = Auth.auth().currentUser?.uid
databaseReference.child("Users").child(userID!).observe(DataEventType.value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
// let Points_Earn = value?["Points_Earned"] as? String ?? ""
let Points_Redeem = value?["Points_Redeemed"] as? String ?? ""
// self.Points_Earned.text = Points_Earn
self.Points_Redeemed.text = Points_Redeem
let pointsRedeemedAsInt:Int = Int(Points_Redeem)!
// Do any additional setup after loading the view.
//Call your return back function called "completion"
completion(pointsRedeemedAsInt)
}
)}
Second Function
func loadPointsCompleted(completion: #escaping (_:Int)->()){
self.challengeList.removeAll()
databaseReference = Database.database().reference()
let userID = Auth.auth().currentUser?.uid
let refChallenges = Database.database().reference(withPath: "Challenges").child(userID!).queryOrdered(byChild: "Status").queryEqual(toValue: "Complete")
refChallenges.observeSingleEvent(of: .value, with: { (snapshot) in
//if the reference have some values
if snapshot.childrenCount > 0 {
//clearing the list
self.challengeList.removeAll()
//iterating through all the values
for Challenges in snapshot.children.allObjects as! [DataSnapshot] {
//getting values
let challengeObject = Challenges.value as? [String: AnyObject]
let Points = challengeObject?["Points"] as! Int
//creating challenge object with model and fetched values
let challenge = pointsModel(Points: (Points as Int?)!)
//appending it to list
self.challengeList.append(challenge)
}
let sumOfPointsCompleted = self.challengeList.reduce(0) {$0 + $1.Points}
let sumOfPointsCompletedString = String(sumOfPointsCompleted)
self.Calc_Earned.text = sumOfPointsCompletedString
completion(sumOfPointsCompleted)
}
}
)}
Third Function
func balanceOfPoints(completion: #escaping (_:Int)->()) {
loadPointsCompleted{(sum) in
//HERE YOU CAN USE THE RESULT OF loadPointsCompleted
//I CALLED IT sum
loadPointsRedeemed{ (pointsRedeemedAsInt) in
// HERE YOU CAN USE THE RESULT OF loadPointsRedeemed
//I CALLED IT pointsRedeemedAsInt
let balance = sum - pointsRedeemedAsInt
completion(balance)
}
}
}
To call the balance function wherever you want:
balanceOfPoints{ (balance) in
// Whatever you want with balance
}
If you change the view ( for example you set some label text ), be sure to use the functions in the main thread.
The problem is that you are trying to access variables outside the scope of BalanceOfPoints().
Try returning the values you want to use in the equation from the first two functions, loadPointsRedeemed() and LoadPointsCompleted(). This can be done like so:
First Function
func loadPointsRedeemed() -> Int {
databaseReference = Database.database().reference()
let userID = Auth.auth().currentUser?.uid
databaseReference.child("Users").child(userID!).observe(DataEventType.value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
// let Points_Earn = value?["Points_Earned"] as? String ?? ""
let Points_Redeem = value?["Points_Redeemed"] as? String ?? ""
// self.Points_Earned.text = Points_Earn
self.Points_Redeemed.text = Points_Redeem
let pointsRedeemedAsInt:Int = Int(Points_Redeem)!
// Do any additional setup after loading the view.
return pointsRedeemedAsInt
}
)}
Second Function
func loadPointsCompleted() -> Int {
self.challengeList.removeAll()
databaseReference = Database.database().reference()
let userID = Auth.auth().currentUser?.uid
let refChallenges = Database.database().reference(withPath: "Challenges").child(userID!).queryOrdered(byChild: "Status").queryEqual(toValue: "Complete")
refChallenges.observeSingleEvent(of: .value, with: { (snapshot) in
//if the reference have some values
if snapshot.childrenCount > 0 {
//clearing the list
self.challengeList.removeAll()
//iterating through all the values
for Challenges in snapshot.children.allObjects as! [DataSnapshot] {
//getting values
let challengeObject = Challenges.value as? [String: AnyObject]
let Points = challengeObject?["Points"] as! Int
//creating challenge object with model and fetched values
let challenge = pointsModel(Points: (Points as Int?)!)
//appending it to list
self.challengeList.append(challenge)
}
let sumOfPointsCompleted = self.challengeList.reduce(0) {$0 + $1.Points}
let sumOfPointsCompletedString = String(sumOfPointsCompleted)
self.Calc_Earned.text = sumOfPointsCompletedString
return sumOfPointsCompleted
}
}
)}
Third Function
func balanceOfPoints() -> Int {
let sum = loadPointsCompleted()
let pointsRedeemedAsInt = loadPointsRedeemed()
let balance = sum - pointsRedeemedAsInt
return balance
}
Now, wherever you call the functions loadPointsRedeemed() and loadPointsCompleted(), replace these calls with balanceOfPoints.
Notice the main changes I made to your code are adding return values to your functions so they can be used in other areas of your code. Check out the Swift Functions Documentation to learn more.
I have an app where when a user taps a cell from a TableView representing a group of images, he is taken to another tableView where all the images within that group should be shown. However I am unsure of how to do this.
I have currently have extracted at the beginning the info necessary to make a reference and get all the data, which is in an array of objects. However how can I access these values from another class?
DataModel:
struct UserImage {
var userID: String
var image: UIImage
var postNum: String
}
I am creating an array of this as shown bellow, in a P1TableVC:
let arrayOfUserImageData = [UserImage]()
The function which retrieves and store the data looks as follows:
func fetchAllUserFristImage() {
print("Description: calling of fetchAllUserFristImage()")
Database.database().reference().child("Posts").observe(.childAdded, with: {(snapshot) in
if snapshot.value as? [String: AnyObject] != nil {
let user = snapshot.key
print("Description: calling of snapshot.value is not nil ")
self.databaseRef = Database.database().reference()
let usersPostRef2 = self.databaseRef.child("Posts").child(user)
usersPostRef2.observe(.value, with: {(postXSnapshots) in
if let postDictionary2 = postXSnapshots.value as? [String:AnyObject] {
for (p) in postDictionary2 {
if let posts = p.value as? [String:AnyObject] {
print("Description: posts has value of: \(posts)")
//to get back to where i was delete the below for i
for (i) in posts {
if let imageUrlString = i.value as? [String:AnyObject], let postUrl = imageUrlString["image1"] as? String {
print("Description: inside the if let imageUrlString = i.value ")
self.feedArray.append(Post(fetchedImageURL: postUrl))
if let imageUrl = URL(string: postUrl), let imageDataL = try? Data(contentsOf: imageUrl), let image = UIImage(data: imageDataL) {
print("Description: inside the if let imageUrl = URL(string: postUrl)")
print("Description: img url's of posts: \(imageUrl)")
self.tableData.append(UserImage(userID: user, image: image, postNum: p.key))
self.tableView.reloadData()
} else {print("this user had no posts, was nil")}
}
}
}
}
}
})
//below shud stay same
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
})
}
In didSelectItem method fetch the selected data from tableData based on indexPath.row and pass this data to next controller (make a variable in next controller and before pushing/presenting assign the fetched data to that variable).
didSelectItem : Fetch the selected data - let data = tableData[indexPath.row]
Assign it to next controller's variable -
let vc = nextVC()
vc.fetchedData = data
push/present vc
Or you may probably use Singleton
For instance: just create class Model
class Model {
static let sharedInstance = Model()
var tableVC: P1TableVC!
}
Then inside of your VC in viewDidLoad
class P1TableVC: UIViewController {
override func viewDidLoad() {
Model.sharedInstance.tableVC = self
}
}
Then you can use your class everywhere you want
Model.sharedInstance.tableVC.arrayOfUserImageData
Or you can create variable for all your data
class Model {
static let sharedInstance = Model()
var data: [UserImage]()
}
And then use data not VC
Model.sharedInstance.data
I had firebase database structure like this:
Currently I'm fetching the top key which is categories, But I want it to make dynamic when network call when selecting picker based on its categories and display on tableview, Is there any way to do that in swift3? But somehow I figure out by adding it in array.
my question is how do I convert array to multidimensional array?
func getCategories() {
DataService.ds.REF_CATEGORIES.observe(FIRDataEventType.value, with: { snapshot in
if let snapshot = snapshot.children.allObjects as? [FIRDataSnapshot] {
for (_, item) in snapshot.enumerated().reversed() {
let key = item.key.capitalized
self.categories.append(key)
print("key --> \(key)")
if let catDict = item.value as? JSONDictionary {
let count = catDict.count
for i in catDict {
if let finalCat = i.value as? JSONDictionary {
let finalKey = i.key
let category = Pizza(categoriesKey: finalKey , categoriesData: finalCat)
self.multiCat.append([category])
print("multiCat --> \(self.multiCat)")
print("i count --> \(count)")
}
}
}
}
}
})
}
Console Log: multiCat --> [[Pizza,Pizza,Pizza,Pizza,Pizza,Pizza,Pizza,Pizza,Pizza,]]
What I want is: [[Pizza,Pizza,Pizza], [Pizza,Pizza,Pizza], [Pizza,Pizza,Pizza,]]
func generateDataForRecents() {
if URLArrayStringThisSeason.count == 0 {
self.activityIndicator2.isHidden = false
self.activityIndicator2.startAnimating()
}
let databaseRef = FIRDatabase.database().reference()
databaseRef.child("palettes").queryLimited(toFirst: 100).observe(.value, with: { (snapshot) in
if let snapDict = snapshot.value as? [String:AnyObject]{
for each in snapDict as [String:AnyObject]{
let URL = each.value["URL"] as! String
self.URLArrayStringRecents.append(URL)
//print(self.URLArrayString.count)
//print(snapshot)
//let pictureTitle = each.value["title"] as! String
print(self.URLArrayStringRecents.count)
}
}
self.whatsNewCollectionView?.reloadData() //Reloads data after the number and all the URLs are fetched
self.activityIndicator2.stopAnimating()
self.activityIndicator2.isHidden = true
})
}
The following code does a retrieval of data each time the function is called, or when a new data is added.
This is extremely useful when the app is first started up or closed and then restarted. However, when the app is running, whenever a new entry is added, the code seemed to run again and thus appending twice the amount of new data.
For example, when there are already 15 entries identified and then suddenly a new entry is added, the array of the URL would contain 15+16 thus amounting to a total of 31.
How do I make it such that the new data is added to the array instead of adding the entire snapshot in?
You do that by listening for .childAdded events, instead of listening for .value:
var query = databaseRef.child("palettes").queryLimited(toFirst: 100)
query.observe(.childAdded, with: { (snapshot) in
let URL = snapshot.childSnapshot(forPath/: "URL").value as! String
self.URLArrayStringRecents.append(URL)
}
Since you have a limit-query, adding a 101st item means that one item will be removed from the view. So you'll want to handle .childRemoved too:
query.observe(.childRemoved, with: { (snapshot) in
// TODO: remove the item from snapshot.key from the araay
})
I recommend that you spend some time in the relevant documentation on handling child events before continuing.
Please check below method. I have use this method not getting any duplicate entry.
func getallNotes()
{
let firebaseNotesString: String = Firebase_notes.URL
let firebaseNotes = FIRDatabase.database().referenceFromURL(firebaseNotesString).child(email)
firebaseNotes.observeEventType(.Value, withBlock: { snapshot in
if snapshot.childSnapshotForPath("Category").hasChildren()
{
let child = snapshot.children
self.arrNotes = NSMutableArray()
self.arrDictKeys = NSMutableArray()
for itemsz in child
{
let childz = itemsz as! FIRDataSnapshot
let AcqChildKey : String = childz.key
if AcqChildKey == AcqIdGlobal
{
if (childz.hasChildren() == true)
{
let dictChild = childz.value as! NSMutableDictionary
self.arrDictKeys = NSMutableArray(array: dictChild.allKeys)
for i in 0..<self.arrDictKeys.count
{
let _key = self.arrDictKeys.objectAtIndex(i).description()
print(_key)
let dictData : NSMutableDictionary = NSMutableDictionary(dictionary: (dictChild.valueForKey(_key)?.mutableCopy())! as! [NSObject : AnyObject])
dictData.setObject(_key, forKey: "notesId")
self.arrNotes.addObject(dictData)
}
}
}
}
self.tableviewNote.reloadData()
}
})
}
As for the query for removed child,
query.observe(.childRemoved, with: { (snapshot) in
print(snapshot)
let URL = snapshot.childSnapshot(forPath: "URL").value as! String
self.URLArrayStringThisSeason = self.URLArrayStringThisSeason.filter() {$0 != URL}
self.thisSeasonCollectionView.reloadData()
})
it will obtain the URL of the removed child and then update the array accordingly.