DatabaseWriter

public protocol DatabaseWriter : DatabaseReader

DatabaseWriter is the protocol for all types that can write into an SQLite database.

It is adopted by DatabaseQueue and DatabasePool.

The protocol comes with isolation guarantees that describe the behavior of adopting types in a multithreaded application.

Types that adopt the protocol can in practice provide stronger guarantees. For example, DatabaseQueue provides a stronger isolation level than DatabasePool.

Warning: Isolation guarantees stand as long as there is no external connection to the database. Should you have to cope with external connections, protect yourself with transactions, and be ready to setup a busy handler.

Writing in Database

  • write(_:) Default implementation

    Synchronously executes database updates in a protected dispatch queue, wrapped inside a transaction, and returns the result.

    If the updates throw an error, the transaction is rollbacked and the error is rethrown.

    Eventual concurrent database updates are postponed until the transaction has completed.

    Eventual concurrent reads are guaranteed to not see any partial updates of the database until the transaction has completed.

    It is a programmer error to call this method from another database access method:

    try writer.write { db in
        // Raises a fatal error
        try writer.write { ... )
    }
    

    Throws

    The error thrown by the updates, or by the wrapping transaction.

    Default Implementation

    Declaration

    Swift

    @_disfavoredOverload
    func write<T>(_ updates: (Database) throws -> T) throws -> T

    Parameters

    updates

    The updates to the database.

  • Synchronously executes database updates in a protected dispatch queue, outside of any transaction, and returns the result.

    Eventual concurrent database updates are postponed until the updates are completed.

    Eventual concurrent reads may see partial updates unless you wrap them in a transaction.

    It is a programmer error to call this method from another database access method:

    try writer.write { db in
        // Raises a fatal error
        try writer.writeWithoutTransaction { ... )
    }
    

    Throws

    The error thrown by the updates.

    Declaration

    Swift

    @_disfavoredOverload
    func writeWithoutTransaction<T>(_ updates: (Database) throws -> T) rethrows -> T

    Parameters

    updates

    The updates to the database.

  • Synchronously executes database updates in a protected dispatch queue, outside of any transaction, and returns the result.

    Updates are guaranteed an exclusive access to the database. They wait until all pending writes and reads are completed. They postpone all other writes and reads until they are completed.

    It is a programmer error to call this method from another database access method:

    try writer.write { db in
        // Raises a fatal error
        try writer.barrierWriteWithoutTransaction { ... )
    }
    

    Throws

    The error thrown by the updates.

    Declaration

    Swift

    @_disfavoredOverload
    func barrierWriteWithoutTransaction<T>(_ updates: (Database) throws -> T) rethrows -> T

    Parameters

    updates

    The updates to the database.

  • Asynchronously executes database updates in a protected dispatch queue, outside of any transaction, and returns the result.

    Updates are guaranteed an exclusive access to the database. They wait until all pending writes and reads are completed. They postpone all other writes and reads until they are completed.

    Declaration

    Swift

    func asyncBarrierWriteWithoutTransaction(_ updates: @escaping (Database) -> Void)

    Parameters

    updates

    The updates to the database.

  • asyncWrite(_:completion:) Default implementation

    Asynchronously executes database updates in a protected dispatch queue, wrapped inside a transaction.

    If the updates throw an error, the transaction is rollbacked.

    The completion closure is always called with the result of the database updates. Its arguments are a database connection and the result of the transaction. This result is a failure if the transaction could not be committed.

    Eventual concurrent database updates are postponed until the transaction and the completion closure have completed.

    Eventual concurrent reads are guaranteed to not see any partial updates of the database until the transaction has completed.

    This method is not reentrant.

    Throws

    The error thrown by the updates, or by the wrapping transaction.

    Default Implementation

    Asynchronously executes database updates in a protected dispatch queue, wrapped inside a transaction.

    If the updates throw an error, the transaction is rollbacked.

    The completion closure is always called with the result of the database updates. Its arguments are a database connection and the result of the transaction. This result is a failure if the transaction could not be committed. The completion closure is executed in a protected dispatch queue, outside of any transaction.

    Eventual concurrent database updates are postponed until the transaction and the completion closure have completed.

    Eventual concurrent reads are guaranteed to not see any partial updates of the database until the transaction has completed.

    This method is not reentrant.

    Throws

    The error thrown by the updates, or by the wrapping transaction.

    Declaration

    Swift

    func asyncWrite<T>(
        _ updates: @escaping (Database) throws -> T,
        completion: @escaping (Database, Result<T, Error>) -> Void)

    Parameters

    updates

    The updates to the database.

    completion

    A closure that is called with the eventual transaction error.

  • Asynchronously executes database updates in a protected dispatch queue, outside of any transaction.

    Eventual concurrent reads may see partial updates unless you wrap them in a transaction.

    Declaration

    Swift

    func asyncWriteWithoutTransaction(_ updates: @escaping (Database) -> Void)
  • Synchronously executes database updates in a protected dispatch queue, outside of any transaction, and returns the result.

    Eventual concurrent database updates are postponed until the updates are completed.

    Eventual concurrent reads may see partial updates unless you wrap them in a transaction.

    This method is reentrant. It should be avoided because it fosters dangerous concurrency practices.

    Declaration

    Swift

    func unsafeReentrantWrite<T>(_ updates: (Database) throws -> T) rethrows -> T

Reading from Database

  • Concurrently executes a read-only block in a protected dispatch queue.

    This method must be called from a writing dispatch queue, outside of any transaction. You’ll get a fatal error otherwise.

    The block argument is guaranteed to see the database in the last committed state at the moment this method is called. Eventual concurrent database updates are not visible inside the block.

    To access the fetched results, you call the wait() method of the returned future, on any dispatch queue.

    In the example below, the number of players is fetched concurrently with the player insertion. Yet the future is guaranteed to return zero:

    try writer.writeWithoutTransaction { db in
        // Delete all players
        try Player.deleteAll()
    
        // Count players concurrently
        let future = writer.concurrentRead { db in
            return try Player.fetchCount()
        }
    
        // Insert a player
        try Player(...).insert(db)
    
        // Guaranteed to be zero
        let count = try future.wait()
    }
    

    Declaration

    Swift

    func concurrentRead<T>(_ value: @escaping (Database) throws -> T) -> DatabaseFuture<T>

Transaction Observers

  • Add a transaction observer, so that it gets notified of database changes.

    To remove the observer, use DatabaseReader.remove(transactionObserver:).

    Declaration

    Swift

    public func add(
        transactionObserver: TransactionObserver,
        extent: Database.TransactionObservationExtent = .observerLifetime)

    Parameters

    transactionObserver

    A transaction observer.

    extent

    The duration of the observation. The default is the observer lifetime (observation lasts until observer is deallocated).

  • remove(transactionObserver:) Extension method

    Undocumented

    Declaration

    Swift

    public func remove(transactionObserver: TransactionObserver)

Erasing the content of the database

  • erase() Extension method

    Erases the content of the database.

    Declaration

    Swift

    @_disfavoredOverload
    public func erase() throws

Claiming Disk Space

Asynchronous Database Access

  • write(_:) Extension method, asynchronous

    Asynchronously executes database updates, wrapped inside a transaction, and returns the result.

    Experimental

    If the updates throw an error, the transaction is rollbacked and the error is rethrown.

    Eventual concurrent reads are guaranteed to not see any partial updates of the database until the transaction has completed.

    Throws

    The error thrown by the updates, or by the wrapping transaction.

    Declaration

    Swift

    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
    public func write<T>(_ updates: @escaping @Sendable (Database) throws -> T) async throws -> T

    Parameters

    updates

    The updates to the database.

  • writeWithoutTransaction(_:) Extension method, asynchronous

    Asynchronously executes database updates, outside of any transaction, and returns the result.

    Experimental

    Eventual concurrent reads may see partial updates unless you wrap them in a transaction.

    Throws

    The error thrown by the updates.

    Declaration

    Swift

    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
    public func writeWithoutTransaction<T>(_ updates: @escaping @Sendable (Database) throws -> T) async throws -> T

    Parameters

    updates

    The updates to the database.

  • barrierWriteWithoutTransaction(_:) Extension method, asynchronous

    Asynchronously executes database updates, outside of any transaction, and returns the result.

    Experimental

    Updates are guaranteed an exclusive access to the database. They wait until all pending writes and reads are completed. They postpone all other writes and reads until they are completed.

    Throws

    The error thrown by the updates.

    Declaration

    Swift

    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
    public func barrierWriteWithoutTransaction<T>(
        _ updates: @Sendable @escaping (Database) throws -> T)
    async throws -> T

    Parameters

    updates

    The updates to the database.

  • erase() Extension method, asynchronous

    Erases the content of the database.

    Experimental

    Declaration

    Swift

    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
    public func erase() async throws
  • vacuum() Extension method, asynchronous

    Rebuilds the database file, repacking it into a minimal amount of disk space.

    Experimental

    See https://www.sqlite.org/lang_vacuum.html for more information.

    Declaration

    Swift

    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
    public func vacuum() async throws
  • vacuum(into:) Extension method, asynchronous

    Creates a new database file at the specified path with a minimum amount of disk space.

    Experimental

    See https://www.sqlite.org/lang_vacuum.html#vacuuminto for more information.

    Declaration

    Swift

    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
    public func vacuum(into filePath: String) async throws

    Parameters

    filePath

    file path for new database

Publishing Database Updates

  • writePublisher(updates:) Extension method

    Returns a Publisher that asynchronously writes into the database.

    // DatabasePublishers.Write<Int>
    let newPlayerCount = dbQueue.writePublisher { db -> Int in
        try Player(...).insert(db)
        return try Player.fetchCount(db)
    }
    

    Its value and completion are emitted on the main dispatch queue.

    Declaration

    Swift

    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
    public func writePublisher<Output>(
        updates: @escaping (Database) throws -> Output)
    -> DatabasePublishers.Write<Output>

    Parameters

    updates

    A closure which writes in the database.

  • Returns a Publisher that asynchronously writes into the database.

    // DatabasePublishers.Write<Int>
    let newPlayerCount = dbQueue.writePublisher(
        receiveOn: DispatchQueue.global(),
        updates: { db -> Int in
            try Player(...).insert(db)
            return try Player.fetchCount(db)
        })
    

    Its value and completion are emitted on scheduler.

    Declaration

    Swift

    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
    public func writePublisher<S, Output>(
        receiveOn scheduler: S,
        updates: @escaping (Database) throws -> Output)
    -> DatabasePublishers.Write<Output>
    where S: Scheduler

    Parameters

    scheduler

    A Combine Scheduler.

    updates

    A closure which writes in the database.

  • Returns a Publisher that asynchronously writes into the database.

    // DatabasePublishers.Write<Int>
    let newPlayerCount = dbQueue.writePublisher(
        updates: { db in try Player(...).insert(db) }
        thenRead: { db, _ in try Player.fetchCount(db) })
    

    Its value and completion are emitted on the main dispatch queue.

    Declaration

    Swift

    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
    public func writePublisher<T, Output>(
        updates: @escaping (Database) throws -> T,
        thenRead value: @escaping (Database, T) throws -> Output)
    -> DatabasePublishers.Write<Output>

    Parameters

    updates

    A closure which writes in the database.

    value

    A closure which reads from the database.

  • Returns a Publisher that asynchronously writes into the database.

    // DatabasePublishers.Write<Int>
    let newPlayerCount = dbQueue.writePublisher(
        receiveOn: DispatchQueue.global(),
        updates: { db in try Player(...).insert(db) }
        thenRead: { db, _ in try Player.fetchCount(db) })
    

    Its value and completion are emitted on scheduler.

    Declaration

    Swift

    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
    public func writePublisher<S, T, Output>(
        receiveOn scheduler: S,
        updates: @escaping (Database) throws -> T,
        thenRead value: @escaping (Database, T) throws -> Output)
    -> DatabasePublishers.Write<Output>
    where S: Scheduler

    Parameters

    scheduler

    A Combine Scheduler.

    updates

    A closure which writes in the database.

    value

    A closure which reads from the database.