[Feature request] Swift 4 Codable Encoder Decoder
Closed this issue · 7 comments
Hi,
I'm currently evaluating Swift Mysql integration and was wondering if Swift 4 Codable protocol would be possible to integrate?
struct User: Codable {
let id: Int
let userName: String
let age: Int?
// Generated automatically by the compiler if not specified
private enum CodingKeys: String, CodingKey {
case id
case userName = "user_name"
case age
}
// Generated automatically by the compiler if not specified
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: . id)
try container.encode(userName, forKey: . userName)
try container.encode(age, forKey: . age)
}
// Generated automatically by the compiler if not specified
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(Int.self, forKey: .id)
userName = try container.decode(String.self, forKey: .userName)
age = try container.decode(Int.self, forKey: .age)
}
}
CodingKeys encode and decode are generated automatically. Reference https://www.mikeash.com/pyblog/friday-qa-2017-07-14-swiftcodable.html
An mysql ORM could now implement the necessary encode and decode container for arrays of objects. It looks similar to your current implementation.
Would such an integration be possible?
Regards,
Patrick
Hello.
We could update the library to treat current QueryRowResultType
and Swift.Codable
as query results and parameters, and could make more use cases and API design for the integration.
Decode functionality would be worth a first try and simplifies library usage:
struct User: Codable {
let id: Int
let user_name: String
let age: Int
}
let rows: [User] = try conn.query("SELECT id, user_name, age FROM users")
Encoding could be used for INSERT/UPDATE statements. I assume you already use reflection to serialise structs to SQL statements.
Some ORM patterns to represent a table like User.findById(id: int), user.save(), user.delete() would also be nice.
I will try to make some example code for decode functionality as soon as i figure out how to use KeyedDecodingContainer.
I prepared a simplified decoder to decode one object. Decoding an array of objects adds a lot of boiler plate code and complicates stuff a lot.
struct User: Codable {
let id: Int
let username: String
let age: Int? // only set if RowKeyedDecodingContainer.contains() returns true
}
let decoder = RowDecoder()
let user = try User(from: decoder)
The decoder could represent a single row and hold colmnNames and data. Close to QueryRowResult. User(from: decoder)
calls decoder.container().
struct RowDecoder : Decoder {
var codingPath = [CodingKey]()
var userInfo = [CodingUserInfoKey : Any]()
var columnNames = ["id", "username", "age"]
public func container<Key>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> {
return KeyedDecodingContainer(RowKeyedDecodingContainer<Key>(decoder: self))
}
public func unkeyedContainer() throws -> UnkeyedDecodingContainer {
throw "UnkeyedDecodingContainer not implemented"
}
public func singleValueContainer() throws -> SingleValueDecodingContainer {
throw "SingleValueDecodingContainer not implemented"
}
}
RowKeyedDecodingContainer decodes keys to values. User(from: decoder)
calls decode for each attribute. Optional attributes call contains()
before calling decode()
. Functions for nesting other container are not required just throw an error.
struct RowKeyedDecodingContainer<K : CodingKey> : KeyedDecodingContainerProtocol {
typealias Key = K
let decoder : RowDecoder
let allKeys = [Key]()
let codingPath = [CodingKey]()
func decodeNil(forKey key: K) throws -> Bool {
return false
}
func contains(_ key: K) -> Bool {
return decoder.columnNames.contains(key.stringValue)
}
func decode(_ type: Bool.Type, forKey key: K) throws -> Bool {
return false
}
func decode(_ type: Int.Type, forKey key: K) throws -> Int {
return 123
}
func decode(_ type: Int8.Type, forKey key: K) throws -> Int8 {
return 123
}
func decode(_ type: Int16.Type, forKey key: K) throws -> Int16 {
return 123
}
func decode(_ type: Int32.Type, forKey key: K) throws -> Int32 {
return 123
}
func decode(_ type: Int64.Type, forKey key: K) throws -> Int64 {
return 123
}
func decode(_ type: UInt.Type, forKey key: K) throws -> UInt {
return 123
}
func decode(_ type: UInt8.Type, forKey key: K) throws -> UInt8 {
return 123
}
func decode(_ type: UInt16.Type, forKey key: K) throws -> UInt16 {
return 123
}
func decode(_ type: UInt32.Type, forKey key: K) throws -> UInt32 {
return 123
}
func decode(_ type: UInt64.Type, forKey key: K) throws -> UInt64 {
return 123
}
func decode(_ type: Float.Type, forKey key: K) throws -> Float {
return 123
}
func decode(_ type: Double.Type, forKey key: K) throws -> Double {
return 123
}
func decode(_ type: String.Type, forKey key: K) throws -> String {
if !self.contains(key) {
throw "Key '\(key.stringValue)' not in columnNames"
}
return "some string"
}
func decode<T>(_ type: T.Type, forKey key: K) throws -> T where T : Decodable {
throw "decode<T> not implemented"
}
func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: K) throws -> KeyedDecodingContainer<NestedKey> {
throw "nestedContainer not implemented"
}
func nestedUnkeyedContainer(forKey key: K) throws -> UnkeyedDecodingContainer {
throw "nestedUnkeyedContainer not implemented"
}
func superDecoder() throws -> Decoder {
throw "superDecoder not implemented"
}
func superDecoder(forKey key: K) throws -> Decoder {
throw "superDecoder forKey not implemented"
}
}
I am not familiar with this library and it probably would take a few days work to integrate. Could try to implement it or hint me in the right direction?
Thanks!
Thanks. I will check it soon.
@novi Any news?
Edit: I have implemented a first version. PR will follow tomorrow
@patrick-zippenfenig Sorry for late. I'm still trying to design for the Codable API support.
Thanks for PR! I will check that.
The implementation in #64 causes an issue in my code. My model types conform to both QueryRowResultType
and Decodable
. The compiler does not know which query
method to use: Ambiguous use of 'query'
.