Tracking deletion of rows


Can you give me some advice on the following problem?

I've a document based application where the document class manages the database.
The document can have multiple windows, each with their own selection of objects (rows).
When objects are deleted from the database they of course need to be removed from the (per window) selection memory as well.

What's the best way to monitor for these deletions?

ValueObservation doesn't seem quite right as there is no value associated with a deleted row. I can't think of a query that will tell me which objects are deleted.
The transaction observer gives the fine grained control that's wanted, but it would involve a lot of database logic in the window controller, which doesn't feel right from the isolation perspective. All database logic should be in the document class, shouldn't it?
The DatabaseRegionObservation seems to fall a bit in between these two, with no real fit to this problem.

Can you give some advice here?

Thanks in advance.

Kind regards,

Remco Poelstra

Hello @remcopoelstra,

Just perform a diff between subsequent outputs of a regular ValueObservation of all rows.

Or have each window controller track its row with a regular ValueObservation: when it becomes nil, the row was deleted.

The game here is to build your application tools on top of the library. Not to look in the library for a ready-made tool.


The sentence above is not what I wanted to say, @remcopoelstra. What I wanted to say was:

It's useful to distinguish three concepts:

  • the acquisition of database values (from GRDB apis).
  • the processing of database values (by your app).
  • the actions that modify database values (through GRDB apis).

Your initial post shows you lost in a sea of various ways to acquire database values because none of them acquire actions (the deletions you are looking for).

The solutions I suggest have you use an acquisition tool you are already familiar with (the good old ValueObservation), and turn your problem into the processing of database values (spot values that turn missing).

As you can see, GRDB provides no way to observe actions directly (except with the low-level TransactionObserver protocol). Instead, it focuses on value acquisition and observation, because it's the strongest trend in application development in the recent years. Combine, SwiftUI, UITableViewDiffableDataSource, functional programming, focus on immutability, are all programming idioms that let applications process values, instead of handling actions.

A "deletion" is not observed directly. Instead, you process value changes in order to find deletions.

One can wonder if this is really easier :sweat_smile:.

Indeed, another advice could have been to send a deletion notification through NotificationCenter after objects were deleted. The problem with this solution, though, is twofold:

  • You can not ask GRDB to post the deletion notification for you. If it would, then another user would ask for a notification for insertion, another one for the change of some column in some table, and another one... etc. "Action" is too general: there's no way to fulfill all needs.
  • So you send the notification from application code. But now, if you forget, now or in the future, to post the notification in some place, or post an incorrect one, some windows won't get updated. That's a big burden on application maintenance.

That's why my best advice is to use the value observation techniques. Those never forget to notify a change. And you can infer actions from those changes. Deletions, but also all other kinds of diffs that your apps are interested in.

Thanks. This was just the advice I was looking for.

I indeed hadn't realised that I can use ValueObservation to observe something gone missing :slight_smile:

I tried this and it works great. I now simply register a value observation that just tracks the row IDs that are selected and I cancel and setup a new observation when the selection changes in the didSet property observer. This seems to work just fine.

The 'notification from my code' had crossed my mind, but I was reluctant to go that way just because of the reasons you mention. And it turned out to be the right thing to do.


Kind regards,

Remco Poelstra

1 Like
Terms of Service

Privacy Policy

Cookie Policy