GRDB.swift v1.0.0 Release Notes

  • 🚀 Released June 20, 2017 :tada:

    GRDB 1.0 comes with enhancements, and API stability.

    It comes with breaking changes, but the good news is that they are the last (until GRDB 2.0) :sweat_smile:!

    • Requirements have changed: Xcode 8.3+ / Swift 3.1

      As a matter of fact, GRDB 1.0 still supports Xcode 8.1 and Swift 3.0. But future versions are free to use Swift 3.1 features, and will require Xcode 8.3+.

      The targetted operating systems are unchanged: iOS 8.0+ / OSX 10.9+ / watchOS 2.0+

    • Record types have their persistentDictionary property replaced with the encode(to:) method:

      struct Player : Persistable {
          let name: String
          let score: Int
      
          // Old
      //    var persistentDictionary: [String: DatabaseValueConvertible?] {
      //        return [
      //            "name": name,
      //            "score": score,
      //        ]
      //    }
      
          // New
          func encode(to container: inout PersistenceContainer) {
              container["name"] = name
              container["score"] = score
          }
      }
      

      This is good for applications that declare lists of columns:

      struct Player : RowConvertible, Persistable {
          let name: String
          let score: Int
      
          static let databaseTableName = "players"
      
          // Declare Player columns
          enum Columns {
              static let name = Column("name")
              static let score = Column("score")
          }
      
          // Use columns in `init(row:)`
          init(row: Row) {
              name = row.value(Columns.name)
              score = row.value(Columns.score)
          }
      
          // Use columns in the new `encode(to:)` method:
          func encode(to container: inout PersistenceContainer) {
              container[Columns.name] = name
              container[Columns.score] = score
          }
      }
      
    • Database Observation has been enhanced:

      Database.afterNextTransactionCommit(_:) is the simplest way to handle successful transactions, and synchronize the database with other resources such as files, or system sensors (documentation).

      // Make sure the database is inside a transaction
      db.inSavepoint {
          // Perform some database job
          try ...
      
          // Register extra job that is only executed after database changes
          // have been committed and written to disk.
          db.afterNextTransactionCommit { ... }
      }
      

      On the low-level side, applications can now specify the extent of database observation (documentation).

    • DatabaseMigrator is easier to test, with its DatabaseMigrator.migrate(_:upTo:) method which partially migrates your databases (documentation).

    • On the side of database schema introspection, the new Database.foreignKeys(on:) method lists the foreign keys defined on a table.

    Full list of changes

     class Database {
    -    func add(transactionObserver: TransactionObserver)
    +    func add(transactionObserver: TransactionObserver, extent: TransactionObservationExtent = .observerLifetime)
    +    func afterNextTransactionCommit(_ closure: @escaping (Database) -> ())
    +    func foreignKeys(on tableName: String) throws -> [ForeignKeyInfo]
     }
    
    -struct DatabaseCoder: DatabaseValueConvertible
    
     struct DatabaseMigrator {
    +    func migrate(_ writer: DatabaseWriter, upTo targetIdentifier: String) throws
     }
    
     protocol DatabaseWriter : DatabaseReader {
    -    func add(transactionObserver: TransactionObserver)
    +    func add(transactionObserver: TransactionObserver, extent: TransactionObservationExtent = .observerLifetime)
     }
    
     protocol MutablePersistable : TableMapping {
    -    var persistentDictionary: [String: DatabaseValueConvertible?] { get }
    +    func encode(to container: inout PersistenceContainer)
     }
    
     extension QueryInterfaceRequest {
    -    func contains(_ value: SQLExpressible) -> SQLExpression
    -    func exists() -> SQLExpression
     }
    
     extension Request {
    -    func adapted(_ adapter: @escaping (Database) throws -> RowAdapter) -> AnyRequest
    +    func adapted(_ adapter: @escaping (Database) throws -> RowAdapter) -> AdaptedRequest<Self>
     }
    
    protocol TypedRequest : Request {
    -    associatedtype Fetched
    +    associatedtype RowDecoder
    }
    
     extension TypedRequest {
    -    func adapted(_ adapter: @escaping (Database) throws -> RowAdapter) -> AnyTypedRequest<Fetched>
    +    func adapted(_ adapter: @escaping (Database) throws -> RowAdapter) -> AdaptedTypedRequest<Self>
     }