Updating table view cells swift - swift

BACKGROUND
I have a simple bill splitting app that allows the users to assign a meal item to multiple people or users. When the meal is assigned to multiple people, the price is divided accordingly. The meal (which contains an item name and a price are the rows and the users are the sections.
When I delete a row, I want to delete the row, and update or alter certain other values (I basically want to reassign the price to one less person when an item is deleted from a user). My data model is a multidimensional array. Here it is:
struct UserModel {
var name: String
var itemModels = [ItemModel]()
init(name: String, itemModels: [ItemModel]? = nil) {
self.name = name
if let itemModels = itemModels {
self.itemModels = itemModels
}
}
}
struct ItemModel {
var itemName: String
var price: Double
init(itemName: String, price: Double) {
self.itemName = itemName
self.price = price
}
}
class Data {
static var userModels = [UserModel]()
static var itemModels = [ItemModel]()
}
For example, in trailingSwipeActionsConfigurationForRowAt
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let delete = UIContextualAction(style: .destructive, title: "Delete") { (contextualAction, view, actionPerformed: #escaping (Bool) -> ()) in
let user = Data.userModels[indexPath.section].name
let item = Data.userModels[indexPath.section].itemModels[indexPath.row].itemName
let price = Data.userModels[indexPath.section].itemModels[indexPath.row].price
ItemModelFunctions.removeFromUser(from: indexPath.section, remove: indexPath.row)
var duplicate = Data.userModels.filter({$0.itemModels.contains(where: {$0.itemName == item})}).filter({$0.name != user})
for i in 0..<duplicate.count {
???
}
tableView.reloadData()
actionPerformed(true)
}
return UISwipeActionsConfiguration(actions: [delete])
}
The variable var duplicate returns an array of the other users who have the same item at the indexPath.row, but not the user(indexPath.section) who has the item. I know it sounds really confusing, but I can provide more code or print statements if needed.
Also in the for loop, I want to do something like this:
for i in 0..<duplicate.count {
let oldPrice = duplicate[i].price
let newPrice = oldPrice * duplicate.count
duplicate[i].price = newPrice
}
But I can't access the price. I believe need an indexPath.section and indexPath.row.
If you made it this far, thank you for taking the time. I feel like I need a nested loop, but I'm not sure how exactly to implement that. If there are any other easier ways to achieve this I'm open to any suggestions.
Thank you so much!
EDIT:
The marked answer worked! In case anyone else was having a similar issue this is what my final code looks like in the trailingSwipeActionsConfigurationForRowAt:
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let delete = UIContextualAction(style: .destructive, title: "Delete") { (contextualAction, view, actionPerformed: #escaping (Bool) -> ()) in
let item = Data.userModels[indexPath.section].itemModels[indexPath.row]
ItemModelFunctions.removeFromUser(from: indexPath.section, remove: indexPath.row)
let duplicate = Data.userModels.filter({$0.itemModels.contains(item)})
for i in 0..<Data.userModels.count {
if let idx = Data.userModels[i].itemModels.firstIndex(of: item) {
let oldPrice = Data.userModels[i].itemModels[idx].price
let newPrice = oldPrice * Double(duplicate.count+1)
Data.userModels[i].itemModels[idx].price = newPrice / Double(duplicate.count)
}
}
tableView.reloadData()
actionPerformed(true)
}
return UISwipeActionsConfiguration(actions: [delete])
}

Perhaps you can try this. Best of luck, please comment if it doesn't work. I am new to Swift :)
let actualItem = Data.userModels[indexPath.section].itemModels[indexPath.row]
for i in 0..<duplicate.count {
if let idx = duplicate[i].itemModels.firstIndex(of: actualItem) {
let oldPrice = duplicate[i].itemModels[idx].price
duplicate[i].itemModels[idx].price = oldPrice * duplicate.count
}
}

Related

First TableView section title doesn't scroll Swift 4

I'm building the SalesViewControllerfor my app and it consists of a TableView showing all items found in a date range.
Itemis child of Order and it has category, date, itemId, itemName, priceattributes all String.
I finally succeed in displaying the result of itemFetchResultController properly divided in sections as I had wrong sortDescriptor. In configuring itemFetchResultController I want use category property from fetched Item entities to be the section displayed title in populated TableView. My goal is, dough I'm not sure it would be possible or how to achieve it, to only have 1 row per itemName in its section but know ho many of it have been found in fetch and use it to display sold value. It's the first time I use sections and it's all a bit confusing to me. I'm trying to follow Apple documentation sample code gives me a couple of errors in tableView's data source methods as you can see by commented out code. All other posts I found here on stack overflow are very old and in objective c so I don't find answers to my doubts.
So far TableViewgets populated correctly , but first section title doesn't move when scrolling.
Any Idea of what's causing this ?
As always many thanks.
Here is the code I'm using for itemFetchResultController :
lazy var itemFetchedResultController: NSFetchedResultsController<Item> = {
// first sortDescriptor filters the date range: possibly change date from String to dates in both function and CoreData and use "(date >= %#) AND (date <= %#)" instead of "BEGINSWITH" in predicate
let itemFetchRequest = NSFetchRequest<Item>(entityName: "Item")
itemFetchRequest.sortDescriptors = [NSSortDescriptor(key: "category", ascending: true)]
itemFetchRequest.predicate = NSPredicate(format: "order.user.name == %#", UserDetails.fullName!)
itemFetchRequest.predicate = NSPredicate(format: "date BEGINSWITH %#", dateToFetch)
let itemFetchedResultController = NSFetchedResultsController(fetchRequest: itemFetchRequest, managedObjectContext: context, sectionNameKeyPath: "category", cacheName: nil)
return itemFetchedResultController
}()
TableView data source:
func numberOfSections(in tableView: UITableView) -> Int {
// apple doc : trhrows an error : Initializer for conditional binding must have Optional type, not 'NSFetchedResultsController<Item>'
// if let frc = itemFetchedResultController {
// return frc.sections!.count
// }
// return 0
return itemFetchedResultController.sections!.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard let sections = self.itemFetchedResultController.sections else {
print(" Error :no sections in fetchedResultController" )
return 0
}
let sectionInfo = sections[section]
return sectionInfo.numberOfObjects
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "statisticsCell", for: indexPath) as! StatisticsTableViewCell
cell.idInfoLabel.text = itemFetchedResultController.object(at: indexPath).itemId!
cell.nameInfoLabel.text = itemFetchedResultController.object(at: indexPath).itemName!
let item = itemFetchedResultController.object(at: indexPath).itemName!
let productRequest: NSFetchRequest<Product> = Product.fetchRequest()
productRequest.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
productRequest.predicate = NSPredicate(format: "name == %#", item)
productRequest.fetchLimit = 1
do {
let fetch = try context.fetch(productRequest)
cell.productImageView.image = UIImage(data: (fetch[0].productImage! as Data))
cell.minimumStockInfoLabel.text = fetch[0].minimumStock
cell.soldQuantityInfoLabel.text = fetch[0].soldQuantity
} catch {
print("Error in fetching product for cell: \(error)")
}
return cell
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
// guard let sections = self.itemFetchedResultController.sections else {
// print(" Error : no sections in itemsFetchedResultController " )
// return "empty"
// }
// let sectionInfo = sections[section]
// return sectionInfo.name
guard let sectionInfo = itemFetchedResultController.sections?[section] else {
return nil
}
return sectionInfo.name
}
func sectionIndexTitles(for tableView: UITableView) -> [String]? {
// if let sectionIndexTitles: FetchedResultController.sectionIndexTitles = self.itemFetchedResultController.sectionIndexTitles {
// print(" Error : no sections in itemsFetchedResultController " )
// return [""]
// }
return itemFetchedResultController.sectionIndexTitles
}
func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
// apple doc : trhrows an error : Initializer for conditional binding must have Optional type, not 'Int'
// guard let result = itemFetchedResultController.section(forSectionIndexTitle: title, at: index) else {
// fatalError("Unable to locate section for \(title) at index: \(index)")
// }
// return result
let result = itemFetchedResultController.section(forSectionIndexTitle: title, at: index)
return result
}
Set your UITableView style to grouped and your all section will scroll along with cells

Separating objects by column in a Table View Controller

I have a user object that takes in a few fields such as name, email, and an occupation field that stores a string array.
When a user is creating an account, they're prompted to enter up to 3 occupational choices, which is then stored in an array and uploaded to firebase under their user id:
I have a table view controller with the purpose of displaying all of the users, but separated by their occupation. The idea is to allow users to search other people based off of their particular occupation so for reference, all the designers on the app can be found on the tableview controller under the section titled "Designer". In another scenario, if the user is a Designer AND a developer, they should show up under Developer as well.
The key part here is the fact that users enter their occupation manually. So I can just create an array with a specific set of occupational titles. I have to get a list of all occupations, and then filter the users based off of the occupational title. Using the help I received from a smaller version of this idea (Smaller Version), I came up with a function to separate users by their occupation but it's not working at all:
func getOccupations() -> [IndexedOccupations] {
while !users.isEmpty {
//-- filter the users
guard let referencedUser = users.first else { return [] }
for x in (referencedUser.occupation?.count)! {
let filteredUsers = users.filter { (user) -> Bool in
for y in user.occupation.count {
return user.occupation[y] == referencedUser.occupation[x]
}
return false
}
//-- create an occupation object with an occupation name, and list of filtered users & return occupations
let indexedOccupation = IndexedOccupations(occupation: referencedUser.occupation[x]!, users: filteredUsers)
occupations.append(indexedOccupation)
}}
return occupations
}
Heres how both objects look for reference btw:
class User: NSObject {
//-- things that a User will have stored
var id: String?
var bio: String?
var city: String?
var name: String?
var email: String?
var picture: String?
var state: String?
var links: [String]?
var playerID: String?
var occupation: [String]?
//-- initializer method
init(dictionary: [String: AnyObject]) {
self.id = dictionary["id"] as? String
self.name = dictionary["Name"] as? String
self.email = dictionary["Email"] as? String
self.city = dictionary["City"] as? String
self.state = dictionary["State"] as? String
self.picture = dictionary["Picture"] as? String
self.bio = dictionary["Bio"] as? String
self.playerID = dictionary["playerID"] as? String
self.links = dictionary["Links"] as? [String]
self.occupation = dictionary["Occupation"] as? [String]
}
}
And Occupations:
class IndexedOccupations: NSObject {
//-- things that a user will have stored
var occupation: String?
var users: [User]
//-- initializer method
init(occupation: String, users: [User]) {
self.occupation = occupation
self.users = users
}
}
I'd make a dictonary like:
var userByOccupation: [String: [User]] = []
And then make a function where you insert all Users in this Dictonary with the Key being their Occupation (if they have more then 1 just add new Key with same Value)
So I don't know how your Data Model looks like until Occupations but what you need to do is just iterate over every child of Occupations and for every child take the value and save this value as key and the current User as Value into the userByOccupation Array.
So first get the current User and then:
ref?.child("Occupation").observeSingleEvent(of: .value, with: { (snapshot) in
let enumerator = snapshot.children
while let rest = enumerator.nextObject() as? DataSnapshot{
if let occupation = rest.key as? String{
//And here insert OCCUPATION/USER like
if (userByOccupation[occupation] != nil) {
userByOccupation[occupation]?.append(user)
} else {
userByOccupation[occupation] = [user]
}
}
}
})
EDIT to comment-question:
I don't know if it is the best solution but here's how I'd do it.
After loading the dictionary just make an array of all keys like this
allOccupations = Array(userByOccupation.keys)
After this you can order this array for example -> dictionaries are unordered!
And then:
override func numberOfSections(in tableView: UITableView) -> Int {
return allOccupations.count
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return allOccupations[section]
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return userByOccupation[allOccupations[section]].count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Then get the user like this
guard let allUsers = userByOccupation[allOccupations[section]] else {
return }
let currentUser = allUsers[indexPath.row]
}
Made this pretty fast so I'm sorry if there are errors in it, I hope it helps you!

UITableView with Segmented Controller not displaying data immediately - Swift

I have a Tab Bar Controller, and one of the two views from it is a TableViewController. The TableViewController has a SegmentedControl bar on the top of the table to switch between two datasets pulled from Firebase.
When I run the app, the table doesn't show any data straight away, only when I switch to the other segment of the SegmentControl bar. But, when I switch back, the data for the first segments loads.
I put in a breakpoint to see what was happening, and it was skipping over the code I wrote to pull the data from Firebase, so the arrays were empty upon initial loading, hence the lack of data. Yet, when I switch segments to the other option, the data appears.
I'm also trying to sort the data within the arrays, and this doesn't do anything at all because it runs when the arrays come back empty, so there is nothing to sort.
My code is:
class LeaderboardViewController: UITableViewController
{
#IBOutlet weak var segmentedControl: UISegmentedControl!
#IBOutlet var leaderboardTable: UITableView!
var countyRef = Database.database().reference().child("countyleaderboard")
var schoolRef = Database.database().reference().child("schoolleaderboard")
var refHandle: UInt!
var countyList = [County]()
var schoolList = [School]()
override func viewDidLoad()
{
super.viewDidLoad()
fetchData()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
var sectionCount = 0
switch(segmentedControl.selectedSegmentIndex)
{
case 0:
sectionCount = countyList.count
break
case 1:
sectionCount = schoolList.count
break
default:
break
}
return sectionCount
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let boardCell = tableView.dequeueReusableCell(withIdentifier: "boardCell", for: indexPath)
switch(segmentedControl.selectedSegmentIndex)
{
case 0:
boardCell.textLabel!.text = "" + countyList[indexPath.row].name! + ": \(countyList[indexPath.row].score!)"
break
case 1:
boardCell.textLabel!.text = "" + schoolList[indexPath.row].name! + ": \(schoolList[indexPath.row].score!)"
break
default:
break
}
return boardCell
}
func fetchData()
{
schoolRef.observe(.childAdded, with:
{ snapshot in
if let dictionary = snapshot.value as? [String: AnyObject]
{
let school = School()
school.name = dictionary["name"] as! String
school.score = dictionary["score"] as! Float
self.schoolList.append(school)
}
}, withCancel: nil)
self.schoolList.sort(by: { $0.score > $1.score })
countyRef.observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children
{
let snap = child as! DataSnapshot
let county = County()
county.name = snap.key as String
county.score = snap.value as! Float
self.countyList.append(county)
}
})
self.countyList.sort(by: { $0.score > $1.score })
DispatchQueue.main.async
{
self.leaderboardTable.reloadData()
}
}
#IBAction func segmentedControlChanged(_ sender: Any)
{
DispatchQueue.main.async
{
self.leaderboardTable.reloadData()
}
}
}
My questions are:
Why does the data not load straight away?
Where are the arrays coming from if they are not being populated with data on the first run? And why are they not being sorted if that code is directly below the code that populates them?
The dispatch block should be after the for loop and before the }).
It should be within the observe/observeSingleEvent. Also your sort code needs to be inside the same block. Like below:
countyRef.observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children
{
let snap = child as! DataSnapshot
let county = County()
county.name = snap.key as String
county.score = snap.value as! Float
self.countyList.append(county)
}
self.countyList.sort(by: { $0.score > $1.score })
DispatchQueue.main.async
{
self.leaderboardTable.reloadData()
}
})

UISearchBar textDidChange data from plist

I'd like to search through items of my plist. The plist consists of an array of dictionaries. Each key/value represents Strings/Ints, etc but that isn't important.
As you'll see in the tableViewController class below, I've currently got an array that I have typed. I know I need to make an array of objects/items from my plist but I can't work out how to reference objects from the plist in the view controller.
View controller.swift file:
import UIKit
class TableViewController: UITableViewController, UISearchResultsUpdating {
var array = ["Example 1", "Example 2", "Example 3"]
var filteredArray = [String]()
var searchController = UISearchController()
var resultsController = UITableViewController()
override func viewDidLoad() {
super.viewDidLoad()
searchController = UISearchController(searchResultsController: resultsController)
tableView.tableHeaderView = searchController.searchBar
searchController.searchResultsUpdater = self
resultsController.tableView.delegate = self
resultsController.tableView.dataSource = self
}
//Added func to update search results
func updateSearchResults(for searchController: UISearchController) {
filteredArray = array.filter({ (array:String) -> Bool in
if array.contains(searchController.searchBar.text!) {
return true
} else {
return false
}
})
resultsController.tableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
extension TableViewController {
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView == resultsController.tableView {
return filteredArray.count
} else {
return array.count
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
if tableView == resultsController.tableView {
cell.textLabel?.text = filteredArray[indexPath.row]
} else {
cell.textLabel?.text = array[indexPath.row]
}
return cell
}
}
I've tried solving this by creating an object class from a tutorial on plists. It uses the example of a periodic table of elements:
import UIKit
struct Element {
enum State: String {
case Solid, Liquid, Gas
}
let atomicNumber: Int
let atomicWeight: Float
let discoveryYear: String
let group: Int
let name: String
let period: Int
let radioactive: Bool
let state: State
let symbol: String
// Position in the table
let horizPos: Int
let vertPos: Int
}
extension Element {
enum ErrorType: Error {
case noPlistFile
case cannotReadFile
}
/// Load all the elements from the plist file
static func loadFromPlist() throws -> [Element] {
// First we need to find the plist
guard let file = Bundle.main.path(forResource: "Element", ofType: "plist") else {
throw ErrorType.noPlistFile
}
// Then we read it as an array of dict
guard let array = NSArray(contentsOfFile: file) as? [[String: AnyObject]] else {
throw ErrorType.cannotReadFile
}
// Initialize the array
var elements: [Element] = []
// For each dictionary
for dict in array {
// We implement the element
let element = Element.from(dict: dict)
// And add it to the array
elements.append(element)
}
// Return all elements
return elements
}
/// Create an element corresponding to the given dict
static func from(dict: [String: AnyObject]) -> Element {
let atomicNumber = dict["atomicNumber"] as! Int
let atomicWeight = Float(dict["atomicWeight"] as! String) ?? 0
let discoveryYear = dict["discoveryYear"] as! String
let group = dict["group"] as! Int
let name = dict["name"] as! String
let period = dict["period"] as! Int
let radioactive = dict["radioactive"] as! String == "True"
let state = State(rawValue: dict["state"] as! String)!
let symbol = dict["symbol"] as! String
let horizPos = dict["horizPos"] as! Int
let vertPos = dict["vertPos"] as! Int
return Element(atomicNumber: atomicNumber,
atomicWeight: atomicWeight,
discoveryYear: discoveryYear,
group: group,
name: name,
period: period,
radioactive: radioactive,
state: state,
symbol: symbol,
horizPos: horizPos,
vertPos: vertPos)
}
}
And in the viewController class, instead of having
var array = ["Example 1", "Example 2", "Example 3"]
I've tried variations of
var array = Element["name"]
and
var array = elements.name
But they obviously don't work because the reference to the plist is in the object class.
If anyone has any idea on how to solve this using swift 3/xcode 8 I would be very appreciative!!
I hope your question is still relevant. As I understand, you can't filter your array, right? If so, I recommend you to take a look at this and this tutorials. Both of them are representing a little bit different approaches to load filtered array, but it doesn't matter much, they work.
P.S. I don't recommend you to make a special tableView for searching, if you want to customize it hereafter, because you will have to do it programmaticly later.
It will be more efficiant to do like this:
If searchController.isActive {
// do some stuff
} else { // another stuff }
But it is just my opinion. I hope my question will help ;).
Thanks for the links Oleg. They were really good!!
It wasn't so much the filtering I had a problem with but actually parsing objects from my plist into a tableview. It took me a few days but I found an answer in case other people were also having the same problem. Keep in mind I'm reasonably new to this and so it might not be the best/perfect way of doing it but it works.
In viewDidLoad, I made a reference to the plist using the following code:
let path = Bundle.main.path(forResource: "Elements", ofType: "plist")
let dictArray = NSArray(contentsOfFile: path!)
I'm not sure of the relevance but I'm pretty sure this next bit is needed if the plist ever needed to be updated. (Also in viewDidLoad)
for elementItem in dictArray! {
let newElement : Element = Element(state:((elementItem as AnyObject).object(forKey: "state")) as! String,
atomicNumber:((elementItem as AnyObject).object(forKey: "atomicNumber")) as! Int,
atomicWeight:((elementItem as AnyObject).object(forKey: "atomicWeight")) as! Float,
discoveryYear:((elementItem as AnyObject).object(forKey: "discoveryYear")) as! String
etc. for each of the keys in the dictionary in the same order as in the Element object in the question. Then (also in viewDidLoad):
elementsArray.append(newElement)
Then its pretty easy, in cellForRow I just have to make a new variable that refers back to the object and I can call up each/any of the associated dictionary keys. E.g.:
let element : Element
cell.textLabel!.text = element.name
cell.detailTextLabel?.text = element.symbol
return cell
Like I said, I know its not perfect but it worked for me. I've heard its not best practice to put too much information in viewDidLoad, so someone else might be able to confirm or provide a better answer.

Firebase duplicate tableView when new date is added, updated or deleted

Whenever I add new data to Firebase or update the data in the Firebase console, my tableView data duplicates. The udemy tutorial I've followed never showed how to solve the issue. A Google search brought up my problem but non seemed to explain the root of the problem; I after the root of the issue and not just an answer. I'm learning as I go along but this one bugs me.
One answer mentioned the use of self.members.removeAll() but Im using Swift 3, where to put that and why?
My issue relates to this but that answer seemed vague and not really an answer. I have a configureCell() but what should be added?
I upvote most of my question's answers when someone explains and just adding an answer. What is happening and why, please? Do I need to reload/remove something in:
if let cell = tableView.dequeueReusableCellWithIdentifier("PostCell") as? PostCell{...}
Edited:
var expenses = [Expense]()
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return expenses.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let expense = expenses[indexPath.row]
if let cell = tableView.dequeueReusableCell(withIdentifier: "expenseFeedCell") as? ExpenseFeedCell {
cell.selectionStyle = .none
cell.configureCell(expense: expense)
return cell
} else {
return ExpenseFeedCell()
}
}
And the Expense Class:
class Expense {
// Private variables
private var _date: Double!
private var _type: String!
private var _amount: Double!
private var _notes: String!
private var _expenseId: String!
// Setting up the getteres
var type: String {
return _type
}
var date: Double {
return _date
}
var amount: Double {
return _amount
}
var notes: String {
return _notes
}
var expenseId: String {
return _expenseId
}
init(type: String, date: Double, amount: Double, notes: String) {
self._type = type
self._date = date
self._amount = amount
self._notes = notes
}
init(expenseId: String, expenseData: [String: AnyObject]) {
self._expenseId = expenseId
if let type = expenseData["type"] as? String {
self._type = type
}
if let date = expenseData["date"] as? Double {
self._date = date
}
if let amount = expenseData["amount"] as? Double {
self._amount = amount
}
if let notes = expenseData["notes"] as? String {
self._notes = notes
}
}
}
If the firebase method you use to read data is called each time there is a new entry, you should remove the content of your data array before adding data on it.
So in the firebase method, start by having an empty array. After that you can treat with the snapchot and fill this same array.
Add a self.tableView.reloadData() to reload the view with new data in this array