0

I have a live (working) version of an iOS app (swift 3) which uses Realm Swift (2.4.2) for storage and offline use. I'm using simple tables as structures with primary key and (yet) no relationships at all. Everything was working perfect, until the moment I realized that calling below snippet::

entities = fetch from web...... // pseudo

let realm = try Realm()

try realm.write {
   realm.add(entities, update: true)
}
  1. Successfully adds entities(rows) that didn't exist in db before (based on primary key).
  2. Updates existing rows(based on primary key again) that exist in db.
  3. WILL NOT delete rows that use to exist in db but do not anymore (based on primary key).

Declaration of primary key::

override static func primaryKey() -> String? {
    return "ID"
}

where "ID" is the Class' primary key attribute as per realm doc.

Anyway as a workaround I was nuking (drop) whole table before updating::

entities = fetch from web...... // pseudo

let realm = try Realm()
let storedObjects = realm.objects(Entity.self)

try realm.write {
    realm.delete(storedObjects) // nuke entities
    realm.add(entities)
}

but I am pretty sure there must be must be a better way.

I' ve found a similar thread in StackOverflow in the past, but proposed solutions are workarounds pretty much like my own. This is not what I' m looking for...

Jimi
  • 198
  • 2
  • 13
  • Am I correct in understanding that you're looking for a way to make the state of the Realm database match the information given in `entities` (remove anything that doesn't exist, update things that do, add anything that's new)? – bdash Mar 02 '17 at 21:32
  • Totally! Thats exactly what I need. – Jimi Mar 03 '17 at 06:59
  • Indeed, it doesn't look like Realm has an easy way of supporting what you'd like to do. If you want, you can file a feature request ticket at our GitHub with details about how you'd like such a feature to work, and we'll use it to gauge interest and see if we can build it out. https://github.com/realm/realm-cocoa/issues – AustinZ Mar 03 '17 at 23:03
  • Seems like a feature many users of Realm can benefit from. I'll file the request the soonest possible. Thank you! – Jimi Mar 07 '17 at 08:11

1 Answers1

0

You could add a new property to your realm objekt like

dynamic var lastUpdate: Double

or something like this.

Then you can do

let currentMs = NSDate().timeIntervalSince1970
for entity in entities {
    entity.lastUpdate = currentMs
}

realm.add(entities, update: true)

let oldObjects= realm.objects(Entity.self).filter("lastUpdate < \(currentMs)")
realm.delete(oldObjects)

With this solution you don't have to "nuke" all entities. But I don't think it's much better regarding performance

Dominik Vincenz
  • 445
  • 3
  • 10
  • RealmSwift (2.4.2) doesn't provide such definition for your realm.create method. – Jimi Mar 03 '17 at 08:47
  • @Jimi yes, sorry for that. Please notice my last edit. I removed this approach – Dominik Vincenz Mar 03 '17 at 08:49
  • Problem with above code is that sometimes let oldObjects= realm.objects(Entity.self).filter("lastUpdate < \(currentMs)") returns all rows of table. Since realm.add is a synchronous call one would expect timestamps are definitely updated thus returning all rows is impossible except the case where indeed request returned empty result, which is not. – Jimi Mar 03 '17 at 12:07
  • To me this http://stackoverflow.com/a/40976134/2143387 seems a bit cleaner. But Realm doesn't seem to have native way of deleting objects that were not included in the response. – Pahnev Mar 03 '17 at 14:27
  • Indeed looks cleaner and should be more efficient as well. Thank you for pointing me there. Problem is that an exception is raised on predicate .filter("id NOT IN %@", ids). ids array seems ok, so the syntax maybe... – Jimi Mar 03 '17 at 15:08
  • Correct Predicate syntax is:: let objectsToDelete = realm.objects(Items.self).filter("NOT (id IN %@)", ids) Swift 3 – Jimi Mar 03 '17 at 15:21