DataLireCoder swift package
This commit is contained in:
83
Sources/DataLiteCoder/Classes/DateDecoder.swift
Normal file
83
Sources/DataLiteCoder/Classes/DateDecoder.swift
Normal file
@@ -0,0 +1,83 @@
|
||||
import Foundation
|
||||
internal import DLCDecoder
|
||||
|
||||
extension RowDecoder {
|
||||
class DateDecoder: DLCDecoder.DateDecoder {
|
||||
typealias ValueDecoder = DLCDecoder.ValueDecoder
|
||||
typealias RowDecoder = DLCDecoder.RowDecoder
|
||||
|
||||
let strategy: DateDecodingStrategy
|
||||
|
||||
init(strategy: DateDecodingStrategy) {
|
||||
self.strategy = strategy
|
||||
}
|
||||
|
||||
func decode(
|
||||
from decoder: any ValueDecoder
|
||||
) throws -> Date {
|
||||
try decode(from: decoder) { decoder in
|
||||
try decoder.decode(Date.self)
|
||||
} _: { decoder in
|
||||
try decoder.decode(String.self)
|
||||
} _: { decoder in
|
||||
try decoder.decode(Int64.self)
|
||||
} _: { decoder in
|
||||
try decoder.decode(Double.self)
|
||||
}
|
||||
}
|
||||
|
||||
func decode(
|
||||
from decoder: any RowDecoder,
|
||||
for key: any CodingKey
|
||||
) throws -> Date {
|
||||
try decode(from: decoder) { decoder in
|
||||
try decoder.decode(Date.self, for: key)
|
||||
} _: { decoder in
|
||||
try decoder.decode(String.self, for: key)
|
||||
} _: { decoder in
|
||||
try decoder.decode(Int64.self, for: key)
|
||||
} _: { decoder in
|
||||
try decoder.decode(Double.self, for: key)
|
||||
}
|
||||
}
|
||||
|
||||
private func decode<D: Decoder>(
|
||||
from decoder: D,
|
||||
_ date: (D) throws -> Date,
|
||||
_ string: (D) throws -> String,
|
||||
_ int: (D) throws -> Int64,
|
||||
_ double: (D) throws -> Double
|
||||
) throws -> Date {
|
||||
switch strategy {
|
||||
case .deferredToDate:
|
||||
return try date(decoder)
|
||||
case .formatted(let dateFormatter):
|
||||
guard
|
||||
let date = dateFormatter.date(
|
||||
from: try string(decoder)
|
||||
)
|
||||
else {
|
||||
let info = "Date string does not match format expected by formatter."
|
||||
let context = DecodingError.Context(
|
||||
codingPath: decoder.codingPath,
|
||||
debugDescription: info
|
||||
)
|
||||
throw DecodingError.dataCorrupted(context)
|
||||
}
|
||||
return date
|
||||
case .millisecondsSince1970Int:
|
||||
let milliseconds = Double(try int(decoder))
|
||||
return Date(timeIntervalSince1970: milliseconds / 1000)
|
||||
case .millisecondsSince1970Double:
|
||||
let milliseconds = try double(decoder)
|
||||
return Date(timeIntervalSince1970: milliseconds / 1000)
|
||||
case .secondsSince1970Int:
|
||||
let seconds = Double(try int(decoder))
|
||||
return Date(timeIntervalSince1970: seconds)
|
||||
case .secondsSince1970Double:
|
||||
let seconds = try double(decoder)
|
||||
return Date(timeIntervalSince1970: seconds)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
51
Sources/DataLiteCoder/Classes/DateEncoder.swift
Normal file
51
Sources/DataLiteCoder/Classes/DateEncoder.swift
Normal file
@@ -0,0 +1,51 @@
|
||||
import Foundation
|
||||
import DataLiteCore
|
||||
|
||||
internal import DLCEncoder
|
||||
|
||||
extension RowEncoder {
|
||||
class DateEncoder: DLCEncoder.DateEncoder {
|
||||
typealias ValueEncoder = DLCEncoder.ValueEncoder
|
||||
typealias RowEncoder = DLCEncoder.RowEncoder
|
||||
|
||||
let strategy: DateEncodingStrategy
|
||||
|
||||
init(strategy: DateEncodingStrategy) {
|
||||
self.strategy = strategy
|
||||
}
|
||||
|
||||
func encode(
|
||||
_ date: Date,
|
||||
to encoder: any ValueEncoder
|
||||
) throws {
|
||||
let value = encodeValue(from: date)
|
||||
try encoder.encode(value)
|
||||
}
|
||||
|
||||
func encode(
|
||||
_ date: Date,
|
||||
for key: any CodingKey,
|
||||
to encoder: any RowEncoder
|
||||
) throws {
|
||||
let value = encodeValue(from: date)
|
||||
try encoder.encode(value, for: key)
|
||||
}
|
||||
|
||||
private func encodeValue(from date: Date) -> SQLiteRawBindable {
|
||||
switch strategy {
|
||||
case .deferredToDate:
|
||||
date
|
||||
case .formatted(let dateFormatter):
|
||||
dateFormatter.string(from: date)
|
||||
case .millisecondsSince1970Int:
|
||||
Int64(date.timeIntervalSince1970 * 1000)
|
||||
case .millisecondsSince1970Double:
|
||||
date.timeIntervalSince1970 * 1000
|
||||
case .secondsSince1970Int:
|
||||
Int64(date.timeIntervalSince1970)
|
||||
case .secondsSince1970Double:
|
||||
date.timeIntervalSince1970
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
253
Sources/DataLiteCoder/Classes/RowDecoder.swift
Normal file
253
Sources/DataLiteCoder/Classes/RowDecoder.swift
Normal file
@@ -0,0 +1,253 @@
|
||||
import Foundation
|
||||
import DataLiteCore
|
||||
|
||||
private import DLCDecoder
|
||||
|
||||
/// Decoder that decodes SQLite values into Swift types conforming to the `Decodable` protocol.
|
||||
///
|
||||
/// ## Overview
|
||||
///
|
||||
/// Use `RowDecoder` to convert `SQLiteRow` or an array of `SQLiteRow` into Swift types that conform
|
||||
/// to the `Decodable` protocol.
|
||||
///
|
||||
/// ### Decode a Single Row
|
||||
///
|
||||
/// Use ``decode(_:from:)->T`` to decode a single `SQLiteRow` into a `Decodable` value.
|
||||
///
|
||||
/// ```swift
|
||||
/// struct User: Decodable {
|
||||
/// var id: Int
|
||||
/// var name: String
|
||||
/// }
|
||||
///
|
||||
/// do {
|
||||
/// var row: SQLiteRow = .init()
|
||||
/// row["id"] = .int(1)
|
||||
/// row["name"] = .text("John Doe")
|
||||
///
|
||||
/// let decoder = RowDecoder()
|
||||
/// let user = try decoder.decode(User.self, from: row)
|
||||
/// } catch {
|
||||
/// print("Decoding error:", error)
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ### Decode a Row into an Array
|
||||
///
|
||||
/// Use ``decode(_:from:)->T`` to decode a row containing homogeneous values
|
||||
/// into an array of Swift values.
|
||||
///
|
||||
/// ```swift
|
||||
/// do {
|
||||
/// var row: SQLiteRow = .init()
|
||||
/// row["a"] = .int(10)
|
||||
/// row["b"] = .int(20)
|
||||
///
|
||||
/// let decoder = RowDecoder()
|
||||
/// let numbers = try decoder.decode([Int].self, from: row)
|
||||
/// } catch {
|
||||
/// print("Decoding error:", error)
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ### Decode Multiple Rows
|
||||
///
|
||||
/// Use ``decode(_:from:)->[T]`` to decode an array of `SQLiteRow` into an array of `Decodable` values.
|
||||
///
|
||||
/// ```swift
|
||||
/// struct User: Decodable {
|
||||
/// var id: Int
|
||||
/// var name: String
|
||||
/// }
|
||||
///
|
||||
/// do {
|
||||
/// let rows: [SQLiteRow] = fetchRows() // Fetch rows from a database
|
||||
/// let decoder = RowDecoder()
|
||||
/// let users = try decoder.decode([User].self, from: rows)
|
||||
/// } catch {
|
||||
/// print("Decoding error:", error)
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ### Customize Decoding with User Info
|
||||
///
|
||||
/// Use the ``userInfo`` property to pass context or flags to decoding logic.
|
||||
///
|
||||
/// First, define a custom key:
|
||||
///
|
||||
/// ```swift
|
||||
/// extension CodingUserInfoKey {
|
||||
/// static let isAdmin = CodingUserInfoKey(
|
||||
/// rawValue: "isAdmin"
|
||||
/// )!
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Then access it inside your model:
|
||||
///
|
||||
/// ```swift
|
||||
/// struct User: Decodable {
|
||||
/// enum CodingKeys: String, CodingKey {
|
||||
/// case id, name
|
||||
/// }
|
||||
///
|
||||
/// var id: Int
|
||||
/// var name: String
|
||||
/// var isAdmin: Bool
|
||||
///
|
||||
/// init(from decoder: Decoder) throws {
|
||||
/// let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
/// id = try container.decode(Int.self, forKey: .id)
|
||||
/// name = try container.decode(String.self, forKey: .name)
|
||||
///
|
||||
/// isAdmin = (decoder.userInfo[.isAdmin] as? Bool) ?? false
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Pass the value before decoding:
|
||||
///
|
||||
/// ```swift
|
||||
/// do {
|
||||
/// var row: SQLiteRow = .init()
|
||||
/// row["id"] = .int(1)
|
||||
/// row["name"] = .text("John Doe")
|
||||
///
|
||||
/// let decoder = RowDecoder()
|
||||
/// decoder.userInfo[.isAdmin] = true
|
||||
///
|
||||
/// let user = try decoder.decode(User.self, from: row)
|
||||
/// } catch {
|
||||
/// print("Decoding error:", error)
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ### Date Decoding Strategy
|
||||
///
|
||||
/// Use the ``dateDecodingStrategy`` property to customize how `Date` values are decoded.
|
||||
///
|
||||
/// ```swift
|
||||
/// struct Log: Decodable {
|
||||
/// let timestamp: Date
|
||||
/// }
|
||||
///
|
||||
/// do {
|
||||
/// var row: SQLiteRow = .init()
|
||||
/// row["timestamp"] = .int(1_700_000_000)
|
||||
///
|
||||
/// let decoder = RowDecoder()
|
||||
/// decoder.dateDecodingStrategy = .secondsSince1970Int
|
||||
///
|
||||
/// let log = try decoder.decode(Log.self, from: row)
|
||||
/// } catch {
|
||||
/// print("Decoding error:", error)
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// For more detailed information, see ``DateDecodingStrategy``.
|
||||
///
|
||||
/// ## Topics
|
||||
///
|
||||
/// ### Creating a Decoder
|
||||
///
|
||||
/// - ``init(userInfo:dateDecodingStrategy:)``
|
||||
///
|
||||
/// ### Decoding
|
||||
///
|
||||
/// - ``decode(_:from:)->T``
|
||||
/// - ``decode(_:from:)->[T]``
|
||||
///
|
||||
/// ### Customizing Decoding
|
||||
///
|
||||
/// - ``userInfo``
|
||||
///
|
||||
/// ### Decoding Dates
|
||||
///
|
||||
/// - ``dateDecodingStrategy``
|
||||
/// - ``DateDecodingStrategy``
|
||||
public final class RowDecoder {
|
||||
// MARK: - Properties
|
||||
|
||||
/// A dictionary containing user-defined information accessible during decoding.
|
||||
///
|
||||
/// This dictionary allows passing additional context or settings that can influence
|
||||
/// the decoding process. Values stored here are accessible inside custom `Decodable`
|
||||
/// implementations through the `Decoder`'s `userInfo` property.
|
||||
public var userInfo: [CodingUserInfoKey: Any]
|
||||
|
||||
/// The strategy used to decode `Date` values from SQLite data.
|
||||
///
|
||||
/// Determines how `Date` values are decoded from SQLite storage, supporting
|
||||
/// different formats such as timestamps or custom representations.
|
||||
public var dateDecodingStrategy: DateDecodingStrategy
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
/// Initializes a new `RowDecoder` instance with optional configuration.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - userInfo: A dictionary with user-defined information accessible during decoding.
|
||||
/// - dateDecodingStrategy: The strategy to decode `Date` values. Default is `.deferredToDate`.
|
||||
public init(
|
||||
userInfo: [CodingUserInfoKey: Any] = [:],
|
||||
dateDecodingStrategy: DateDecodingStrategy = .deferredToDate
|
||||
) {
|
||||
self.userInfo = userInfo
|
||||
self.dateDecodingStrategy = dateDecodingStrategy
|
||||
}
|
||||
|
||||
// MARK: - Decoding Methods
|
||||
|
||||
/// Decodes a single `SQLiteRow` into an instance of the specified `Decodable` type.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - type: The target type conforming to `Decodable`.
|
||||
/// - row: The SQLite row to decode.
|
||||
/// - Returns: An instance of the specified type decoded from the row.
|
||||
/// - Throws: An error if decoding fails.
|
||||
public func decode<T: Decodable>(
|
||||
_ type: T.Type,
|
||||
from row: SQLiteRow
|
||||
) throws -> T {
|
||||
let dateDecoder = DateDecoder(strategy: dateDecodingStrategy)
|
||||
let decoder = SingleRowDecoder(
|
||||
dateDecoder: dateDecoder,
|
||||
sqliteData: row,
|
||||
codingPath: [],
|
||||
userInfo: userInfo
|
||||
)
|
||||
return try T(from: decoder)
|
||||
}
|
||||
|
||||
/// Decodes an array of `SQLiteRow` values into an array of the specified `Decodable` type.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - type: The array type containing the element type to decode.
|
||||
/// - rows: The array of SQLite rows to decode.
|
||||
/// - Returns: An array of decoded instances.
|
||||
/// - Throws: An error if decoding any row fails.
|
||||
public func decode<T: Decodable>(
|
||||
_ type: [T].Type,
|
||||
from rows: [SQLiteRow]
|
||||
) throws -> [T] {
|
||||
let dateDecoder = DateDecoder(strategy: dateDecodingStrategy)
|
||||
let decoder = MultiRowDecoder(
|
||||
dateDecoder: dateDecoder,
|
||||
sqliteData: rows,
|
||||
codingPath: [],
|
||||
userInfo: userInfo
|
||||
)
|
||||
return try [T](from: decoder)
|
||||
}
|
||||
}
|
||||
|
||||
#if canImport(Combine)
|
||||
import Combine
|
||||
|
||||
// MARK: - TopLevelDecoder
|
||||
|
||||
extension RowDecoder: TopLevelDecoder {
|
||||
/// The type of input data expected by this decoder when used as a `TopLevelDecoder`.
|
||||
public typealias Input = SQLiteRow
|
||||
}
|
||||
#endif
|
||||
235
Sources/DataLiteCoder/Classes/RowEncoder.swift
Normal file
235
Sources/DataLiteCoder/Classes/RowEncoder.swift
Normal file
@@ -0,0 +1,235 @@
|
||||
import Foundation
|
||||
import DataLiteCore
|
||||
|
||||
private import DLCEncoder
|
||||
|
||||
/// Encoder that encodes instances of `Encodable` types into `SQLiteRow` or an array of `SQLiteRow`.
|
||||
///
|
||||
/// ## Overview
|
||||
///
|
||||
/// Use `RowEncoder` to convert `Encodable` objects into a single `SQLiteRow` or an array of `SQLiteRow`.
|
||||
///
|
||||
/// ### Encode a Single Object
|
||||
///
|
||||
/// Use ``encode(_:)->SQLiteRow`` to encode a single `Encodable` value into a `SQLiteRow`.
|
||||
///
|
||||
/// ```swift
|
||||
/// struct User: Encodable {
|
||||
/// let id: Int
|
||||
/// let name: String
|
||||
/// }
|
||||
///
|
||||
/// do {
|
||||
/// let user = User(id: 1, name: "John Doe")
|
||||
/// let encoder = RowEncoder()
|
||||
/// let row = try encoder.encode(user)
|
||||
/// } catch {
|
||||
/// print("Encoding error: ", error)
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ### Encode an Array of Objects
|
||||
///
|
||||
/// Use ``encode(_:)->[SQLiteRow]`` to encode an array of `Encodable` values into an array of `SQLiteRow`.
|
||||
///
|
||||
/// ```swift
|
||||
/// struct User: Encodable {
|
||||
/// let id: Int
|
||||
/// let name: String
|
||||
/// }
|
||||
///
|
||||
/// do {
|
||||
/// let users = [
|
||||
/// User(id: 1, name: "John Doe"),
|
||||
/// User(id: 2, name: "Jane Smith")
|
||||
/// ]
|
||||
/// let encoder = RowEncoder()
|
||||
/// let rows = try encoder.encode(users)
|
||||
/// } catch {
|
||||
/// print("Encoding error: ", error)
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ### Customize Encoding Behavior
|
||||
///
|
||||
/// Use the ``userInfo`` property to pass additional data that can affect encoding logic.
|
||||
///
|
||||
/// First, define a custom key:
|
||||
///
|
||||
/// ```swift
|
||||
/// extension CodingUserInfoKey {
|
||||
/// static let lowercased = CodingUserInfoKey(
|
||||
/// rawValue: "lowercased"
|
||||
/// )!
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Then pass a value using this key:
|
||||
///
|
||||
/// ```swift
|
||||
/// do {
|
||||
/// let user = User(id: 1, name: "John Doe")
|
||||
/// let encoder = RowEncoder()
|
||||
/// encoder.userInfo[.lowercased] = true
|
||||
///
|
||||
/// let row = try encoder.encode(user)
|
||||
/// } catch {
|
||||
/// print("Encoding error: ", error)
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Access this value inside your custom `encode(to:)` method:
|
||||
///
|
||||
/// ```swift
|
||||
/// struct User: Encodable {
|
||||
/// enum CodingKeys: CodingKey {
|
||||
/// case id, name
|
||||
/// }
|
||||
///
|
||||
/// let id: Int
|
||||
/// let name: String
|
||||
///
|
||||
/// func encode(to encoder: any Encoder) throws {
|
||||
/// var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
/// try container.encode(self.id, forKey: .id)
|
||||
///
|
||||
/// if (encoder.userInfo[.lowercased] as? Bool) ?? false {
|
||||
/// try container.encode(self.name.lowercased(), forKey: .name)
|
||||
/// } else {
|
||||
/// try container.encode(self.name.capitalized, forKey: .name)
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ### Date Encoding Strategy
|
||||
///
|
||||
/// Use the ``dateEncodingStrategy`` property to control how `Date` values are encoded into SQLite.
|
||||
///
|
||||
/// ```swift
|
||||
/// struct Log: Encodable {
|
||||
/// let timestamp: Date
|
||||
/// }
|
||||
///
|
||||
/// do {
|
||||
/// let encoder = RowEncoder(
|
||||
/// dateEncodingStrategy: .iso8601
|
||||
/// )
|
||||
///
|
||||
/// let log = Log(timestamp: Date())
|
||||
/// let row = try encoder.encode(log)
|
||||
/// } catch {
|
||||
/// print("Encoding error: ", error)
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// For more detailed information, see ``DateEncodingStrategy``.
|
||||
///
|
||||
/// ## Topics
|
||||
///
|
||||
/// ### Creating an Encoder
|
||||
///
|
||||
/// - ``init(userInfo:dateEncodingStrategy:)``
|
||||
///
|
||||
/// ### Encoding
|
||||
///
|
||||
/// - ``encode(_:)->SQLiteRow``
|
||||
/// - ``encode(_:)->[SQLiteRow]``
|
||||
///
|
||||
/// ### Customizing Encoding
|
||||
///
|
||||
/// - ``userInfo``
|
||||
///
|
||||
/// ### Encoding Dates
|
||||
///
|
||||
/// - ``dateEncodingStrategy``
|
||||
/// - ``DateEncodingStrategy``
|
||||
public final class RowEncoder {
|
||||
// MARK: - Properties
|
||||
|
||||
/// A dictionary you use to customize encoding behavior.
|
||||
///
|
||||
/// Use this dictionary to pass additional contextual information to the encoded type's `encode(to:)`
|
||||
/// implementation. You can define your own keys by extending `CodingUserInfoKey`.
|
||||
///
|
||||
/// This can be useful when encoding needs to consider external state, such as user roles,
|
||||
/// environment settings, or formatting preferences.
|
||||
public var userInfo: [CodingUserInfoKey: Any]
|
||||
|
||||
/// The strategy to use for encoding `Date` values.
|
||||
///
|
||||
/// Use this property to control how `Date` values are encoded into SQLite-compatible types.
|
||||
/// By default, the `.deferredToDate` strategy is used.
|
||||
///
|
||||
/// You can change the strategy to encode dates as timestamps, ISO 8601 strings,
|
||||
/// or using a custom formatter.
|
||||
///
|
||||
/// For details on available strategies, see ``DateEncodingStrategy``.
|
||||
public var dateEncodingStrategy: DateEncodingStrategy
|
||||
|
||||
// MARK: - Inits
|
||||
|
||||
/// Creates a new instance of `RowEncoder`.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - userInfo: A dictionary you can use to customize the encoding process by passing
|
||||
/// additional contextual information. The default value is an empty dictionary.
|
||||
/// - dateEncodingStrategy: The strategy to use for encoding `Date` values.
|
||||
/// The default value is `.deferredToDate`.
|
||||
public init(
|
||||
userInfo: [CodingUserInfoKey: Any] = [:],
|
||||
dateEncodingStrategy: DateEncodingStrategy = .deferredToDate
|
||||
) {
|
||||
self.userInfo = userInfo
|
||||
self.dateEncodingStrategy = dateEncodingStrategy
|
||||
}
|
||||
|
||||
// MARK: - Methods
|
||||
|
||||
/// Encodes a single value of a type that conforms to `Encodable` into a `SQLiteRow`.
|
||||
///
|
||||
/// - Parameter value: The value to encode.
|
||||
/// - Returns: A `SQLiteRow` containing the encoded data of the provided value.
|
||||
/// - Throws: An error if any value throws an error during encoding.
|
||||
public func encode<T: Encodable>(_ value: T) throws -> SQLiteRow {
|
||||
let dateEncoder = DateEncoder(
|
||||
strategy: dateEncodingStrategy
|
||||
)
|
||||
let encoder = SingleRowEncoder(
|
||||
dateEncoder: dateEncoder,
|
||||
codingPath: [],
|
||||
userInfo: userInfo
|
||||
)
|
||||
try value.encode(to: encoder)
|
||||
return encoder.sqliteData
|
||||
}
|
||||
|
||||
/// Encodes an array of values conforming to `Encodable` into an array of `SQLiteRow`.
|
||||
///
|
||||
/// - Parameter value: The array of values to encode.
|
||||
/// - Returns: An array of `SQLiteRow` objects containing the encoded data.
|
||||
/// - Throws: An error if any value throws an error during encoding.
|
||||
public func encode<T: Encodable>(_ value: [T]) throws -> [SQLiteRow] {
|
||||
let dateEncoder = DateEncoder(
|
||||
strategy: dateEncodingStrategy
|
||||
)
|
||||
let encoder = MultiRowEncoder(
|
||||
dateEncoder: dateEncoder,
|
||||
codingPath: [],
|
||||
userInfo: userInfo
|
||||
)
|
||||
try value.encode(to: encoder)
|
||||
return encoder.sqliteData
|
||||
}
|
||||
}
|
||||
|
||||
#if canImport(Combine)
|
||||
import Combine
|
||||
|
||||
// MARK: - TopLevelEncoder
|
||||
|
||||
extension RowEncoder: TopLevelEncoder {
|
||||
/// The output type produced by the encoder.
|
||||
public typealias Output = SQLiteRow
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user