0
votes

I have a few realm managed objects in my app that need to keep a track of the timestamp it was last modified.

I could implement the repository programming pattern for this, or I could just remember to set the property every time I change something on those objects (which is a fallback idea, as horrible as it sounds).

What I've done is create a singleton object that monitors collections of objects that I want to keep a "last modified" timestamp for. This is booted up on app start and keep's an eye on the collections for any modifications.

The general "flow" is:

  • Wait for change notification
  • On change, loop the modified indexes
  • Check the index exists before trying to access the object (just in case)
  • realm.write a new last modified Date to the object

As you can probably guess already (and as I suspected), that realm.write to update my last modified timestamp, then creates a new change notification, which in turn makes the timestamp update again. So it ends up just looping forever after the first change.

Looking for options on how best to solve this.

Here's what I have implemented:

class RealmCollectionLastModifiedMonitor {
    static let shared = RealmCollectionLastModifiedMonitor()

    private var formObjectNotificationToken: NotificationToken?

    private init() {
        startMonitoring()
    }

    deinit {
        stopMonitoring()
    }

    func startMonitoring() {
        let realm = try! Realm()
        let caseforms = realm.objects(CaseForm.self)

        formObjectNotificationToken = caseforms.addNotificationBlock { [weak self] (changes: RealmCollectionChange) in
            switch changes {

            case .initial:
                // Do nothing for now - as i've not worked out what this is :P                
                break

            case .update(_, let deletions, let insertions, let modifications):

                // Some debug output
                print("Form collection change: Deletions = \(deletions.count), Insertions = \(insertions.count), Modifications = \(modifications.count)")

                // Loop the modificated indexes, get a reference to the form, then update the last modified timestamp
                for i in modifications {
                    if caseforms.indices.contains(i) {
                        do {
                            try realm.write {
                                caseforms[i].lastModified = Date()
                            }
                        } catch {
                            print("Failed to update modified date")
                        }
                    }
                }

                break

            case .error(let error):
                // Do nothing for now - If a write fails, we could rollback, but it's not uber-important
                break
            }
        }
    }

    func stopMonitoring() {
        formObjectNotificationToken = nil
    }
}
1

1 Answers

2
votes

Unfortunately my suggestion is a refactoring of your current approach. You're intent is not to monitor general changes to the collection, but rather changes to specific properties on specific objects.

With this is mine I feel that you will be far better served by monitoring each individual object, and this will allow you introspection into the properties that have changed on said object.

For example,

func monitor(_ caseForm: CaseForm) -> NotificationToken {
    return caseForm.addNotificationBlock { change in
        switch change {
            case .change(let properties):
                guard properties.count > 0 !properties.first(where: { $0.name == "lastModified" }) else { return }
                    // modify last modified date
            case .deleted:
                break
            case .error(let error):
                // handle error
            }
        }
   }
}