4.1 KiB
GRDB/DatabaseRegionObservation
DatabaseRegionObservation tracks changes in a database region, and notifies impactful transactions.
Overview
DatabaseRegionObservation tracks insertions, updates, and deletions that impact the tracked region, whether performed with raw SQL, or doc:QueryInterface. This includes indirect changes triggered by foreign keys actions or SQL triggers.
See doc:GRDB/DatabaseRegionObservation#Dealing-with-Undetected-Changes below for the list of exceptions.
DatabaseRegionObservation calls your application right after changes have been committed in the database, and before any other thread had any opportunity to perform further changes. This is a pretty strong guarantee, that most applications do not really need. Instead, most applications prefer to be notified with fresh values: make sure you check ValueObservation before using DatabaseRegionObservation.
DatabaseRegionObservation Usage
Create a DatabaseRegionObservation with one or several requests to track:
// Tracks the full player table
let observation = DatabaseRegionObservation(tracking: Player.all())
Then start the observation from a DatabaseQueue or DatabasePool:
let cancellable = try observation.start(in: dbQueue) { error in
// Handle error
} onChange: { (db: Database) in
print("Players were changed")
}
Enjoy the changes notifications:
try dbQueue.write { db in
try Player(name: "Arthur").insert(db)
}
// Prints "Players were changed"
You stop the observation by calling the DatabaseCancellable/cancel() method on the object returned by the start method. Cancellation is automatic when the cancellable is deallocated:
cancellable.cancel()
DatabaseRegionObservation can also be turned into a Combine publisher, or an RxSwift observable (see the companion library RxGRDB):
let cancellable = observation.publisher(in: dbQueue).sink { completion in
// Handle completion
} receiveValue: { (db: Database) in
print("Players were changed")
}
You can feed DatabaseRegionObservation with any type that conforms to the DatabaseRegionConvertible protocol: FetchRequest, DatabaseRegion, Table, etc. For example:
// Observe the score column of the 'player' table
let observation = DatabaseRegionObservation(
tracking: Player.select(Column("score")))
// Observe the 'score' column of the 'player' table
let observation = DatabaseRegionObservation(
tracking: SQLRequest("SELECT score FROM player"))
// Observe both the 'player' and 'team' tables
let observation = DatabaseRegionObservation(
tracking: Table("player"), Table("team"))
// Observe the full database
let observation = DatabaseRegionObservation(
tracking: .fullDatabase)
Dealing with Undetected Changes
DatabaseRegionObservation will not notify impactful transactions whenever the database is modified in an undetectable way:
- Changes performed by external database connections.
- Changes performed by SQLite statements that are not compiled and executed by GRDB.
- Changes to the database schema, changes to internal system tables such as
sqlite_master. - Changes to
WITHOUT ROWIDtables.
To have observations notify such undetected changes, applications can take explicit action: call the Database/notifyChanges(in:) Database method from a write transaction:
try dbQueue.write { db in
// Notify observations that some changes were performed in the database
try db.notifyChanges(in: .fullDatabase)
// Notify observations that some changes were performed in the player table
try db.notifyChanges(in: Player.all())
// Equivalent alternative
try db.notifyChanges(in: Table("player"))
}
Topics
Creating DatabaseRegionObservation
init(tracking:)-5ldbeinit(tracking:)-2nqjd
Observing Database Transactions
publisher(in:)start(in:onError:onChange:)