GRDB.swift v4.1.0 Release Notes
Release Date: 2019-06-20 // almost 5 years ago-
๐ Released June 20, 2019 • diff
- ๐ #537: Remove useless parenthesis from generated SQL
- ๐ #538 by @Timac: Add FAQ to clarify "Wrong number of statement arguments" error with "like '%?%'"
- #539: Expose joining methods on both requests and associations
- โก๏ธ #540: Update SQLite to 3.28.0 (thanks to @swiftlyfalling)
- ๐ #542: Move eager loading of hasMany associations to FetchRequest
- #546 by @robcas3: Fix SPM errors with Xcode 11 beta
- ๐ #549 Support for Combine
- #550 Asynchronous Database Access Methods
- #555 Avoid a crash when read-only access can't be established
๐ Documentation Diff
๐ Good practices evolve: 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.
๐ 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 new APIs for building observation, and combining observations together in order to avoid data races.
The [ValueObservation Error Handling](README.md#valueobservation-error-handling) chapter explains with more details how to deal with observation errors.
API Diff
Asynchronous database access methods
protocol DatabaseReader { + var configuration: Configuration { get } + + #if compiler(>=5.0) + func asyncRead(_ block: @escaping (Result<Database, Error>) -> Void) + #endif } protocol DatabaseWriter { + func asyncWriteWithoutTransaction(_ updates: @escaping (Database) -> Void) + + #if compiler(>=5.0) + func asyncWrite<T>(_ updates: @escaping (Database) throws -> T, completion: @escaping (Database, Result<T, Error>) -> Void) + #endif }
ValueObservation changes
+extension FetchRequest { + func observationForCount() -> ValueObservation<...> +} +extension FetchRequest where RowDecoder: ... { + func observationForAll() -> ValueObservation<...> + func observationForFirst() -> ValueObservation<...> +) +extension TableRecord { + static func observationForCount() -> ValueObservation<...> + static func observationForAll() -> ValueObservation<...> + static func observationForFirst() -> ValueObservation<...> +} extension ValueObservation where ... { + @available(*, deprecated) static func trackingCount<Request: FetchRequest>(_ request: Request) -> ValueObservation<...> + @available(*, deprecated) static func trackingAll<Request: FetchRequest>(_ request: Request) -> ValueObservation<...> + @available(*, deprecated) static func trackingOne<Request: FetchRequest>(_ request: Request) -> ValueObservation<...> } extension ValueObservation where Reducer: ValueReducer { - func start(in reader: DatabaseReader, onError: ((Error) -> Void)? = nil, onChange: @escaping (Reducer.Value) -> Void) throws -> TransactionObserver + func start(in reader: DatabaseReader, onChange: @escaping (Reducer.Value) -> Void) throws -> TransactionObserver + func start(in reader: DatabaseReader, onError: @escaping (Error) -> Void, onChange: @escaping (Reducer.Value) -> Void) -> TransactionObserver + func combine<..., Combined>(..., transform: @escaping (...) -> Combined) -> ValueObservation<...> } extension ValueObservation where Reducer: ValueReducer, Reducer.Value: Equatable { + @available(*, deprecated) func distinctUntilChanged() -> ValueObservation<...> + func removeDuplicates() -> ValueObservation<...> }
Joining methods on both requests and associations
+protocol JoinableRequest { + associatedtype RowDecoder +} + +extension JoinableRequest { + func including<A: AssociationToMany>(all association: A) -> Self where A.OriginRowDecoder == RowDecoder + func including<A: Association>(optional association: A) -> Self where A.OriginRowDecoder == RowDecoder + func including<A: Association>(required association: A) -> Self where A.OriginRowDecoder == RowDecoder + func joining<A: Association>(optional association: A) -> Self where A.OriginRowDecoder == RowDecoder + func joining<A: Association>(required association: A) -> Self where A.OriginRowDecoder == RowDecoder +} -protocol DerivableRequest: SelectionRequest, FilteredRequest, OrderedRequest { } +protocol DerivableRequest: SelectionRequest, FilteredRequest, OrderedRequest, JoinableRequest { }
FetchRequest changes
+struct PreparedRequest { + var statement: SelectStatement + var adapter: RowAdapter? + init(statement: SelectStatement, adapter: RowAdapter? = nil) +} protocol FetchRequest { + // deprecated func prepare(_ db: Database, forSingleResult singleResult: Bool) throws -> (SelectStatement, RowAdapter?) + func makePreparedRequest(_ db: Database, forSingleResult singleResult: Bool) throws -> PreparedRequest }
The core FetchRequest preparation method is now
makePreparedRequest(_:forSingleResult:)
. The former core methodprepare(_:forSingleResult:)
will remain a requirement of the FetchRequest protocol until GRDB 5 due to semantic versioning constraints. Both methods are provided with a default implementation which makes each one depend on the other: this creates an infinite loop unless you provide at least one of them. If you have a choice, implement onlymakePreparedRequest(_:forSingleResult:)
.