Encoding an Object as a String in Swift
For a project I’m working on I had a need for a Codable object which could be represented as a basic text string, and specifically not a JSON string. I was eventually able to find a solution so I thought I’d document it, partially to help anyone who has the same need but also invite anyone to correct me if there is a simpler way to accomplish this.
Let’s say you have this struct:
struct MyData {
let text: String
let number: Int
}
And you want to encode the data as a string in the following format: My text (12)
The Encodable part of it is simple, just implement the encode method and return the formatted string in a single-value container:
extension MyData: Encodable {
func encode(to encoder: any Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode("\(text) (\(number))")
}
}
Decodable is a bit more work, but also rather straightforward. I’ve chosen to implement a constructor that can initialise the struct from a source string—using regular expressions because I love problems—and then it’s just a manner of calling that initialiser from the decode initialiser, after having extracted the single-value container:
extension MyData: Decodable {
init(from string: String) throws {
let regex = #/^(.+?) \(([\d]+)\)$/#
let result = try regex.wholeMatch(in: string)!
self.text = String(result.1) // convert to an owned String
self.number = Int(result.2)!
}
init(from decoder: any Decoder) throws {
let container = try decoder.singleValueContainer()
let sourceString: String = try container.decode(String.self)
try self.init(from: sourceString)
}
}
Having this extra constructor isn’t necessary as all of it can be done in the decode constructor, but I felt it was good form. Note that this will crash if encountering a string that’s not formatted correctly, so putting in the correct safeguards is left as an exercise for the reader.