Skip to content

Commit

Permalink
Merge pull request #435 from groue/feature/ValueObservation
Browse files Browse the repository at this point in the history
Improved support for values observation
  • Loading branch information
groue authored Nov 2, 2018
2 parents 6a4d9fb + 07bf588 commit 0cbf259
Show file tree
Hide file tree
Showing 30 changed files with 3,668 additions and 299 deletions.
123 changes: 122 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,127 @@
Release Notes
=============

## Next Version

This release comes with **[ValueObservation](README.md#valueobservation)**, a new type which tracks the results of database requests, and notifies fresh values whenever the database changes:

```swift
let observer = ValueObversation
.trackingOne(Player.filter(key: 42))
.start(in: dbQueue) { player: Player? in
print("Player has changed")
}
```

ValueObservation also aims at providing support for third-party code that needs to react to database changes. For example, [RxSwiftCommunity/RxGRDB#46](https://github.com/RxSwiftCommunity/RxGRDB/pull/46) is the pull request which implements RxGRDB, the companion library based on [RxSwift](https://github.com/ReactiveX/RxSwift), on top of ValueObservation.


### New

- [#435](https://github.com/groue/GRDB.swift/pull/435): Improved support for values observation
- It is now possible to observe database change from a [DatabaseReader](https://groue.github.io/GRDB.swift/docs/3.5/Protocols/DatabaseReader.html).


### Breaking Change

It used to be possible to define custom types that adopt the [DatabaseReader and DatabaseWriter protocols](README.md#databasewriter-and-databasereader-protocols), with a major drawback: it was impossible to add concurrency-related APIs to GRDB without breaking user code. Now all types that adopt those protocols are defined by GRDB: DatabaseQueue, DatabasePool, DatabaseSnapshot, AnyDatabaseReader, and AnyDatabaseWriter. **Expanding this set is no longer supported.**


### Documentation Diff

- [ValueObservation](README.md#valueobservation): this new chapter describes the new way to observe database values.


### API diff

```diff
protocol DatabaseReader: class {
+ func add<Reducer: ValueReducer>(
+ observation: ValueObservation<Reducer>,
+ onError: ((Error) -> Void)?,
+ onChange: @escaping (Reducer.Value) -> Void)
+ throws -> TransactionObserver
+ func remove(transactionObserver: TransactionObserver)
}

protocol DatabaseWriter: DatabaseReader {
- func remove(transactionObserver: TransactionObserver)
}

+protocol DatabaseRegionConvertible {
+ func databaseRegion(_ db: Database) throws -> DatabaseRegion
+}
+
+extension DatabaseRegion: DatabaseRegionConvertible { }
+
+struct AnyDatabaseRegionConvertible: DatabaseRegionConvertible {
+ init(_ region: @escaping (Database) throws -> DatabaseRegion)
+ init(_ region: DatabaseRegionConvertible)
+}

-protocol FetchRequest { ... }
+protocol FetchRequest: DatabaseRegionConvertible { ... }

+enum ValueScheduling {
+ case mainQueue
+ case onQueue(DispatchQueue, startImmediately: Bool)
+ case unsafe(startImmediately: Bool)
+}

+protocol ValueReducer {
+ associatedtype Fetched
+ associatedtype Value
+ func fetch(_ db: Database) throws -> Fetched
+ mutating func value(_ fetched: Fetched) -> Value?
+}
+
+extension ValueReducer {
+ func map<T>(_ transform: @escaping (Value) -> T?) -> MapValueReducer<Self, T>
+}
+
+struct AnyValueReducer<Fetched, Value>: ValueReducer {
+ init(fetch: @escaping (Database) throws -> Fetched, value: @escaping (Fetched) -> Value?)
+ init<Reducer: ValueReducer>(_ reducer: Reducer) where Reducer.Fetched == Fetched, Reducer.Value == Value
+}

+struct ValueObservation<Reducer> {
+ var extent: Database.TransactionObservationExtent
+ var requiresWriteAccess: Bool
+ var scheduling: ValueScheduling
+ static func tracking(_ regions: DatabaseRegionConvertible..., reducer: Reducer) -> ValueObservation
+}
+
+extension ValueObservation where Reducer: ValueReducer {
+ func map<T>(_ transform: @escaping (Reducer.Value) -> T)
+ -> ValueObservation<MapValueReducer<Reducer, T>>
+ func start(
+ in reader: DatabaseReader,
+ onError: ((Error) -> Void)? = nil,
+ onChange: @escaping (Reducer.Value) -> Void) throws -> TransactionObserver
+}
+
+extension ValueObservation where Reducer == Void {
+ static func tracking<Value>(
+ _ regions: DatabaseRegionConvertible...,
+ fetch: @escaping (Database) throws -> Value)
+ -> ValueObservation<ValueReducers.Raw<Value>>
+
+ static func tracking<Value>(
+ _ regions: DatabaseRegionConvertible...,
+ fetchDistinct: @escaping (Database) throws -> Value)
+ -> ValueObservation<ValueReducers.Distinct<Value>>
+ where Value: Equatable
+
+ static func trackingCount<Request: FetchRequest>(_ request: Request)
+ -> ValueObservation<ValueReducers.Raw<Int>>
+ static func trackingAll<Request: FetchRequest>(_ request: Request)
+ -> ValueObservation<ValueReducers.Raw<[Request.RowDecoder]>>
+ static func trackingOne<Request: FetchRequest>(_ request: Request)
+ -> ValueObservation<ValueReducers.Raw<Request.RowDecoder?>>
+}
```


## 3.4.0

Released October 8, 2018 &bull; [diff](https://github.com/groue/GRDB.swift/compare/v3.3.1...v3.4.0)
Expand Down Expand Up @@ -74,7 +195,7 @@ This release comes with **Association Aggregates**. They let you compute values
+}

+extension SQLSpecificExpressible {
+ public func aliased(_ key: CodingKey) -> SQLSelectable
+ func aliased(_ key: CodingKey) -> SQLSelectable
+}
```

Expand Down
32 changes: 12 additions & 20 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ The ideas, in alphabetical order:
- [CloudKit]
- [Concurrency]
- [Custom FTS5 Auxiliary Functions]
- [Database Observation]
- [Date and Time Functions]
- [Decode NSDecimalNumber from Text Columns]
- [Documentation]
Expand All @@ -86,6 +85,7 @@ The ideas, in alphabetical order:
- [JSON]
- [Linux]
- [More SQL Generation]
- [Reactive Database Observation]
- [Records: Splitting Database Encoding from Ability to Write in the Database]
- [SQL Console in the Debugger]
- [SQLCipher in a Shared App Container]
Expand Down Expand Up @@ -160,24 +160,6 @@ Applications can define their own [custom FTS5 auxiliary functions](https://www.
See issue [#421](https://github.com/groue/GRDB.swift/issues/421) for more information.


### Database Observation

:muscle: Hard

[Database Observation](README#database-changes-observation) is currently available in three flavors:

- The [TransactionObserver](README.md#transactionobserver-protocol) protocol: versatile, but low-level and challenging in terms of concurrency, especially when used in conjunction with database pools.

- [FetchedRecordsController]: high-level and easier to use.

- [RxGRDB]: high-level and easier to use, depends on [RxSwift](https://github.com/ReactiveX/RxSwift).

Suggested contributions are:

- Enhancements to FetchedRecordsController (see below).
- More choices of reactive engines.


### Date and Time Functions

:baby: Starter Task
Expand Down Expand Up @@ -257,7 +239,7 @@ let stringRequest = Player.select(Column("name"), as: String.self)
let rowRequest = SQLRequest<Row>("SELECT ...")
```

This limitation does not apply to [RxGRDB], the reactive sibling of FetchedRecordsController. It would be nice if FetchedRecordsController would become just as versatile.
This limitation does not apply to [ValueObservation] and [RxGRDB]. It would be nice if FetchedRecordsController would become just as versatile.


### FetchedRecordsController Support for Sections
Expand Down Expand Up @@ -312,6 +294,15 @@ There are several SQLite features that GRDB could natively support:
- [More ideas](https://www.sqlite.org/lang.html)


### Reactive Database Observation

:baby: Starter Task

We already have the [RxGRDB] companion library, which offers [RxSwift](https://github.com/ReactiveX/RxSwift) bindings.

We need more choices of reactive engines.


### Records: Splitting Database Encoding from Ability to Write in the Database

:baby: Starter Task :pencil: Documentation
Expand Down Expand Up @@ -416,3 +407,4 @@ Features that blur this focus are non-goals:
[PersistableRecord]: README.md#persistablerecord-protocol
[Record Comparison]: README.md#record-comparison
[Requesting Associated Records]: Documentation/AssociationsBasics.md#requesting-associated-records
[ValueObservation]: README.md#valueobservation
Loading

0 comments on commit 0cbf259

Please sign in to comment.