-3

I get a large JSON document from a service with a deeply nested hierarchy containing lots of information (typically 5MB-10MB). I've already defined the classes and structures that are encoded in this JSON document in the service backend.

What's the advantage of redefining all of these structures in Swift so that they are Codable/Encodable in order to use with the JSONSerializer?

In other words, what's the advantage of treating the objects in my JSON structure as first-class Swift classes instead of just treating the entire JSON document as a dictionary?

HangarRash
  • 7,314
  • 5
  • 5
  • 32
Carpetfizz
  • 8,707
  • 22
  • 85
  • 146
  • 3
    The primary advantages are type safety, ease of property access and not having to unwrap *everything* – Paulw11 Dec 25 '22 at 20:31
  • Thanks @Paulw11, what do you mean by "not having to unwrap *everything*"? – Carpetfizz Dec 25 '22 at 20:46
  • 1
    Because `jsonObject(with:options:)` returns `Any` and if you have no predefine types you would need to cast the content as `[Any]` or `[String:Any]` until you get down to the basic types where you do the last cast. – Joakim Danielson Dec 25 '22 at 20:55
  • 2
    If your struct have "meaning", you can add them functions, computed properties, etc. It's also more readable to have a param in a func which is a "MyModel" rather than a `Dict`, where you'll have to check all its values: Is it a String, an Int, etc each time you want to use it. – Larme Dec 25 '22 at 21:02
  • 2
    https://stackoverflow.com/questions/46873542/swift-struct-or-dictionary https://stackoverflow.com/questions/29552399/should-a-dictionary-be-converted-to-a-class-or-struct-in-swift etc. – Larme Dec 25 '22 at 21:03
  • 1
    @Carpetfizz `someDictionary["someProperty"]` will return an optional. `someStruct.someProperty` is only an optional if `someProperty` is declared as optional. Another advantage is that JSONDecoder will throw if the input json isn't valid/doesn't match you structs – Paulw11 Dec 25 '22 at 21:15

1 Answers1

1

Swift, as a strongly-typed language, gives you a lot of tools in as part of its type system that make development safer and easier — by using full-fledged Swift types, you can make use of those tools to both improve the development experience, and make your code more performant:

  • A dictionary is an un-typed, general-purpose data structure. This means that:

    1. The compiler cannot prevent you from making typos when writing out key names, whereas code which refers to a property on a type which does not exist will not compile
    2. The compiler cannot auto-complete key names for dictionaries like it can for properties on a known type
    3. Accessing a value inside of a dictionary requires looking up the value, which incurs a performance cost; for properties on a defined type, however, the compiler can generate code which accesses those properties directly
    4. Accessing a value inside of a [String: Any] yields an Optional value of an unknown type:
      • If you know that the value is supposed to be there, the Optional is added overhead, mentally and performance-wise
      • If you know what type the value is supposed to be, casting it to the type you expect it to be can also be error-prone, and also represents some mental and performance overhead
    5. Using a dictionary as the primary data store for your data means that every access incurs all of these drawbacks
  • Using JSONDecoder (or similar) with a well-defined type gives you the opportunity to validate the incoming data in advance, in one localized spot that may be able to handle failures, or invalid data. It gives you the opportunity to both assert that the individual values are of the right types that you expect (and that they are not missing), but also that the entirety of the data is semantically-meaningful; without performing this validation, you risk either passing invalid data along to a different part of your application (where, combined with all of the above, means that it's very easy to accidentally access the data incorrectly), or, discovering that the data is invalid elsewhere, but not being able to handle the failure from that spot, leading to a very generic error

    • This pass also only happens once up-front, meaning that further access to the data incurs no performance cost

In all, except for extraordinarily trivial use-cases, it's highly recommended to use fully fledged types, instead of leaving the untyped data inside of a dictionary.

Itai Ferber
  • 28,308
  • 5
  • 77
  • 83