TM-SGNL-iOS/SignalServiceKit/Storage/Database/SDSDatabaseStorage/V2/DB.swift
TeleMessage developers dde0620daf initial commit
2025-05-03 12:28:28 -07:00

248 lines
7.7 KiB
Swift

//
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import Foundation
public import GRDB
/// Wrapper around `SDSDatabaseStorage` that allows for an access pattern identical
/// to the original (you get a transaction object you pass around to perform database operations)
/// but which is easily stubbed out in tests.
/// Method signatures are identical to those in `SDSDatabaseStorage`.
///
/// This protocol is **not** for testing or stubbing actual database operations (SQL statements).
/// It allows you to write classes that utilize persistent storage, in the abstract, but which hand off the
/// actual queries to lower level helper classes (e.g. the "FooFinder" and "FooModel: SDSCodableModel" classes.)
///
/// Consumers of this protocol should **never** inspect the transaction objects, and should treat them
/// as black boxes. Any class that _actually_ wants to talk to the database to perform reads
/// and writes should use SDS- classes directly, unwrapping these transactions by using `SDSDB.shimOnlyBridge`.
///
/// Check out ToyExample.swift in the SDSDatabaseStorage/V2 directory under SignalServiceKitTests for a walkthrough
/// of the reasoning and how to use this class.
public protocol DB {
associatedtype ReadTransaction: DBReadTransaction
associatedtype WriteTransaction: DBWriteTransaction
// MARK: - Async Methods
func asyncRead<T>(
file: String,
function: String,
line: Int,
block: @escaping (ReadTransaction) -> T,
completionQueue: DispatchQueue,
completion: ((T) -> Void)?
)
func asyncWrite<T>(
file: String,
function: String,
line: Int,
block: @escaping (WriteTransaction) -> T,
completionQueue: DispatchQueue,
completion: ((T) -> Void)?
)
func asyncWriteWithTxCompletion<T>(
file: String,
function: String,
line: Int,
block: @escaping (WriteTransaction) -> TransactionCompletion<T>,
completionQueue: DispatchQueue,
completion: ((T) -> Void)?
)
// MARK: - Awaitable Methods
func awaitableWrite<T>(
file: String,
function: String,
line: Int,
block: (WriteTransaction) throws -> T
) async rethrows -> T
func awaitableWriteWithTxCompletion<T>(
file: String,
function: String,
line: Int,
block: (WriteTransaction) -> TransactionCompletion<T>
) async -> T
// MARK: - Promises
func readPromise<T>(
file: String,
function: String,
line: Int,
_ block: @escaping (ReadTransaction) throws -> T
) -> Promise<T>
func writePromise<T>(
file: String,
function: String,
line: Int,
_ block: @escaping (WriteTransaction) throws -> T
) -> Promise<T>
// MARK: - Value Methods
func read<T>(
file: String,
function: String,
line: Int,
block: (ReadTransaction) throws -> T
) rethrows -> T
func write<T>(
file: String,
function: String,
line: Int,
block: (WriteTransaction) throws -> T
) rethrows -> T
func writeWithTxCompletion<T>(
file: String,
function: String,
line: Int,
block: (WriteTransaction) -> TransactionCompletion<T>
) -> T
// MARK: - Observation
func add(
transactionObserver: TransactionObserver,
extent: Database.TransactionObservationExtent
)
// MARK: - Touching
func touch(_ interaction: TSInteraction, shouldReindex: Bool, tx: DBWriteTransaction)
/// See note on `shouldUpdateChatListUi` parameter in docs for ``TSGroupThread.updateWithGroupModel:shouldUpdateChatListUi:transaction``.
func touch(_ thread: TSThread, shouldReindex: Bool, shouldUpdateChatListUi: Bool, tx: DBWriteTransaction)
func touch(_ storyMessage: StoryMessage, tx: DBWriteTransaction)
}
// MARK: - Default arguments
extension DB {
// MARK: - Async Methods
public func asyncRead<T>(
file: String = #file,
function: String = #function,
line: Int = #line,
block: @escaping (ReadTransaction) -> T,
completionQueue: DispatchQueue = .main,
completion: ((T) -> Void)? = nil
) {
asyncRead(file: file, function: function, line: line, block: block, completionQueue: completionQueue, completion: completion)
}
public func asyncWrite<T>(
file: String = #file,
function: String = #function,
line: Int = #line,
block: @escaping (WriteTransaction) -> T,
completionQueue: DispatchQueue = .main,
completion: ((T) -> Void)? = nil
) {
asyncWrite(file: file, function: function, line: line, block: block, completionQueue: completionQueue, completion: completion)
}
public func asyncWriteWithTxCompletion<T>(
file: String = #file,
function: String = #function,
line: Int = #line,
block: @escaping (WriteTransaction) -> TransactionCompletion<T>,
completionQueue: DispatchQueue = .main,
completion: ((T) -> Void)? = nil
) {
asyncWriteWithTxCompletion(file: file, function: function, line: line, block: block, completionQueue: completionQueue, completion: completion)
}
// MARK: - Awaitable Methods
public func awaitableWrite<T>(
file: String = #file,
function: String = #function,
line: Int = #line,
block: (WriteTransaction) throws -> T
) async rethrows -> T {
return try await awaitableWrite(file: file, function: function, line: line, block: block)
}
public func awaitableWriteWithTxCompletion<T>(
file: String = #file,
function: String = #function,
line: Int = #line,
block: (WriteTransaction) -> TransactionCompletion<T>
) async -> T {
return await awaitableWriteWithTxCompletion(file: file, function: function, line: line, block: block)
}
// MARK: - Promises
public func readPromise<T>(
file: String = #file,
function: String = #function,
line: Int = #line,
_ block: @escaping (ReadTransaction) throws -> T
) -> Promise<T> {
return readPromise(file: file, function: function, line: line, block)
}
public func writePromise<T>(
file: String = #file,
function: String = #function,
line: Int = #line,
_ block: @escaping (WriteTransaction) throws -> T
) -> Promise<T> {
return writePromise(file: file, function: function, line: line, block)
}
// MARK: - Value Methods
public func read<T>(
file: String = #file,
function: String = #function,
line: Int = #line,
block: (ReadTransaction) throws -> T
) rethrows -> T {
return try read(file: file, function: function, line: line, block: block)
}
public func write<T>(
file: String = #file,
function: String = #function,
line: Int = #line,
block: (WriteTransaction) throws -> T
) rethrows -> T {
return try write(file: file, function: function, line: line, block: block)
}
public func writeWithTxCompletion<T>(
file: String = #file,
function: String = #function,
line: Int = #line,
block: (WriteTransaction) -> TransactionCompletion<T>
) -> T {
return writeWithTxCompletion(file: file, function: function, line: line, block: block)
}
// MARK: - Touching
public func touch(_ thread: TSThread, shouldReindex: Bool, tx: DBWriteTransaction) {
self.touch(thread, shouldReindex: shouldReindex, shouldUpdateChatListUi: true, tx: tx)
}
// MARK: - Observation
public func add(transactionObserver: TransactionObserver) {
self.add(transactionObserver: transactionObserver, extent: .observerLifetime)
}
}