I'm building a project management app in Swift, using Realm to store my data.
I'm using DayViewController
to display projects in a TableView. In order to be able to delete projects from the database (and from the UI) via a swipe action, I'm using the tableView:commitEditingStyle:forRowAtIndexPath:
function.
However, every time I'm trying to swipe-delete a project from the TableView, the app crashes with a Realm Exception:
Terminating app due to uncaught exception 'RLMException', reason: 'Object has been deleted or invalidated.'
*** First throw call stack:
(
0 CoreFoundation 0x00007fff23c7127e __exceptionPreprocess + 350
1 libobjc.A.dylib 0x00007fff513fbb20 objc_exception_throw + 48
2 Realm 0x0000000105e57b44 _ZL17RLMVerifyAttachedP13RLMObjectBase + 84
3 Realm 0x0000000105e5d8fc _ZN12_GLOBAL__N_18getBoxedIN5realm10StringDataEEEP11objc_objectP13RLMObjectBasem + 28
4 Realm 0x0000000105e5d8d7 ___ZN12_GLOBAL__N_115makeBoxedGetterIN5realm10StringDataEEEP11objc_objectm_block_invoke + 39
5 Social Media Management App 0x00000001055d9cdd $s27Social_Media_Management_App17DayViewControllerC05tableF0_12cellForRowAtSo07UITableF4CellCSo0mF0C_10Foundation9IndexPathVtF + 973
6 Social Media Management App 0x00000001055da055 $s27Social_Media_Management_App17DayViewControllerC05tableF0_12cellForRowAtSo07UITableF4CellCSo0mF0C_10Foundation9IndexPathVtFTo + 165
7 UIKitCore 0x00007fff48297462 -[UITableView _createPreparedCellForGlobalRow:withIndexPath:willDisplay:] + 781
8 UIKitCore 0x00007fff4826043b -[UITableView _updateVisibleCellsNow:] + 3081
9 UIKitCore 0x00007fff4828055f -[UITableView layoutSubviews] + 194
10 UIKitCore 0x00007fff485784bd -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 2478
11 QuartzCore 0x00007fff2b131db1 -[CALayer layoutSublayers] + 255
12 QuartzCore 0x00007fff2b137fa3 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 517
13 QuartzCore 0x00007fff2b1438da _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 80
14 QuartzCore 0x00007fff2b08a848 _ZN2CA7Context18commit_transactionEPNS_11TransactionEd + 324
15 QuartzCore 0x00007fff2b0bfb51 _ZN2CA11Transaction6commitEv + 643
16 QuartzCore 0x00007fff2b0c04ba _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 76
17 CoreFoundation 0x00007fff23bd3867 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
18 CoreFoundation 0x00007fff23bce2fe __CFRunLoopDoObservers + 430
19 CoreFoundation 0x00007fff23bce97a __CFRunLoopRun + 1514
20 CoreFoundation 0x00007fff23bce066 CFRunLoopRunSpecific + 438
21 GraphicsServices 0x00007fff384c0bb0 GSEventRunModal + 65
22 UIKitCore 0x00007fff48092d4d UIApplicationMain + 1621
23 Social Media Management App 0x00000001055d5feb main + 75
24 libdyld.dylib 0x00007fff5227ec25 start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
I've done some research on the topic and from my understanding, this error occurs when the code tries to access an object (or an object's property) after the object has been deleted from the database or invalidated.
However, I'm not quite sure which part of my code is trying to access the deleted object.
DayViewController.swift:
import UIKit
import RealmSwift
class DayViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
// MARK: Variables
@IBOutlet weak var dayViewTableView: UITableView!
// Realm initialization
let realm = try! Realm()
var currentDate: String = ""
var Projects: [Project] = []
var projectsToken: NotificationToken?
// MARK: View Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
setupTableView()
Projects = getProjectsForDay(day: currentDate)
// Observe Realm database for changes and reload tableview
projectsToken = realm.observe { (notification, realm) in
self.dayViewTableView.reloadData()
}
}
override func viewWillDisappear(_ animated: Bool) {
projectsToken?.invalidate()
}
// MARK: Datasource / Delegate Methods
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Projects.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let dayViewCell = dayViewTableView.dequeueReusableCell(withIdentifier: DayViewTableViewCell.reuseIdentifier()) as! DayViewTableViewCell
dayViewCell.setupCellLabels(projectName: Projects[indexPath.row].title, notes: Projects[indexPath.row].notes ?? "")
return dayViewCell
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == UITableViewCell.EditingStyle.delete {
let objectToDelete = Projects[indexPath.row]
do {
try realm.write {
realm.delete(objectToDelete)
}
}
catch {
print("Error trying to delete object from realm database. \(error)")
}
}
}
// MARK: User defined functions
func getProjectsForDay(day: String) -> [Project] {
let filteredProjects = realm.objects(Project.self).filter("dueDateString == %@", day)
return Array(filteredProjects)
}
func setupTableView() {
dayViewTableView.register(UINib(nibName: DayViewTableViewCell.nibName(), bundle: nil), forCellReuseIdentifier: DayViewTableViewCell.reuseIdentifier())
dayViewTableView.delegate = self
dayViewTableView.dataSource = self
dayViewTableView.rowHeight = UITableView.automaticDimension
dayViewTableView.estimatedRowHeight = 54.0
}
}
Because your array is not updated after you are deleting and item. // Observe Realm database for changes and reload tableview
projectsToken = realm.observe { (notification, realm) in
projects = getProjectsForDay(day: currentDate)
self.dayViewTableView.reloadData()
}
This should fix it. Your object will remain in array because realm will not delete it from there, and when tableView will try to reload it's data it will crash at the reference of the deleted item. What are you trying to achieve is done with var projects: Results. And you are doing a bit of overkill observing the whole realm. You can add observer on a specific query, but it's more hard to achieve that kind of live data when you are modifying the day.
And please don't name properties with capital letters.