Learn two techniques in Codable
{"name": "iOS developer", "status": "open"}
struct Job: Codable {
enum Status: String, Codable {
case open
case close
}
let name: String
let status: Status
}
{
"name": "XXX",
"image": "data:image/png;base64,iVBORw0KGgo......."
}
struct Base64ImageWrapper: Codable {
let image: UIImage
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let base64String = try container.decode(String.self)
let components = base64String.split(separator: ",")
if components.count != 2 {
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Wrong data format")
}
let dataString = String(components[1])
if let dataDecoded = Data(base64Encoded: dataString, options: .ignoreUnknownCharacters),
let image = UIImage(data: dataDecoded) {
self.image = image
} else {
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Can't initialize image from data string")
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
let data = image.jpegData(compressionQuality: 1)
let prefix = "data:image/jpeg;base64,"
guard let base64String = data?.base64EncodedString(options: .lineLength64Characters) else {
throw EncodingError.invalidValue(image, EncodingError.Context(codingPath: [], debugDescription: "Can't encode image to base64 string."))
}
try container.encode(prefix + base64String)
}
}
let values = try decoder.container(keyedBy: CodingKeys.self)
let container = try decoder.singleValueContainer()
struct ModelUsingImageWrapper: Codable {
let name: String
let image: Base64ImageWrapper
}
extension KeyedEncodingContainer {
mutating func encode(_ value: UIImage,
forKey key: KeyedEncodingContainer.Key) throws {
let imageData = value.jpegData(compressionQuality: 1)
let prefix = "data:image/jpeg;base64,"
guard let base64String = imageData?.base64EncodedString(options: .lineLength64Characters) else {
throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: codingPath, debugDescription: "Can't encode image to base64 string."))
}
try encode(prefix + base64String, forKey: key)
}
}
extension KeyedDecodingContainer {
public func decode(_ type: UIImage.Type, forKey key: KeyedDecodingContainer.Key) throws -> UIImage {
let base64String = try decode(String.self, forKey: key)
// data:image/svg+xml;base64,PD.....
let components = base64String.split(separator: ",")
if components.count != 2 {
throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath, debugDescription: "Unsupported format"))
}
let dataString = String(components[1])
if let dataDecoded = Data(base64Encoded: dataString, options: .ignoreUnknownCharacters), let image = UIImage(data: dataDecoded) {
return image
} else {
throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath, debugDescription: "Unsupported format"))
}
}
}
struct ModelUsingKeyedEncodingContainer: Codable {
let name: String
let image: UIImage enum CodingKeys: String, CodingKey {
case name
case image
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decode(String.self, forKey: .name)
image = try container.decode(UIImage.self, forKey: .image)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(name, forKey: .name)
try container.encode(image, forKey: .image)
}
}
From us to your inbox weekly.