I have two table views in one view controller that both hold separate types of data not related to each other. for some reason whenever I add new data to either project the app crashes because it adds the new data to both table views instead of only one. I'm trying to add the data to the correct table view. here is my code.
var tasks: Todo!
var progress: [Millestone] = []
var milestone: Millestone!
var list: [Todo] = []
var taskfetch: NSFetchedResultsController<Todo>!
var progressfetch : NSFetchedResultsController<Millestone>!
let fetching: NSFetchRequest<Todo> = Todo.fetchRequest()
let sorting = NSSortDescriptor(key: "dateadded", ascending: true)
fetching.predicate = NSPredicate(format: "projectname = %@", "\(title! as String)")
fetching.sortDescriptors = [sorting]
if let appDelegate = (UIApplication.shared.delegate as? AppDelegate) {
let context = appDelegate.persistentContainer.viewContext
taskfetch = NSFetchedResultsController(fetchRequest: fetching, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
taskfetch.delegate = self
do {
try taskfetch.performFetch()
if let fetchedObjects = taskfetch.fetchedObjects {
list = fetchedObjects
}
} catch {
print(error)
}
}
let lining: NSFetchRequest<Millestone> = Millestone.fetchRequest()
let sorting2 = NSSortDescriptor(key: "dateadded", ascending: true)
lining.predicate = NSPredicate(format: "projectname = %@", "\(title! as String)")
lining.sortDescriptors = [sorting2]
if let appDelegate = (UIApplication.shared.delegate as? AppDelegate) {
let context = appDelegate.persistentContainer.viewContext
progressfetch = NSFetchedResultsController(fetchRequest: lining, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
progressfetch.delegate = self
do {
try progressfetch.performFetch()
if let fetchedObjects = progressfetch.fetchedObjects {
progress = fetchedObjects
}
} catch {
print(error)
}
}
func getdata() {
let context = (UIApplication.shared.delegate as! AppDelegate!).persistentContainer.viewContext
do {
print("getting")
let tasking = try context.fetch(Todo.fetchRequest())
let progressname = try context.fetch(Millestone.fetchRequest())
} catch{
print("whoopsie")
}
}
let oktaskaction = UIAlertAction(title: "Add", style: .default, handler: {(action:UIAlertAction!) -> Void in
if text.textFields?[0].text != nil, text.textFields?[0].text != "" {
// self.taskTable.beginUpdates()
// let song = self.songs[indexPath.row]
//(UIApplication.shared.delegate as! AppDelegate).saveContext()
if let appDelegate = (UIApplication.shared.delegate as? AppDelegate){
self.tasks = Todo(context: appDelegate.persistentContainer.viewContext)
self.tasks.taskname = text.textFields?[0].text
self.tasks.projectname = self.title
self.tasks.completed = false
let formatter = DateFormatter()
formatter.dateStyle = DateFormatter.Style.medium
formatter.timeStyle = DateFormatter.Style.none
self.tasks.dateadded = self.date
appDelegate.saveContext()
}else {
print("nothing there")
text.textFields?[0].placeholder = "did not enter text"
}
self.taskTable.refreshControl?.beginRefreshing()
self.getdata()
self.taskTable.reloadData()
}
})
let okAction = UIAlertAction(title: "Add Milestone", style: .default, handler: {(action:UIAlertAction!) -> Void in
if text2.textFields?[0].text != nil, text2.textFields?[0].text != "", text2.textFields?[1].text != nil {
print("i'm working on adding the milestone")
if let appDelegate = (UIApplication.shared.delegate as? AppDelegate){
self.milestone = Millestone(context: appDelegate.persistentContainer.viewContext)
self.milestone.progressname = text2.textFields?[0].text
self.milestone.date = text2.textFields?[1].text
self.milestone.projectname = self.title
appDelegate.saveContext()
print("adding to graph")
self.chartLegend.append(self.milestone.progressname!)
self.chartData.append(self.chartData.count + 1)
print("saved the new milestone")
}else {
print("nothing there")
text.textFields?[0].placeholder = "did not enter text"
}
self.milestoneTableView.reloadData()
self.projectlinechart.reloadData()
}
})
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
print("Begining")
print("\(list.count)")
print("\(progress.count)")
taskTable.beginUpdates()
milestoneTableView.beginUpdates()
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch type {
case .insert:
if let newIndexPath = newIndexPath {
print("adding")
taskTable.insertRows(at: [newIndexPath], with: .fade)
milestoneTableView.insertRows(at: [newIndexPath], with: .fade)
}
case .delete:
if let indexPath = indexPath {
print("delete")
taskTable.deleteRows(at: [indexPath], with: .fade)
milestoneTableView.deleteRows(at: [indexPath], with: .fade)
}
case .update:
if let indexPath = indexPath {
print("updating")
taskTable.reloadRows(at: [indexPath], with: .fade)
milestoneTableView.reloadRows(at: [indexPath], with: .fade)
}
default:
print("doing something else")
taskTable.reloadData()
milestoneTableView.reloadData()
}
if let fetchedObjects = controller.fetchedObjects {
projects = fetchedObjects as! [Project]
list = fetchedObjects as! [Todo]
progress = fetchedObjects as! [Millestone]
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
print("ending")
print("\(list.count)")
print("\(progress.count)")
taskTable.endUpdates()
milestoneTableView.endUpdates()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView.tag == 1 {
return list.count
} else if tableView.tag == 2 {
return progress.count
} else {
return 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellidentifier = "taskcell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellidentifier, for: indexPath) as! TaskTableViewCell
if tableView.tag == 1 {
let tasks = list[indexPath.row]//this is where the crash occurs
cell.taskname.text = tasks.taskname
cell.taskname.adjustsFontSizeToFitWidth = true
if tasks.completed == true {
cell.accessoryType = .checkmark
}
}
} else if tableView.tag == 2 {
if let appDelegate = (UIApplication.shared.delegate as? AppDelegate){
let progress2 = progress[indexPath.row]
if (progress2.progressname != nil), progress2.date != nil{
cell.progressname.text = "\(progress2.progressname!) on \(progress2.date!)"
cell.progressname.adjustsFontSizeToFitWidth = true
self.chartData.append(self.chartData.count + 1)
chartLegend.insert(cell.progressname.text!, at: indexPath.row)
} else {
cell.progressname.text = "No Milestones"
}
}
}
return cell
}
Get rid of the following variables progress
and list
. The fetchedResultsController are tracking changes to the objects, so when an object is deleted, or inserted or moved it updates for you. By having the fetched results copied into the arrays you are looking at out of date information after there is a change. Instead look at the values of the fetchedResultsController directly (ie access self.taskfetch.fetchedObjects
or use self.taskfetch.object(at:indexPath)
.
The reason this caused a crash is because you are updating your tableview based on changes that the fetchedResultsController(s) are informing you about, but not updating the amount of rows in your table because you looking at the old stale data.
Another problem is that you are updating BOTH tables when either set of data changes. So if something is inserted in one set of data you insert a row incorrectly in the wrong table. In all controller methods first check which controller it is. Something like if controller == taskfetch {