Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Combine #549

Merged
merged 8 commits into from
Jun 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,17 @@ GRDB adheres to [Semantic Versioning](https://semver.org/), with one expection:
- [#539](https://github.com/groue/GRDB.swift/pull/539): Expose joining methods of both requests and associations
- [#542](https://github.com/groue/GRDB.swift/pull/542): Move eager loading of hasMany associations to FetchRequest
- [#546](https://github.com/groue/GRDB.swift/pull/546) by [@robcas3](https://github.com/robcas3): Fix SPM errors with Xcode 11 beta
- You can now [combine](README.md#valueobservationcombine) up to eight ValueObservations in a single shot.
- [#549](https://github.com/groue/GRDB.swift/pull/549) Support for Combine

### Documentation Diff

The [Define Record Requests](Documentation/GoodPracticesForDesigningRecordTypes.md#define-record-requests) chapter of the The [Good Practices for Designing Record Types](Documentation/GoodPracticesForDesigningRecordTypes.md) has been rewritten. Yes, good practices evolve.

The [Examples of Record Definitions](README.md#examples-of-record-definitions) has been extended with a sample record optimized for fetching performance.

The [ValueObservation](README.md#valueobservation) chapter has been updated with the new APIs.


### API Diff

```diff
Expand Down
4 changes: 2 additions & 2 deletions Documentation/AssociationsBasics.md
Original file line number Diff line number Diff line change
Expand Up @@ -827,8 +827,8 @@ Those requests can also turn out useful when you want to track their changes wit
```swift
// Track changes in the author's books:
let author: Author = ...
ValueObservation
.trackingAll(author.books)
author.books
.observationForAll()
.start(in: dbQueue) { (books: [Book]) in
print("Author's book have changed")
}
Expand Down
2 changes: 1 addition & 1 deletion Documentation/GRDB3MigrationGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ To guarantee asynchronous notifications, and never ever block your main thread,

```swift
// On main queue
var observation = ValueObservation.trackingAll(Player.all())
var observation = Player.observationForAll()
observation.scheduling = .async(onQueue: .main, startImmediately: true)
let observer = try observation.start(in: dbQueue) { (players: [Player]) in
// On main queue
Expand Down
16 changes: 8 additions & 8 deletions GRDB.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -610,9 +610,9 @@
56A8C2331D1914540096E9D4 /* UUID.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56A8C22F1D1914540096E9D4 /* UUID.swift */; };
56A8C2471D1918F00096E9D4 /* FoundationNSUUIDTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56A8C2361D1914790096E9D4 /* FoundationNSUUIDTests.swift */; };
56A8C2481D1918F00096E9D4 /* FoundationUUIDTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56A8C21E1D1914110096E9D4 /* FoundationUUIDTests.swift */; };
56AACAA822ACED7100A40F2A /* Passthrough.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56AACAA722ACED7100A40F2A /* Passthrough.swift */; };
56AACAA922ACED7100A40F2A /* Passthrough.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56AACAA722ACED7100A40F2A /* Passthrough.swift */; };
56AACAAA22ACED7100A40F2A /* Passthrough.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56AACAA722ACED7100A40F2A /* Passthrough.swift */; };
56AACAA822ACED7100A40F2A /* Fetch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56AACAA722ACED7100A40F2A /* Fetch.swift */; };
56AACAA922ACED7100A40F2A /* Fetch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56AACAA722ACED7100A40F2A /* Fetch.swift */; };
56AACAAA22ACED7100A40F2A /* Fetch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56AACAA722ACED7100A40F2A /* Fetch.swift */; };
56AE64122229A53700AD1B0B /* HasOneThroughAssociation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56AE64112229A53700AD1B0B /* HasOneThroughAssociation.swift */; };
56AE64132229A53700AD1B0B /* HasOneThroughAssociation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56AE64112229A53700AD1B0B /* HasOneThroughAssociation.swift */; };
56AE64142229A53700AD1B0B /* HasOneThroughAssociation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56AE64112229A53700AD1B0B /* HasOneThroughAssociation.swift */; };
Expand Down Expand Up @@ -1222,7 +1222,7 @@
56A8C21E1D1914110096E9D4 /* FoundationUUIDTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FoundationUUIDTests.swift; sourceTree = "<group>"; };
56A8C22F1D1914540096E9D4 /* UUID.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UUID.swift; sourceTree = "<group>"; };
56A8C2361D1914790096E9D4 /* FoundationNSUUIDTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FoundationNSUUIDTests.swift; sourceTree = "<group>"; };
56AACAA722ACED7100A40F2A /* Passthrough.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Passthrough.swift; sourceTree = "<group>"; };
56AACAA722ACED7100A40F2A /* Fetch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Fetch.swift; sourceTree = "<group>"; };
56AE64112229A53700AD1B0B /* HasOneThroughAssociation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HasOneThroughAssociation.swift; sourceTree = "<group>"; };
56AE6423222AAC9500AD1B0B /* AssociationHasOneThroughSQLTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssociationHasOneThroughSQLTests.swift; sourceTree = "<group>"; };
56AF746A1D41FB9C005E9FF3 /* DatabaseValueConvertibleEscapingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseValueConvertibleEscapingTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1999,8 +1999,8 @@
children = (
5613ED3E21A95A8B00DC7A68 /* Combine.swift */,
564CE4D521B2DEB500652B19 /* CompactMap.swift */,
56AACAA722ACED7100A40F2A /* Fetch.swift */,
5613ED3421A95A5C00DC7A68 /* Map.swift */,
56AACAA722ACED7100A40F2A /* Passthrough.swift */,
564CE59621B7A8B500652B19 /* RemoveDuplicates.swift */,
5613ED4321A95B2C00DC7A68 /* ValueReducer.swift */,
);
Expand Down Expand Up @@ -2720,7 +2720,7 @@
5695962B222C462D002CB7C9 /* HasManyThroughAssociation.swift in Sources */,
565490D31D5AE252005622CB /* (null) in Sources */,
566475D91D981D5E00FF74B8 /* SQLOperators.swift in Sources */,
56AACAAA22ACED7100A40F2A /* Passthrough.swift in Sources */,
56AACAAA22ACED7100A40F2A /* Fetch.swift in Sources */,
566B910F1FA4C3970012D5B0 /* Database+Statements.swift in Sources */,
56F5ABDD1D814330001F60CB /* UUID.swift in Sources */,
565490CB1D5AE252005622CB /* (null) in Sources */,
Expand Down Expand Up @@ -2780,7 +2780,7 @@
5613ED4921A95C1200DC7A68 /* ValueObservation+Row.swift in Sources */,
566B91361FA4D3810012D5B0 /* TransactionObserver.swift in Sources */,
564CE43221AA901800652B19 /* ValueObserver.swift in Sources */,
56AACAA922ACED7100A40F2A /* Passthrough.swift in Sources */,
56AACAA922ACED7100A40F2A /* Fetch.swift in Sources */,
5605F1721C672E4000235C62 /* DatabaseValueConvertible+RawRepresentable.swift in Sources */,
56CEB5141EAA324B00BFAF62 /* FTS3+QueryInterface.swift in Sources */,
566475CF1D981D5E00FF74B8 /* SQLFunctions.swift in Sources */,
Expand Down Expand Up @@ -3310,7 +3310,7 @@
56CEB4F11EAA2EFA00BFAF62 /* FetchableRecord.swift in Sources */,
56D91AA22205E03700770D8D /* SQLRelation.swift in Sources */,
56300B781C53F592005A543B /* QueryInterfaceRequest.swift in Sources */,
56AACAA822ACED7100A40F2A /* Passthrough.swift in Sources */,
56AACAA822ACED7100A40F2A /* Fetch.swift in Sources */,
5605F18D1C6B1A8700235C62 /* SQLCollatedExpression.swift in Sources */,
5613ED5421A95DD000DC7A68 /* ValueObservation+Count.swift in Sources */,
566B91331FA4D3810012D5B0 /* TransactionObserver.swift in Sources */,
Expand Down
12 changes: 12 additions & 0 deletions GRDB/Core/DatabasePool.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ public final class DatabasePool: DatabaseWriter {
return writer.path
}

public var configuration: Configuration {
return writer.configuration
}

// MARK: - Initializer

/// Opens the SQLite database at path *path*.
Expand Down Expand Up @@ -547,6 +551,14 @@ extension DatabasePool : DatabaseReader {
return try writer.reentrantSync(block)
}

/// Asynchronously executes an update block in a protected dispatch queue.
///
/// Eventual concurrent reads may see changes performed in the block before
/// the block completes.
public func unsafeAsyncWrite(_ block: @escaping (Database) -> Void) {
writer.async(block)
}

// MARK: - Functions

/// Add or redefine an SQL function.
Expand Down
4 changes: 4 additions & 0 deletions GRDB/Core/DatabaseQueue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,10 @@ extension DatabaseQueue {
return try writer.reentrantSync(block)
}

public func unsafeAsyncWrite(_ block: @escaping (Database) -> Void) {
writer.async(block)
}

// MARK: - Functions

/// Add or redefine an SQL function.
Expand Down
10 changes: 5 additions & 5 deletions GRDB/Core/DatabaseReader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -166,9 +166,9 @@ public protocol DatabaseReader : class {
/// - returns: a TransactionObserver
func add<Reducer: ValueReducer>(
observation: ValueObservation<Reducer>,
onError: ((Error) -> Void)?,
onError: @escaping (Error) -> Void,
onChange: @escaping (Reducer.Value) -> Void)
throws -> TransactionObserver
-> TransactionObserver

/// Remove a transaction observer.
func remove(transactionObserver: TransactionObserver)
Expand Down Expand Up @@ -257,11 +257,11 @@ public final class AnyDatabaseReader : DatabaseReader {
/// :nodoc:
public func add<Reducer: ValueReducer>(
observation: ValueObservation<Reducer>,
onError: ((Error) -> Void)?,
onError: @escaping (Error) -> Void,
onChange: @escaping (Reducer.Value) -> Void)
throws -> TransactionObserver
-> TransactionObserver
{
return try base.add(observation: observation, onError: onError, onChange: onChange)
return base.add(observation: observation, onError: onError, onChange: onChange)
}

/// :nodoc:
Expand Down
47 changes: 26 additions & 21 deletions GRDB/Core/DatabaseSnapshot.swift
Original file line number Diff line number Diff line change
Expand Up @@ -100,36 +100,41 @@ extension DatabaseSnapshot {

public func add<Reducer: ValueReducer>(
observation: ValueObservation<Reducer>,
onError: ((Error) -> Void)?,
onError: @escaping (Error) -> Void,
onChange: @escaping (Reducer.Value) -> Void)
throws -> TransactionObserver
-> TransactionObserver
{
// Deal with initial value
switch observation.scheduling {
case .mainQueue:
if let value = try unsafeReentrantRead(observation.fetchFirst) {
if DispatchQueue.isMain {
onChange(value)
} else {
DispatchQueue.main.async {
// TODO: fetch asynchronously when possible
do {
// Deal with initial value
switch observation.scheduling {
case .mainQueue:
if let value = try unsafeReentrantRead(observation.fetchFirst) {
if DispatchQueue.isMain {
onChange(value)
} else {
DispatchQueue.main.async {
onChange(value)
}
}
}
}
case let .async(onQueue: queue, startImmediately: startImmediately):
if startImmediately {
if let value = try unsafeReentrantRead(observation.fetchFirst) {
queue.async {
onChange(value)
case let .async(onQueue: queue, startImmediately: startImmediately):
if startImmediately {
if let value = try unsafeReentrantRead(observation.fetchFirst) {
queue.async {
onChange(value)
}
}
}
}
case let .unsafe(startImmediately: startImmediately):
if startImmediately {
if let value = try unsafeReentrantRead(observation.fetchFirst) {
onChange(value)
case let .unsafe(startImmediately: startImmediately):
if startImmediately {
if let value = try unsafeReentrantRead(observation.fetchFirst) {
onChange(value)
}
}
}
} catch {
onError(error)
}

// Return a dummy observer, because snapshots never change
Expand Down
Loading