When I start my app, I perform an API call to see whether there\'s new data available. The data is stored in my local Realm database, and some of it is displayed in the init
after spent a day, i figure out with the remove DispatchQueue.main.async
in my realm.delete()
function and finally it worked.
DispatchQueue.main.async {
realm.delete()
}
to
realm.delete()
Try to create the element on realm instead of add
So:
try! realm.write {
realm.add(...)
}
Replace with:
try! realm.write {
realm.create(...)
}
And then after the delete operation realm should work as expected!
In my case I was deleting data from 2 tables at once.. one with a foreign to the other.
let itemToDelete = counters[indexPath.row]
let realm = try! Realm()
try! realm.write {
realm.delete(itemToDelete)
let predicate = NSPredicate(format: "counterid = \(c.id)")
let children = realm.objects(Details.self).filter(predicate)
realm.delete(children)
}
But the problem was that I was trying to delete the children of the item that does not exist anymore. Switching the order, solved it!
let itemToDelete = counters[indexPath.row]
let realm = try! Realm()
try! realm.write {
let predicate = NSPredicate(format: "counterid = \(c.id)")
let children = realm.objects(Details.self).filter(predicate)
realm.delete(children)
realm.delete(itemToDelete) //this should be deleted after
}
Hope this helps someone else!
What you can do is observe the Results<>
object returned from your initial Realm query (the one used to populate your list/table view) and update that list in the observer callback method whenever there is a change in the database.
Just make sure that you use the same Realm
instance to delete/modify the object as the one used for the initial query.
EDIT:
Some code examples:
let realm = Realm()
let results = realm.objects(Favourite.self)
let notificationToken = results.observe { [weak self] (changes) in
guard let tableView = self?.tableView else { return }
tableView.reloadData()
}
// Somewhere else
try? realm.write {
if let favourite = results.first {
realm.delete(favourite)
}
}
// OR
if let objectRealm = results.first?.realm {
try? objectRealm.write {
objectRealm.delete(results.first!)
}
}
// Don't forget to stop observing in deinit{}
// notificationToken.invalidate()
The issue was in my FavoritesHelper
class. It had both a favoriteLeagueIDs
and favoriteLeagues
property. I always set both of them and used the IDs for internal usage and the other property for whenever I want some data from these leagues.
This meant, that all favorite leagues were constantly referenced by the favoriteLeagues
property (of the type [League]
), thus crashing the app when I wanted to fetch them after they're invalidated.
What I've done to fix this, was to change the property favoriteLeagues
to a computed property as follows:
var favoriteLeagues: [League] {
get {
var leagues = [League]()
for id in favoriteLeagueIDs {
if let league = realm.objectForPrimaryKey(League.self, key: id) {
leagues.append(league)
}
}
return leagues
}
}
Now the leagues are no longer referenced and just get loaded from the database when I need to read them. Invalidated or deleted objects don't get loaded because of the if let
statement (the Realm.objectForPrimaryKey(:key:)
method returns nil
in such a case).
you can calling isInvalidated
or invalidated
to judge the object is invalid (YES) or not (NO).
you can also write a a custom method to define what is real invalidate
look for the document ,we will see a property:
/**
Indicates if the object can no longer be accessed because it is now invalid.
An object can no longer be accessed if the object has been deleted from the Realm that manages it, or
if `invalidate` is called on that Realm.
*/
@property (nonatomic, readonly, getter = isInvalidated) BOOL invalidated;
the invalidated
is readonly but what does isInvalidated
mean?
it is equals to - (BOOL)invalidated { return invalidated; }
it means that you can write a a custom method to define what is real invalidate
you want.