Finalize Async/Await support

This commit is contained in:
Aaron L Bratcher 2022-02-24 11:10:26 -05:00
parent 99c9385087
commit 2ef779202d
6 changed files with 85 additions and 24 deletions

View File

@ -228,7 +228,7 @@ public final class AgileDB {
- throws: DBError
*/
public func asyncTableHasKey(table: DBTable, key: String) async throws -> Bool {
public func tableHasKey(table: DBTable, key: String) async throws -> Bool {
let results = await bridgingTableHasKey(table: table, key: key)
switch results {
case .success(let hasKey):
@ -256,7 +256,7 @@ public final class AgileDB {
- returns: DBActivityToken Returns a DBCommandToken that can be used to cancel the command before it executes If the database file cannot be opened nil is returned.
*/
@available(*, deprecated, message: "Use await asyncTableHasKey instead")
@available(*, deprecated, message: "Use await tableHasKey instead")
@discardableResult
public func tableHasKey(table: DBTable, key: String, queue: DispatchQueue? = nil, completion: @escaping (BoolResults) -> Void) -> DBCommandToken? {
let openResults = openDB()
@ -303,7 +303,7 @@ public final class AgileDB {
- throws: DBError
*/
public func asyncTableHasAllKeys(table: DBTable, keys: [String]) async throws -> Bool {
public func tableHasAllKeys(table: DBTable, keys: [String]) async throws -> Bool {
let results = await bridgingTableHasAllKeys(table: table, keys: keys)
switch results {
case .success(let hasKeys):
@ -331,7 +331,7 @@ public final class AgileDB {
- returns: DBActivityToken Returns a DBCommandToken that can be used to cancel the command before it executes If the database file cannot be opened nil is returned.
*/
@available(*, deprecated, message: "Use await asyncTableHasAllKeys instead")
@available(*, deprecated, message: "Use await tableHasAllKeys instead")
@discardableResult
public func tableHasAllKeys(table: DBTable, keys: [String], queue: DispatchQueue? = nil, completion: @escaping (BoolResults) -> Void) -> DBCommandToken? {
let openResults = openDB()
@ -420,7 +420,7 @@ public final class AgileDB {
- throws: DBError
*/
public func asyncKeysInTable(_ table: DBTable, sortOrder: String? = nil, conditions: [DBCondition]? = nil, validateObjects: Bool = false) async throws -> [String] {
public func keysInTable(_ table: DBTable, sortOrder: String? = nil, conditions: [DBCondition]? = nil, validateObjects: Bool = false) async throws -> [String] {
let results = await bridgingKeysInTable(table, sortOrder: sortOrder, conditions: conditions, validateObjects: validateObjects)
switch results {
case .success(let keys):
@ -463,7 +463,7 @@ public final class AgileDB {
*/
@discardableResult
@available(*, deprecated, message: "Use await asyncKeysInTable instead")
@available(*, deprecated, message: "Use await keysInTable instead")
public func keysInTable(_ table: DBTable, sortOrder: String? = nil, conditions: [DBCondition]? = nil, validateObjects: Bool = false, queue: DispatchQueue? = nil, completion: @escaping (KeyResults) -> Void) -> DBCommandToken? {
let openResults = openDB()
if case .failure(_) = openResults {
@ -636,7 +636,7 @@ public final class AgileDB {
- throws: DBError
*/
public func asyncValueFromTable(_ table: DBTable, for key: String) async throws -> String {
public func valueFromTable(_ table: DBTable, for key: String) async throws -> String {
let results = await bridgingValueFromTable(table, for: key)
switch results {
@ -669,7 +669,7 @@ public final class AgileDB {
*/
@discardableResult
@available(*, deprecated, message: "Use await asyncValueFromTable instead")
@available(*, deprecated, message: "Use await valueFromTable instead")
public func valueFromTable(_ table: DBTable, for key: String, queue: DispatchQueue? = nil, completion: @escaping (JsonResults) -> Void) -> DBCommandToken? {
let openResults = openDB()
if case .failure(_) = openResults, !tables.hasTable(table) {
@ -738,7 +738,7 @@ public final class AgileDB {
- throws: DBError
*/
public func asyncDictValueFromTable(_ table: DBTable, for key: String) async throws -> [String: AnyObject] {
public func dictValueFromTable(_ table: DBTable, for key: String) async throws -> [String: AnyObject] {
let results = await bridgingDictValueFromTable(table, for: key)
switch results {
@ -775,7 +775,7 @@ public final class AgileDB {
- returns: Returns a DBCommandToken that can be used to cancel the command before it executes. If the database file cannot be opened or table does not exist nil is returned.
*/
@discardableResult
@available(*, deprecated, message: "Use await asyncDictValueFromTable instead")
@available(*, deprecated, message: "Use await dictValueFromTable instead")
public func dictValueFromTable(_ table: DBTable, for key: String, queue: DispatchQueue? = nil, completion: @escaping (DictResults) -> Void) -> DBCommandToken? {
let openResults = openDB()
if case .failure(_) = openResults, !tables.hasTable(table) {
@ -2038,7 +2038,7 @@ extension AgileDB {
- throws: DBError
*/
public func asyncSqlSelect(_ sql: String) async throws -> [DBRow] {
public func sqlSelect(_ sql: String) async throws -> [DBRow] {
let results = await bridgingSqlSelect(sql)
switch results {
@ -2064,7 +2064,7 @@ extension AgileDB {
- returns: Result<[DBRow], DBError>
*/
@available(*, deprecated, message: "Use await asyncSqlSelect instead")
@available(*, deprecated, message: "Use await sqlSelect instead")
@discardableResult
public func sqlSelect(_ sql: String, queue: DispatchQueue? = nil, completion: @escaping (RowResults) -> Void) -> DBCommandToken? {
let openResults = openDB()

View File

@ -72,6 +72,25 @@ extension DBObject {
public func delete(from db: AgileDB) -> Bool {
return db.deleteFromTable(Self.table, for: key)
}
/**
Asynchronously instantiate object and populate with values from the database.
- parameter db: Database object to hold the data.
- parameter key: Key of the data entry.
- returns: DBObject.
- throws: DBError
*/
public static func loadFromDB(_ db: AgileDB, for key: String) async throws -> Self {
let dictionaryValue = try await db.dictValueFromTable(table, for: key)
guard let dbObject = dbObjectWithDict(dictionaryValue, db: db, for: key) else {
throw DBError.cannotParseData
}
return dbObject
}
/**
Asynchronously instantiate object and populate with values from the database before executing the passed block with object. If object could not be instantiated properly, block is not executed.
@ -83,6 +102,7 @@ extension DBObject {
- returns: DBCommandToken that can be used to cancel the call before it executes. Nil is returned if database could not be opened.
*/
@available(*, deprecated, message: "Use await loadFromDB instead")
@discardableResult
public static func loadObjectFromDB(_ db: AgileDB, for key: String, queue: DispatchQueue? = nil, completion: @escaping (Self) -> Void) -> DBCommandToken? {
let token = db.dictValueFromTable(table, for: key, queue: queue, completion: { (results) in

View File

@ -98,6 +98,7 @@ public enum DBError: Error {
case damagedFile
case cannotOpenFile
case tableNotFound
case cannotParseData
case other(Int)
}
@ -111,6 +112,7 @@ extension DBError: RawRepresentable {
case 11: self = .damagedFile
case 14: self = .cannotOpenFile
case -1: self = .tableNotFound
case -2: self = .cannotParseData
default: self = .other(rawValue)
}
}
@ -122,6 +124,7 @@ extension DBError: RawRepresentable {
case .damagedFile: return 11
case .cannotOpenFile: return 14
case .tableNotFound: return -1
case .cannotParseData: return -2
case .other(let value): return value
}
}

View File

@ -159,7 +159,7 @@ class AsyncTests: XCTestCase {
waitForExpectations(timeout: 20, handler: nil)
}
func testAsyncTableKeys() async {
func testAwaitKeysInTable() async {
let table: DBTable = "asyncTable8"
db.dropTable(table)
db.setValueInTable(table, for: "testKey1", to: "{\"numValue\":2,\"value2\":1}", autoDeleteAfter: nil)
@ -169,14 +169,14 @@ class AsyncTests: XCTestCase {
db.setValueInTable(table, for: "testKey5", to: "{\"numValue\":2,\"value2\":2}", autoDeleteAfter: nil)
do {
let rows = try await db.asyncKeysInTable(table)
let rows = try await db.keysInTable(table)
XCTAssert(rows.count == 5)
} catch {
XCTFail()
}
}
func testAsyncAwaitTableHasKey() async {
func testAwaitTableHasKey() async {
let table: DBTable = "asyncTable7"
db.dropTable(table)
db.setValueInTable(table, for: "testKey1", to: "{\"numValue\":2,\"value2\":1}", autoDeleteAfter: nil)
@ -186,14 +186,14 @@ class AsyncTests: XCTestCase {
db.setValueInTable(table, for: "testKey5", to: "{\"numValue\":2,\"value2\":2}", autoDeleteAfter: nil)
do {
let hasKey = try await db.asyncTableHasKey(table: table, key: "testKey4")
let hasKey = try await db.tableHasKey(table: table, key: "testKey4")
XCTAssertTrue(hasKey)
} catch {
XCTFail()
}
}
func testAsyncAwaitTableHasAllKeys() async {
func testAwaitTableHasAllKeys() async {
let table: DBTable = "asyncTable6"
db.dropTable(table)
db.setValueInTable(table, for: "testKey1", to: "{\"numValue\":2,\"value2\":1}", autoDeleteAfter: nil)
@ -203,18 +203,18 @@ class AsyncTests: XCTestCase {
db.setValueInTable(table, for: "testKey5", to: "{\"numValue\":2,\"value2\":2}", autoDeleteAfter: nil)
do {
var hasKeys = try await db.asyncTableHasAllKeys(table: table, keys: ["testKey1","testKey2","testKey3","testKey4","testKey5"])
var hasKeys = try await db.tableHasAllKeys(table: table, keys: ["testKey1","testKey2","testKey3","testKey4","testKey5"])
XCTAssertTrue(hasKeys)
self.db.deleteFromTable(table, for: "testKey4")
hasKeys = try await db.asyncTableHasAllKeys(table: table, keys: ["testKey1","testKey2","testKey3","testKey4","testKey5"])
hasKeys = try await db.tableHasAllKeys(table: table, keys: ["testKey1","testKey2","testKey3","testKey4","testKey5"])
XCTAssertFalse(hasKeys)
} catch {
XCTFail()
}
}
func testAsyncAwaitValues() async {
func testAwaitValueFromTable() async {
let table: DBTable = "asyncTable5"
db.dropTable(table)
db.setValueInTable(table, for: "testKey1", to: "{\"numValue\":2,\"value2\":1}", autoDeleteAfter: nil)
@ -224,7 +224,7 @@ class AsyncTests: XCTestCase {
db.setValueInTable(table, for: "testKey5", to: "{\"numValue\":2,\"value2\":2}", autoDeleteAfter: nil)
do {
let value = try await db.asyncValueFromTable(table, for: "testKey3")
let value = try await db.valueFromTable(table, for: "testKey3")
let jsonData = value.data(using: .utf8)!
let jsonObject: [String: Int] = try! JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers) as! [String: Int]
XCTAssertTrue(jsonObject["numValue"] == 2)

View File

@ -131,6 +131,19 @@ class DBObjectTests: XCTestCase {
waitForExpectations(timeout: 2, handler: nil)
}
func testLoadFromDB() async throws {
let transaction = Transaction(key: TransactionValue.key, date: Date(), accountKey: TransactionValue.accountKey, notes: TransactionValue.notes, amount: TransactionValue.amount, isNew: TransactionValue.isNew)
transaction.save(to: db)
do {
let object = try await Transaction.loadFromDB(db, for: TransactionValue.key)
XCTAssertEqual(object.accountKey, TransactionValue.accountKey)
XCTAssertEqual(object.amount, TransactionValue.amount)
} catch {
XCTFail()
}
}
func testNestedSave() throws {
let transaction = EncodingTransaction()

View File

@ -4,6 +4,7 @@
- A SQLite database wrapper written in Swift that requires no SQL knowledge to use.
- No need to keep track of columns used in the database; it's automatic.
- Completely thread safe since it uses it's own Thread subclass.
- Works with Async/Await
- Use the publish method to work with Combine and SwiftUI
## Installation Options ##
@ -17,9 +18,8 @@
- For any method that returns an optional, that value is nil if an error occured and could not return a proper value.
## DBObject Protocol ##
DBbjects can have the following types: DBObject, Int, Double, String, Date, Bool, [DBObject], [Int], [Double], [String], [Date]. All properties may be optional. For DBObject properties, the key is stored so the referenced objects can be edited and saved independently
Bool properties read from the database will be interpreted as follows: An integer 0 = false and any other number is true. For string values "1", "yes", "YES", "true", and "TRUE" evaluate to true.
- DBbjects can have the following types saved and read to the DB: DBObject, Int, Double, String, Date, Bool, [DBObject], [Int], [Double], [String], [Date]. All properties may be optional. For DBObject properties, the key is stored so the referenced objects can be edited and saved independently
- Bool properties read from the database will be interpreted as follows: An integer 0 = false and any other number is true. For string values "1", "yes", "YES", "true", and "TRUE" evaluate to true.
### Protocol Definition ###
```swift
@ -39,6 +39,16 @@ public protocol DBObject: Codable {
*/
public init?(db: AgileDB, key: String)
/**
Asynchronously instantiates object and populate with values from the database.
- parameter db: Database object holding the data.
- parameter key: Key of the data entry.
- throws: DBError
*/
public init(db: AgileDB, key: String) async throws
/**
Save the object's encoded values to the database.
@ -52,6 +62,18 @@ public init?(db: AgileDB, key: String)
public func save(to db: AgileDB, autoDeleteAfter expiration: Date? = nil) -> Bool
/**
Asynchronously instantiate object and populate with values from the database.
- parameter db: Database object to hold the data.
- parameter key: Key of the data entry.
- returns: DBObject
- throws: DBError
*/
public static func loadFromDB(_ db: AgileDB, for key: String) -> Self?
/**
Asynchronously instantiate object and populate with values from the database before executing the passed block with object. If object could not be instantiated properly, block is not executed.
@ -385,6 +407,9 @@ public func processSyncFileAtURL(_ localURL: URL!, syncProgress: syncProgressUpd
# Revision History
### 6.3 ###
- Add async/await support
### 6.2 ###
- New method: tableHasAllKeys and it's asynchronous equivilent