Decodable and CGPoint

Hi All,


I am having trouble decoding JSON into any of these structures. Am I missing something?


The JSON format I am currently using to test looks like the following ...


{

     "center": {

         "x": 129,

         "y": 94

     },

     "radius": 73

}


I would assume that the automatic Decodable would understand that center is a CGPoint especially when declared as such in ...


struct Circle {

     var center: CGPoint

     var radius: CGFloat

     ... other code here

}


I have tried automatically as well as manually with the standard let values = try decoder.container(keyedBy: CodingKeys.self) etc.


If I set center to CGPoint.zero in the init(from decoder: Decoder) then all works perfectly so it is obviously the way I am formatting the JSON data.


Any help would be most appreciated.


Thank you in advance,

DSC.david

Answered by QuinceyMorris in 282074022

I tried this code in a playground:


var circle = Circle (center: CGPoint (x: 129, y: 94), radius: 73)
let encoder = JSONEncoder ()
let encodedData = try! encoder.encode (circle)
let encodedString = String (bytes: encodedData, encoding: .utf8)!
print (encodedString)


and the output was:


{"center":[129,94],"radius":73}


So it looks like CGPoint has a custom implementation of Codable that represents the coordinates as a vector rather than a struct. I'm not sure what I think about that, since it's not documented anywhere. I did find this, though:


medium.com/swiftly-swift/little-xcode-beta-surprises-core-graphics-codable-conformance-c7ff485c68d2

Accepted Answer

I tried this code in a playground:


var circle = Circle (center: CGPoint (x: 129, y: 94), radius: 73)
let encoder = JSONEncoder ()
let encodedData = try! encoder.encode (circle)
let encodedString = String (bytes: encodedData, encoding: .utf8)!
print (encodedString)


and the output was:


{"center":[129,94],"radius":73}


So it looks like CGPoint has a custom implementation of Codable that represents the coordinates as a vector rather than a struct. I'm not sure what I think about that, since it's not documented anywhere. I did find this, though:


medium.com/swiftly-swift/little-xcode-beta-surprises-core-graphics-codable-conformance-c7ff485c68d2

@QuinceyMorris: Thank you for that ... I began to suspect that this was the case after more experimentation.


The next part of the question would be how to get that vector from a JSON reply.


Currently, I am using PHP to query a database, generating the result array, then using json_encode to create a JSON string. The json_encode creates the JSON style object for the CGPoint ... any ideas on how to create the vector style?


Or am I going to have to manually create the JSON string rather than rely on standardised implementation across all of my REST API?


Thank you in advance,

DSC.david

It depends a bit on the scale of the problem.


One solution would be just to write the manual decoding for types like Circle that contain a CGPoint. The synthesized code is almost trivial to re-create manually, so it's just a question of source code size.


Or, you could try to finagle the types like Circle like this:


struct MyPointStruct: Decodable {
     let x: CGFloat
     let y: CGFloat
}
struct Circle: Decodable {
     private var _center: MyPointStruct
     var center: CGPoint {
          get { return CGPoint (x: _center.x, y: _center.y) }
          set { _center = MyPointStruct (x: newValue.x, y: newValue.y) }
     }
}


Or perhaps slightly more cleanly, if you have lot of point-containing structs:


struct MyPointStruct: Decodable {
     var x: CGFloat
     var y: CGFloat
     var point: CGPoint {
          get { return CGPoint (x: x, y: y) }
          set { x = newValue.x; y = newValue.y }
     }
}
struct Circle: Decodable {
     private var _center: MyPointStruct
     var center: CGPoint {
          get { return _center.point }
          set { _center.point = newValue }
     }
}


That way, you don't have to do anything at the PHP end, or write any decoding code.

Oh, I forgot: you'd also have to declare the coding keys explicitly to get the JSON dictionary key to be "center" rather than "_center" (or whatever you choose as the private name).

I was trying to keep it all nice and "clean" so that I could avoid creating my own structs if possible.

I solved the problem by writing a decoder for the situation and left it at that.

Thank you very much for your help.

DSC.david

Decodable and CGPoint
 
 
Q