I'm trying to write an extension to satisfy a protocol in an extension like so:
extension UIColor: JSONRepresentable {
convenience init?(json: Any) {
guard let colourArray = json as? [CGFloat] else {
print("json was not an array of CGFloats")
return nil
}
self.init(
red: colourArray[0],
green: colourArray[1],
blue: colourArray[2],
alpha: colourArray[3]
)
}
}
I'm getting this error:
Initializer requirement 'init(json:)' can only be satisfied by a required initializer in the definition of non-final class 'UIColor'.
If I add a required
keyword, I get this error
'required' initializer must be declared directly in class 'UIColor' (not in an extension).
Is there a reason for this or any way to work around it?
Edit: Just to be clear, here's the protocol
protocol JSONRepresentable {
init?(json: Any)
}
struct Color: Codable {
let red, green, blue, alpha: CGFloat
}
extension Color {
var uiColor: UIColor { return UIColor(color: self) }
var cgColor: CGColor { return uiColor.cgColor }
var ciColor: CIColor { return CIColor(color: uiColor) }
var data: Data { return try! JSONEncoder().encode(self) }
}
extension UIColor {
convenience init(color: Color) {
self.init(red: color.red, green: color.green, blue: color.blue, alpha: color.alpha)
}
var color: Color {
let color = CIColor(color: self)
return Color(red: color.red, green: color.green, blue: color.blue, alpha: color.alpha)
}
}
extension Data {
var string: String {
return String(data: self, encoding: .utf8) ?? ""
}
}
Playground testing
let json = """
{"red": 0.5, "green": 0.0, "blue": 0.0, "alpha": 1.0}
"""
if let color = try? JSONDecoder().decode(Color.self, from: Data(json.utf8)) {
print(color) // "Color(red: 0.5, green: 0.0, blue: 0.0, alpha: 1.0)\n"
print(color.uiColor) // "UIExtendedSRGBColorSpace 0.5 0 0 1\n
print(color.data) // "40 bytes\n"
print(color.data.string) // "{"red":0.5,"alpha":1,"blue":0,"green":0}\n"
}
let redColor = UIColor.red.color
let jsonData = redColor.data.string // "{"red":1,"alpha":1,"blue":0,"green":0}"
If you need to work with your array of CGFloats you can override JSON Encoder and Decoder initializers:
extension Color {
public init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
red = try container.decode(CGFloat.self)
green = try container.decode(CGFloat.self)
blue = try container.decode(CGFloat.self)
alpha = try container.decode(CGFloat.self)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
try container.encode(red)
try container.encode(green)
try container.encode(blue)
try container.encode(alpha)
}
}
Testing
let values: [CGFloat] = [0.5,0.0,0.0,1.0]
let jsonData = try JSONSerialization.data(withJSONObject: values) // 11 bytes
let json = jsonData.string // "[0.5,0,0,1]"
do {
let color = try JSONDecoder().decode(Color.self, from: jsonData)
print(color) // "Color(red: 0.5, green: 0.0, blue: 0.0, alpha: 1.0)\n"
print(color.uiColor) // "UIExtendedSRGBColorSpace 0.5 0 0 1\n
print(color.data) // "11 bytes\n"
print(color.data.string) // "[0.5,0,0,1]\n"
let encodedData = try JSONEncoder().encode(color) // 11 bytes
print(encodedData == jsonData) // true
} catch {
print(error)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With