Skip to content

Commit

Permalink
Improve migration notes for ValueObservation
Browse files Browse the repository at this point in the history
  • Loading branch information
groue committed Sep 29, 2024
1 parent 9bdde6b commit a6b1568
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 4 deletions.
43 changes: 41 additions & 2 deletions Documentation/GRDB7MigrationGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Migrating From GRDB 6 to GRDB 7
- [Column Coding Strategies](#column-coding-strategies)
- [Cancellable Async Database Accesses](#cancellable-async-database-accesses)
- [Default Transaction Kind](#default-transaction-kind)
- [ValueObservation and the Main Actor](#valueobservation-and-the-main-actor)
- [Access to SQLite C functions](#access-to-sqlite-c-functions)
- [The Record Base Class is Discouraged](#the-record-base-class-is-discouraged)
- [Other Changes](#other-changes)
Expand Down Expand Up @@ -109,6 +110,46 @@ In GRDB 7, `Configuration` no longer has a `defaultTransactionKind` property, be

You can still specify a transaction kind explicitly when necessary. See [Transaction Kinds] for details.

## ValueObservation and the Main Actor

In GRDB 7, `ValueObservation` fosters the main actor, because it is frequently used to automatically update database values on screen.

By default, its [`start`](https://swiftpackageindex.com/groue/grdb.swift/documentation/grdb/valueobservation/start(in:scheduling:onerror:onchange:)) method must be started on the main actor, and its notification callbacks run on the main actor as well.

It is not necessary to call `MainActor.assumeIsolated` in `ValueObservation` callbacks:

```swift
// GRDB 7
@MainActor func startObservation() {
let observation = ValueObservation.tracking { ... }

let cancellable = observation.start(in: dbQueue) { error in
// This closure is MainActor-isolated.
} onChange: { value in
// This closure is MainActor-isolated.
print("Fresh value", value)
}
}
```

You can opt-out of the main actor with a specific scheduler such as `.async(onQueue:)`:

```swift
func startObservation() {
let observation = ValueObservation.tracking { ... }

let cancellable = observation.start(
in: dbQueue,
scheduling: .async(onQueue: .main))
{ error in
// Called on the specified dispatch queue.
} onChange: { value in
// Called on the specified dispatch queue.
print("Fresh value", value)
}
}
```

## Access to SQLite C functions

In GRDB 6, the underlying C SQLite library is implicitly available:
Expand Down Expand Up @@ -180,8 +221,6 @@ Do not miss [Swift Concurrency and GRDB], for more recommendations regarding non

## Other Changes

- `ValueObservation` must be started from the Main Actor by default. Use an explicit `async(onQueue: .main)` scheduling in order to remove this constraint.

- `DatabasePool.concurrentRead` has been removed. Use [`asyncConcurrentRead`](https://swiftpackageindex.com/groue/grdb.swift/documentation/grdb/databasepool/asyncconcurrentread(_:)) instead.

- The `PersistenceContainer` subscript no longer guarantees that the value returned is the same as what was previously set. It only guarantees that both values are encoded identically in the database.
Expand Down
4 changes: 2 additions & 2 deletions GRDB/Documentation.docc/Extension/ValueObservation.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,9 @@ By default, `ValueObservation` notifies the initial value, as well as eventual c
```swift
// The default scheduling
let cancellable = observation.start(in: dbQueue) { error in
// Called asynchronously on the main actor
// This closure is MainActor-isolated.
} onChange: { value in
// Called asynchronously on the main actor
// This closure is MainActor-isolated.
print("Fresh value", value)
}
```
Expand Down

0 comments on commit a6b1568

Please sign in to comment.