ALBNoSQLDB
Swift 编写的 SQLite 数据库包装,无需 SQL 知识即可使用。
无需跟踪数据库中使用的列,它是自动的。
因为它使用自己的 Thread 子类,所以完全线程安全。
版本 6.0 中的新功能
- 新的发布者方法,用于 SwiftUI 和 Combine。发布者返回新的 DBResults 对象。如果发布的 DBResults 添加、删除或更新了键,所有活动发布者都将发送新结果。
- 新的可下标的 DBResults 对象。只存储键以更好地使用内存。
- DBObject 结构现在可以存储 [String],[Int],[Double] 和 [Date] 类型。(不支持嵌套对象。)
版本 5.1 中的新功能
- 用 Swift 5 在 Xcode 10.2 中开发和测试。
- 引入了 DBObject 协议。见下面。
- debugMode 属性重命名为 isDebugging。
- 现在提供值时在
key
对象中提供的key
被忽略,而不是引发错误。 keysInTable
方法中的新参数validateObjects
将忽略引用不在表中的对象的条件集。
版本5的新特性
- 使用Xcode 10.1开发和测试
- 一些方法已被弃用,并提供带重命名版本的方法以提高使用时的清晰度。
- 数据可异步检索。
- 类属性
sharedInstance
已重命名为shared
。 - 方法不再是类级别的,必须通过db的实例来访问。将当前代码中的类名后追加.shared即可简单更新。
安装选项
- Swift 包管理器
- CocoaPods
pod ALBNoSQLDB
- 将所有.swift源文件包含到您的项目中
开始使用
- 使用ALBNoSQLDB最简单的方式是使用遵循DBObject协定的对象。这使得您能够轻松地在数据库中保存或实例化对象。对象只能有简单类型:Int、Double、String、Date、Bool、[Int]、[Double]、[String]、[Date]。(这些可以可选)不支持嵌套对象。
- 另外,您可以使用从JSON字符串工作的低级方法。JSON中支持的类型有字符串、int、double、bool以及字符串、int或double数组的底层对象。如果方法返回可选值,该值在发生错误且无法返回正确值时将为nil。
DBObject 协定
创建遵循DBObject协定的类或结构体,并可以实例化对象,这些对象会从数据库中以同步或异步方式自动填充数据,并将数据保存到数据库中。从数据库读取的Bool属性将被解释如下:整数为0 = false,任何其他数字为true。对于字符串值"1"、"yes"、"YES"、"true"和"TRUE"将评估为true。
协议定义
public protocol DBObject: Codable {
static var table: DBTable { get }
var key: String { get set }
}
协议方法
/**
Instantiate object and populate with values from the database. If instantiation fails, nil is returned.
- parameter db: Database object holding the data.
- parameter key: Key of the data entry.
*/
public init?(db: ALBNoSQLDB, key: String)
/**
Save the object to the database based on the values set in the encode method of the object.
- parameter db: Database object to hold the data.
- parameter expiration: Optional Date specifying when the data is to be automatically deleted. Default value is nil specifying no automatic deletion.
- returns: Discardable Bool value of a successful save.
*/
@discardableResult
public func save(to db: ALBNoSQLDB, autoDeleteAfter expiration: Date? = nil) -> Bool
/**
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.
- parameter db: Database object to hold the data.
- parameter key: Key of the data entry.
- parameter queue: DispatchQueue to run the execution block on. Default value is nil specifying the main queue.
- parameter block: Block of code to execute with instantiated object.
- returns: DBCommandToken that can be used to cancel the call before it executes. Nil is returned if database could not be opened.
*/
public static func loadObjectFromDB(_ db: ALBNoSQLDB, for key: String, queue: DispatchQueue? = nil, completion: @escaping (Self) -> Void) -> DBCommandToken?
示例结构
import ALBNoSQLDB
enum Table: String {
static let categories: DBTable = "Categories"
static let accounts: DBTable = "Accounts"
static let people: DBTable = "People"
}
struct Category: DBObject {
static var table: DBTable { return Table.categories }
var key = UUID().uuidString
var accountKey = ""
var name = ""
var inSummary = true
init() { }
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CategoryKey.self)
key = try container.decode(String.self, forKey: .key)
accountKey = try container.decode(String.self, forKey: .accountKey)
name = try container.decode(String.self, forKey: .name)
inSummary = try container.decode(Bool.self, forKey: .inSummary)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CategoryKey.self)
try container.encode(accountKey, forKey: .accountKey)
try container.encode(name, forKey: .name)
try container.encode(inSummary, forKey: .inSummary)
}
}
// save to database
category.save(to: db)
// save to database, automatically delete after designated date
category.save(to: db, autoDeleteAfter: deletionDate)
// instantiate synchronously
guard let category = Category(db: db, key: categoryKey) else { return }
// instantiate asynchronously
let token = Category.loadObjectFromDB(db, for: categoryKey) { (category) in
// use category object
}
// token allows you to cancel the asynchronous call before completion
DBResults 类
- 与 DBObject 元素协同工作
- 使用数据库引用和键实例化类
- 仅存储键以最小化内存使用
- 数据库发布者返回此类的实例
使用方法
guard let keys = db.keysInTable(Category.table) else { return }
let categories = DBResults<Category>(db: db, keys: keys)
for category in categories {
// use category object
}
for index in 0..<categories.count {
let category = categories[index]
// use category object
}
发布者
- 使用发布者结合Combine订阅者和SwiftUI
- 根据需要发送DBResults以反映完成的查询和更新的结果
- 使用完成(completion)发送可能的错误
使用方法
let publisher: DBResultsPublisher<Transaction> = db.publisher(table: Transaction.table)
let _ = publisher.sink(receiveCompletion: { _ in }) { ( results) in
// assign to AnyCancellable property
}
低级方法
键
检查给定的表中是否包含指定的键。
let table: DBTable = "categories"
if let hasKey = ALBNoSQLDB.shared.tableHasKey(table:table, key:"category1") {
// process here
if hasKey {
// table has key
} else {
// table didn't have key
}
} else {
// handle error
}
返回给定表中键的数组。可选地根据根级值指定排序顺序
let table: DBTable = "categories"
if let tableKeys = ALBNoSQLDB.shared.keysInTable(table, sortOrder:"name, date desc") }
// process keys
} else {
// handle error
}
返回与给定表中的一组条件匹配的键的数组。(请参阅类文档以获取更多信息)
let table: DBTable = "accounts"
let accountCondition = DBCondition(set:0,objectKey:"account", conditionOperator:.equal, value:"ACCT1")
if let keys = ALBNoSQLDB.shared.keysInTable(table, sortOrder: nil, conditions: [accountCondition]) {
// process keys
} else {
// handle error
}
值
数据可以像这里显示的那样手动设置或检索,或者你的类/结构可以遵循以下所述的DBObject协议,并使用内置的init和save方法。
设置表中的值
let table: DBTable = "Transactions"
let key = UUID().uuidString
let dict = [
"key": key
, "accountKey": "Checking"
, "locationKey" :"Kroger"
, "categoryKey": "Food"
]
let data = try! JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted)
let json = String(data: data, encoding: .utf8)!
// the key object in the json value is ignored in the setValue method
if ALBNoSQLDB.shared.setValueInTable(table, for: key, to: json)
// success
} else {
// handle error
}
检索给定键的值
let table: DBTable = "categories"
if let jsonValue = ALBNoSQLDB.shared.valueFromTable(table, for:"category1") {
// process value
} else {
// handle error
}
if let dictValue = ALBNoSQLDB.shared.dictValueFromTable(table, for:"category1") {
// process dictionary value
} else {
// handle error
}
删除给定键的值
let table: DBTable = "categories"
if ALBNoSQLDB.shared.deleteFromTable(table, for:"category1") {
// value was deleted
} else {
// handle error
}
异步检索数据
从版本5开始,ALBNoSQLDB允许异步检索数据。返回的DBCommandToken允许在操作之前取消命令。例如,如果TableView由数据库驱动,并且滚动速度太快以致于无法有效查看数据,在prepareForReuse方法中可以调用令牌的cancel方法,这样数据库就不需要在不再查看的单元格中检索数据。
let db = ALBNoSQLDB.shared
let table: DBTable = "categories"
guard let token = db.valueFromTable(table, for: key, completion: { (results) in
if case .success(let value) = results {
// use value
} else {
// error
}
}) else {
XCTFail("Unable to get value")
return
}
// save token for later use
self.token = token
// cancel operation
let successful = token.cancel()
可用的异步方法
- tableHasKey
- keysInTable
- valueFromTable
- dictValueFromTable
- sqlSelect
- DBObject协议中的loadObjectFromDB
SQL查询
ALBNoSQLDB 允许您进行标准 SQL 查询,以解决更复杂的查询。因为给定的值实际上已拆分为表中的单独列,因此可以传递标准 SQL 语句,并可选择返回行数组(值数组)。
let db = ALBNoSQLDB.shared
let sql = "select name from accounts a inner join categories c on c.accountKey = a.key order by a.name"
if let results = db.sqlSelect(sql) {
// process results
} else {
// handle error
}
同步
在处理任何数据之前,ALBNoSQLDB 可以通过启用同步并与同步日志共享来与其他自己的实例进行同步。
/**
Enables syncing. Once enabled, a log is created for all current values in the tables.
- returns: Bool If syncing was successfully enabled.
*/
public func enableSyncing() -> Bool
/**
Disables syncing.
- returns: Bool If syncing was successfully disabled.
*/
public func disableSyncing() -> Bool
/**
Read-only array of unsynced tables. Any tables not in this array will be synced.
*/
var unsyncedTables: [String]
/**
Sets the tables that are not to be synced.
- parameter tables: Array of tables that are not to be synced.
- returns: Bool If list was set successfully.
*/
public func setUnsyncedTables(_ tables: [String]) -> Bool
/**
Creates a sync file that can be used on another ALBNoSQLDB instance to sync data. This is a synchronous call.
- parameter filePath: The full path, including the file itself, to be used for the log file.
- parameter lastSequence: The last sequence used for the given target Initial sequence is 0.
- parameter targetDBInstanceKey: The dbInstanceKey of the target database. Use the dbInstanceKey method to get the DB's instanceKey.
- returns: (Bool,Int) If the file was successfully created and the lastSequence that should be used in subsequent calls to this instance for the given targetDBInstanceKey.
*/
public func createSyncFileAtURL(_ localURL: URL!, lastSequence: Int, targetDBInstanceKey: String) -> (Bool, Int)
/**
Processes a sync file created by another instance of ALBNoSQL This is a synchronous call.
- parameter filePath: The path to the sync file.
- parameter syncProgress: Optional function that will be called periodically giving the percent complete.
- returns: (Bool,String,Int) If the sync file was successfully processed,the instanceKey of the submiting DB, and the lastSequence that should be used in subsequent calls to the createSyncFile method of the instance that was used to create this file. If the database couldn't be opened or syncing hasn't been enabled, then the instanceKey will be empty and the lastSequence will be equal to zero.
*/
public typealias syncProgressUpdate = (_ percentComplete: Double) -> Void
public func processSyncFileAtURL(_ localURL: URL!, syncProgress: syncProgressUpdate?) -> (Bool, String, Int)