I metSQLite.swift
When building projects using the Objective-C programming language, FMDB is basically used for SQLite database operations. After Swift was released, the mixed development of Swift and Objective-C began, and FMDB was still selected for database operation. It wasn’t until pure Swift development that the search for a new SQLite wrapper library began. Github ranks sqLite.SwiftStar the most. After reading ReadMe and feeling good about it, I started.
With the help of Swift language’s powerful generic system, type safety and custom operators, the form definition, add, delete, change and check operations of the database are packaged in a particularly friendly way. For those who haven’t used sqlite.swift, check out sqlite.swift. For those familiar with SQlite.Swift, check out my packaged SQLiteValueExtension library.
The current scheme for storing arrays and dictionaries
For example, if the attribute is an array of user-defined type [BasicInfoModel], first define Expression of the character type: Expression
.
In the storage, with the help of ObjectMapper library, the array into String, the code is as follows:
let string = basic.infos?.toJSONString() ?? ""
let insert = config.insert(infos <- string)
try? connection.run(insert)
Copy the code
When reading from the database, we also need to convert String to [BasicInfoModel] as follows:
for data in try! connection.prepare(config) {
let basic = BasicDataModel(JSON: [String : Any]()) basic? .infos =Array<BasicInfoModel>.init(JSONString: data[parlayRules])
return basic
}
Copy the code
The biggest problem, as you can see from the above code, is that the array/String conversion code is scattered in the business code. If more than one property is an array, the same boilerplate code will be written multiple times. How do you solve this problem gracefully?
Okay, let’s move on to the topic of how to elegantly store array and dictionary data types via SQLite.swift. I will explain it from the following three parts:
SQLite3
Data types supported by the database- To comply with the
Value
The protocol supports user-defined type storage - How to store array and dictionary data types
SQLite3
Data types supported by the database
For SQLite3 databases, only the following basic types are supported; in other words, any other data type needs to be converted to one of these types before being stored. When fetched from the database, it is converted to the corresponding data type.
Swift Type | SQLite Type |
---|---|
Int64 |
INTEGER |
Double |
REAL |
String |
TEXT |
nil |
NULL |
SQLite.Blob |
BLOB |
To comply with theValue
The protocol supports user-defined type storage
For custom types that want to be stored in a database, SQlite.swift defines the Value protocol to regulate the conversion process. This is the official document: custom-types
Here is an example code for an enumeration type that supports Value:
enum Gender: String.Value {
case female
case male
typealias Datatype = String
static var declaredDatatype: String { String.declaredDatatype }
static func fromDatatypeValue(_ datatypeValue: String) -> Gender {
return Gender(rawValue: datatypeValue) ?? .female
}
var datatypeValue: String { rawValue }
}
Copy the code
How to store array and dictionary data types
Through the above foreshadowing, we have a train of thought. Make arrays and dictionaries Value compliant.
1. What types are arrays and dictionaries converted to
The first thing to decide is what type to convert arrays and dictionaries to, and my choice is String.
2. How to convert toString
Then you need to figure out how to convert to String. Taking arrays as an example, we can use the JSONSerialization class to convert a canonical array to Data, and then to String. Example code is as follows:
if let data = try? JSONSerialization.data(withJSONObject: stringArray, options: []) {
return String(data: data, encoding: .utf8) ?? ""
}
Copy the code
3. How to convert to a compliant JSONObject
Then you have to figure out how to convert an array of any type into a compliant JSONObject. There is a cure-all solution in software: “If there is no problem that cannot be solved by adding a layer of abstraction, then add another layer.” The StringValueExpressible protocol is introduced to make array. Elment comply with this protocol and convert it to and from String. StringValueExpressible is defined as follows:
public protocol StringValueExpressible {
associatedtype ValueType = Self
static func fromStringValue(_ stringValue: String) -> ValueType
var stringValue: String { get}}Copy the code
By doing this, we can convert [StringValueExpressible] to [String].
4. Array complianceValue
Implementation code of
extension Array: Value where Element: StringValueExpressible {
public typealias Datatype = String
public static var declaredDatatype: String { String.declaredDatatype }
public static func fromStringValue(_ stringValue: String) -> Self {
var result = [Element] ()if let object = try? JSONSerialization.jsonObject(with: Data(stringValue.utf8), options: []) as? [String] {
for string in object {
let value = Element.fromStringValue(string) as! Element
result.append(value)
}
}
return result
}
public var stringValue: String {
let stringArray = self.map{$0.stringValue }
if let data = try? JSONSerialization.data(withJSONObject: stringArray, options: []) {
return String(data: data, encoding: .utf8) ?? ""
}
return ""
}
public static func fromDatatypeValue(_ datatypeValue: Datatype) -> Self {
return fromStringValue(datatypeValue)
}
public var datatypeValue: Datatype {
return stringValue
}
}
Copy the code
We restrict array. Elment to comply with the StringValueExpressible protocol through the where conditional statement.
Finally, it can be summarized as follows: As long as the elements in the array comply with the StringValueExpressible protocol, the array complies with the Value protocol and can be stored via sqlite.swift. The encapsulated SQLiteValueExtension library, then, internally makes Int, Double, Bool, String, Data, Date, etc., compliant with the StringValueExpressible protocol. Therefore, array types such as [Int] and [Date] can be stored directly.
Storage array, dictionary example code
BasicInfoModel is a custom data class that complies with Value and StringValueExpressible protocols.
class BasicDAO: DataBaseAccessObject {
static let config = Table("config")
static let normalFloat = Expression<Float? > ("normal_float")
static let normalInt = Expression<Int? > ("normal_int")
static let normalModel = Expression<BasicInfoModel? > ("normal_model")
static let normalString = Expression<String? > ("normal_string")
static let intArray = Expression"[Int]? > ("int_array")
static let stringArray = Expression"[String]? > ("string_array")
static let modelArray = Expression"[BasicInfoModel]? > ("model_array")
static let intStringDict = Expression"[Int:String]? > ("int_string_dict")
static let stringModelDict = Expression"[String:BasicInfoModel]? > ("string_model_dict")
class func createTable(a)throws {
try connection.run(config.create(ifNotExists: true) { t in
t.column(normalFloat)
t.column(normalInt)
t.column(normalString)
t.column(intArray)
t.column(intStringDict)
t.column(stringArray)
t.column(normalModel)
t.column(modelArray)
t.column(stringModelDict)
})
}
class func insertEntity(_ basic: BasicDataModel) throws {
let insert = config.insert(normalFloat <- basic.normalFloat,
normalInt <- basic.normalInt,
normalString <- basic.normalString,
normalModel <- basic.normalModel,
intArray <- basic.intArray,
stringArray <- basic.stringArray,
modelArray <- basic.modelArray,
intStringDict <- basic.intStringDict,
stringModelDict <- basic.stringModelDict)
try connection.run(insert)
}
class func queryRows(a)throws- > [BasicDataModel]? {
do {
let rows = try connection.prepare(config)
var result = [BasicDataModel] ()for data in rows {
let basic = BasicDataModel(JSON: [String : Any] ())! basic.normalFloat = data[normalFloat] basic.normalInt = data[normalInt] basic.normalString = data[normalString] basic.normalModel = data[normalModel] basic.intArray = data[intArray] basic.stringArray = data[stringArray] basic.modelArray = data[modelArray] basic.stringModelDict = data[stringModelDict] basic.intStringDict = data[intStringDict] result.append(basic) }return result
} catch let error {
print("queryError:\(error.localizedDescription)")}return nil}}Copy the code
How to useSQLiteValueExtension
Import from Cocoapods:
pod 'SQLiteValueExtension'
Copy the code
Making the address
SQLiteValueExtension