inessential by Brent Simmons

Swift Diary #10: Changes to Properties

Both of the apps I’m working on have a custom persistence engine (built on top of FMDB and SQLite). Core Data isn’t a great fit for these apps. (This isn’t me being contrary. I’m doing very database-y stuff, and Core Data is an object persistence system, not a database. Seriously: you should use Core Data.)

The model objects are pure Swift objects. And I’d like to borrow one of Core Data’s awesome features: when a property is set on an object, the persistence system should get notified. It will record the change, then coalesce and queue updates to the database.

* * *

Let’s say I’m writing a Twitter client. (This an example used for historical reasons — because, if you look at history, you’ll find that people used to write Twitter clients — and should not be taken as indicative.)

Let’s pretend it has a Tweet class, a pure Swift class, with about 20 properties (some from the server, some local) — and setting any one of those should trigger the persistence system to do its thing.

I can do what I want if I add a didSet code block to each property. One of the cool things is that didSet is ignored during init, which means I can initialize an object with data without didSet getting called and triggering an unnecessary database update.

Each didSet block looks like a variation on this:

recordPropertyChange(self, key: "somePropertyName", newValue: somePropertyName)

It’s a pain to do this for every single property in every single class that’s database-backed.

* * *

Were this Objective-C, the situation would be fairly easily dealt with via dynamic method resolution. Each property would be marked as @dynamic, and methods that do the right thing (update in-memory storage and notify the persistence system) would be provided at runtime.

That’s not available to pure Swift classes, though.

I could ask for that feature — but it had me thinking that there might be a better feature request to make. Classes could optionally (perhaps when conforming to a PropertyObserver protocol) implement a class method that gets notified when any property changes in the same circumstances where it would call didSet. (It should still call didSet, when it’s there.)

Something like:

class func didSetProperty<T, U>(object: T, key: String, newValue: U) {
  recordPropertyChange​(object, key: key, newValue: newValue)

(I’m totally unsure that the above is the right way to express this. Maybe object should be Self? I’m still learning my way around generics and protocols.)

If there already is a way to solve this, and I don’t need to make this feature request, then I’m keen to hear the solution. It would be useful.