SwiftSQLiteORM is a Swift SQLite ORM Protocol build on GRDB.swift/SQLCipher, will auto create then connect database, create or alter table schema, mapping value between instance property and table column.
- auto create and connect database file
- auto create or alter table schema relies on table version specified
- support customize table column and property name mapping
- privde convenient filter operator, also support raw SQL expression
SwiftSQLiteORM is available through CocoaPods. To install it, simply add the following line to your Podfile:
pod 'SwiftSQLiteORM'
relies on:
s.dependency 'Runtime'
s.dependency 'GRDB.swift/SQLCipher'
s.dependency 'SQLCipher', '~> 4.0'
refers to testReadmeExample()
in TestSwiftSQLiteORM.swift
:
import SwiftSQLiteORM
func testReadmeExample() {
// nested struct will store as JSON string
struct ExampleNested: Codable {
let desc: String
let index: Int
}
struct ExampleType: DBTableDef {
let name: String
let data: ExampleNested
typealias ORMKey = Columns
/// keep blank or return nil for using hidden 'rowid' column
static var primaryKey: Columns? {
return .name
}
enum Columns: String, DBTableKey {
case name
case data
}
}
do {
// insert / update
let c = ExampleType(name: "c", data: ExampleNested(desc: "c", index: 1))
let u = ExampleType(name: "u", data: ExampleNested(desc: "u", index: 2))
try DBMgnt.push([c, u])
// select
let arr = try DBMgnt.fetch(ExampleType.self, .eq(.name, c.name))
XCTAssert(arr.count == 1, "failed")
XCTAssert(arr[0].name == c.name, "failed")
// delete
try DBMgnt.deletes([c]) // require primaryKey in table definition
try DBMgnt.delete(ExampleType.self, .eq(.name, u.name))
let count = try DBMgnt.fetch(ExampleType.self, .eq(.name, c.name)).count
XCTAssert(count == 0, "failed")
// clear
try DBMgnt.clear(ExampleType.self)
// drop table
try DBMgnt.drop(ExampleType.self)
} catch {
if let err = error as? DBORMError {
fatalError("failed to try block: \(err.localizedDescription)")
} else {
fatalError("failed to try block: \(error.localizedDescription)")
}
}
}
you can specify table name, or use a seperate database file.
struct ExampleType: DBTableDef {
let name: String
let data: ExampleNested
typealias ORMKey = Columns
static var primaryKey: Columns? {
return .name
}
enum Columns: String, DBTableKey {
case name
case data
}
static var tableName: String {
return "orm_example_type_t"
}
/// schema version for table columns, default 0
/// - increase version after you add columns
static var tableVersion: Double {
return 0.0001
}
static var databaseName: String {
return "orm_example_database.sqlite"
}
/// update instance property value created by type reflection
/// - only ORMKey covered property can restore value from database column
/// - others property will use default value
static func ormUpdateNew(_ value: inout ExampleType) -> ExampleType {
}
}
filter option use DBRecordFilter
to pass value to SQLite, make calculation or comparison to ORMKey
, when running fetch
or delete
.
SQLite will cast those input value according to column type, before performing calculation or comparison.
supported column types are listed in DBStoreType
:
- .INTEGER
- .REAL
- .TEXT
- .BLOB
and any custom types confirms to DBPrimitive
can box value inside DBStoreValue
when store into database:
- .integer(Int64)
- .real(Double)
- .text(String)
- .blob(Data)
also support optinal property, and column data is nullable.
after table was created in database, DO NOT
change column type, including optional type, for those property already mapping table column in database.
any ORM types should conforms to DBPrimitive
or Codable
protocol, some basic types a buildin support.
extension Bool: DBPrimitive {}
extension Int: DBPrimitive {}
extension Int8: DBPrimitive {}
extension Int16: DBPrimitive {}
extension Int32: DBPrimitive {}
extension Int64: DBPrimitive {}
extension UInt: DBPrimitive {}
extension UInt8: DBPrimitive {}
extension UInt16: DBPrimitive {}
extension UInt32: DBPrimitive {}
extension UInt64: DBPrimitive {}
extension Float: DBPrimitive {}
extension Double: DBPrimitive {}
extension String: DBPrimitive {}
extension NSString: DBPrimitive {}
extension Data: DBPrimitive {}
extension NSData: DBPrimitive {}
extension NSNumber: DBPrimitive {}
//extension NSDecimalNumber: DBPrimitive {}
extension Decimal: DBPrimitive {}
extension CGFloat: DBPrimitive {}
extension UUID: DBPrimitive {}
extension NSUUID: DBPrimitive {}
// store date as "yyyy-MM-dd HH:mm:ss.SSS" in database, and restore will loss precision
extension Date: DBPrimitive {}
extension NSDate: DBPrimitive {}
and the ObjC wrappered type should return mock type in its ormTypeInfo() like NSString
, NSNumber
does.
you can add other types conforms to DBPrimitive
, support store / restore to database, take sturct URL
for example:
extension URL: DBPrimitive {
public init() {
// will be placed by database value later
self.init(string: "a://a.a")!
}
public static var ormStoreType: SwiftSQLiteORM.DBStoreType { .TEXT }
public func ormToStoreValue() -> SwiftSQLiteORM.DBStoreValue? {
return .text(self.absoluteString)
}
public static func ormFromStoreValue(_ value: SwiftSQLiteORM.DBStoreValue) -> URL? {
guard case .text(let string) = value else {
return nil
}
return URL(string: string)
}
}
so URL
store string to database, then restore its value from string, for its column type is .TEXT
.
then you can use URL
in your struct / class property as other DBPrimitive
types like Int
, String
, which support directly store / restore to database.
more refers to testDBPrimitiveProtocol()
in TestSwiftSQLiteORM.swift
.
those complex / nested struct or class which conforms to Codable
will first encoded as JSON string before store string to database, then restore string to JSON object and mapping dictionary value to instance propertis.
and those complex / nested struct or class also support filter calculation or comparison, according to column type .TEXT
.
you can run test in TestSwiftSQLiteORM.swift
, includnig:
- primitive type CURD
- nested type CURD
- throw invalid property type
- fetch / delete with filter operation
- alter table on tableVersion
- multithreading CURD
- performance
default use GRDB's SQLCipher branch, will auto genearte passphrase for using, then store in KeyChain for next app cold start.
more refers to DBKeyChain.swift
.
SwiftSQLiteORM also relies on a modified AnyCoder source from SQLiteORM, and provide its MIT LICENSE under Codec
folder.
SwiftSQLiteORM is available under the MIT license. See the LICENSE file for more info.