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.
1 Like
ktoso
(Konrad 'ktoso' Malawski 🐟🏴☠️)
2
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.
1 Like
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?