Search code examples
jsonswifttype-erasure

JSON Object in Swift


For the purpose of my app I'm using JSON objects that are defined like:

typealias JSONObject = [String:JSONValue]

Where JSON Value is a protocol to which possible types are conforming to:

protocol JSONValue {}
extension String:      JSONValue {}
extension Int:         JSONValue {}
extension Double:      JSONValue {}
extension Bool:        JSONValue {}
extension NSNull:      JSONValue {}
extension JSONObject:  JSONValue {}

(I prefer using this way instead of creating a JSONValue enum with .string(value), .int(value) etc... My code is private and no-one will be adding other extensions)

To handle arrays, I've added:

extension [JSONValue]: JSONValue {}

So that any JSONValue can be a primitive, an array, or a dictionary (JSONObject)

Everything is working fine. I'm just having an issue with arrays. See this example :

var myObject = JSONObject()
myObject["primitive"] = "OK"
myObject["array"] = ["OK"]

let myObjectAsJSONValue: JSONValue = myObject // OK


var myArray = [JSONObject]()
myArray.append(myObject)

let myArrayAsJSONValue: JSONValue = myArray // Failing (Protocol 'JSONValue' requires the types 'JSONObject' (aka 'Dictionary<String, any JSONValue>') and 'any JSONValue' be equivalent)

I don't understand the error I'm getting since JSONObject conforms to JSONValue (so can be handled as "any JSONValue")

Any help??

Thanks!

Edit: Fixed my issue by replacing:

extension [JSONValue]: JSONValue {}

With:

extension Array : JSONValue where Element : JSONValue {}

I don't really understand the difference but it seems to work...


Solution

  • As stated in the edit, you should write the array extension like this:

    extension Array : JSONValue where Element : JSONValue {}
    

    This is different from extension [JSONValue] ... in that extension [JSONValue] declares an extension on only one type - Array<any JSONValue>.

    So if you just declare myArray as:

    var myArray = [any JSONValue]()
    

    there will be no error.

    The myArray in your code has type Array<JSONObject>. This is a different type from Array<any JSONValue>, and therefore extension [JSONValue] does not apply to it - it does not conform to JSONValue.

    If the extension is extension Array : JSONValue where Element : JSONValue, then the extension applies to all Array types, where the Element type conforms to JSONValue. Since JSONObject conforms to JSONValue, Array<JSONObject> does too.