Does anyone have an example of using an Actor as their data type for their custom Encoder?
A TaskGroup to parallelize the encoding?
If so I'd love to see it. I'm not sure it should be even something to try.
Does anyone have an example of using an Actor as their data type for their custom Encoder?
A TaskGroup to parallelize the encoding?
If so I'd love to see it. I'm not sure it should be even something to try.
All Codable related APIs are synchronous, so no you won't be able to use actors or other async/await together with them.
It could be interesting to have an async compatible version of those but we don't have that.
Thank you. That's what it looked like, but I was curious if someone who was better than me with both of those things had found a way! Swift 7.0/Codeable as Macro version maybe. :)
Okay what if one did something like the below... would it make any difference at all to try?
(I'm very hand-wavey around starting the TaskGroup because I haven't worked with them yet)
(Also FWIW - I'd rather be using pkl, it looks GREAT!)
struct SimpleCoder {
let encodedDataStore = SimpleCoderData([:])
public func encode(_ values: [Encodable]) async throws -> String {
let encoder = _SimpleEncoder(data:encodedDataStore)
//<HANDWAVEY>
let result = TaskGroup {
for value in values {
task.append { try value.encode(to: encoder) }
}
}
if result != Error {
return await encodedDataStore.value()
}
//</HANDWAVEY>
}
}
actor SimpleCoderData {
var storage:[String: String]
init(_ value: [String: String]) {
self.storage = value
}
func insert(encodedKey: String, encodedValue:String) {
storage[encodedKey] = encodedValue
}
func value() -> String {
var lines = storage.map { key, value in
if key.isEmpty {
return "\(value)"
} else {
return "\(key):\(value)"
}
}
lines.sort()
return lines.joined(separator: "/")
}
}
struct _SimpleEncoder {
var data:SimpleCoderData
var codingPath: [CodingKey] = []
}
extension _SimpleEncoder {
func encodeKey(_ key:CodingKey) -> String {
(codingPath + [key]).map { $0.stringValue }.joined(separator: ".")
}
//called from the containers, mostly with just with encode("\(value)", atKey:Key)
func encode(_ value: String, forKey key:CodingKey) {
//TODO: is this the best way?
Task { await data.insert(encodedKey:encodeKey(key), encodedValue:value) }
}
}
extension _SimpleEncoder:Encoder {
var userInfo: [CodingUserInfoKey : Any] { [:] }
func container<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> where Key : CodingKey {
KeyedEncodingContainer(SimpleEncoderKEC<Key>(encoder: self))
}
//NOTE: Both UEC and SVEC invent a key for their values.
func unkeyedContainer() -> UnkeyedEncodingContainer {
SimpleCoderUEC(encoder: self)
}
func singleValueContainer() -> SingleValueEncodingContainer {
SimpleCoderSVEC(encoder: self)
}
}
ETA: For future self or others if going down this route check out Package Manager's PlainText encoder because it punts it straight into a stream. For some async needs this would be better than an Actor?