Skip to content

Instantly share code, notes, and snippets.

@huynguyencong
Last active September 27, 2022 06:08
Show Gist options
  • Save huynguyencong/11536ae933fcef2debc3a880854a7881 to your computer and use it in GitHub Desktop.
Save huynguyencong/11536ae933fcef2debc3a880854a7881 to your computer and use it in GitHub Desktop.
A way to implement transformer when using Codable
import Foundation
// Assume the server return `Int` number in a `String`. If it happen on all models, we can't customise all models with `init(decoder:)` and `encode(encoder:)`, because it will make the code a mess.
// Conform this protocol to implement transformation
protocol Transformer {
associatedtype Origin: Codable
associatedtype Target
static func decode(value: Origin) throws -> Target
static func encode(value: Target) throws -> Origin
}
// This struct help to connect Transformer and Codable
struct Transform<ATransformer: Transformer>: Codable {
enum TransformError: Error {
case codingFailed
}
var value: ATransformer.Target
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let originalValue = try container.decode(ATransformer.Origin.self)
self.value = try ATransformer.decode(value: originalValue)
}
func encode(to encoder: Encoder) throws {
let originalValue = try ATransformer.encode(value: value)
var container = encoder.singleValueContainer()
try container.encode(originalValue)
}
}
import Foundation
// This is how the model look like when using this way.
struct User: Codable {
var yearOld: Transform<IntFromString>?
}
// This transformer help to decode String to Int and vice versa
struct IntFromString: Transformer {
enum SelfError: Error {
case invalidResponse
}
static func decode(value: String) throws -> Int {
guard let intValue = Int(value) else {
throw SelfError.invalidResponse
}
return intValue
}
static func encode(value: Int) throws -> String {
return "\(value)"
}
}
let jsonString = """
{
"yearOld": "10"
}
"""
if let data = jsonString.data(using: .utf8) {
do {
let user = try JSONDecoder().decode(User.self, from: data)
print(user.yearOld?.value ?? -1)
} catch {
print(error)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment