{"id":6424,"date":"2016-09-26T15:53:52","date_gmt":"2016-09-26T15:53:52","guid":{"rendered":"https:\/\/ushipblogsubd.wpengine.com\/?p=6424"},"modified":"2025-09-03T15:59:28","modified_gmt":"2025-09-03T15:59:28","slug":"streamlined-manual-json-parsing-swift","status":"publish","type":"post","link":"https:\/\/ushipblogsubd.wpengine.com\/shipping-code\/streamlined-manual-json-parsing-swift\/","title":{"rendered":"Streamlined, Manual JSON Parsing in Swift"},"content":{"rendered":"<p>There&#8217;s plenty of articles out there\u00a0about how to parse JSON in Swift, what libraries to use. \u00a0Apple even posted one\u00a0themselves as I was finishing up this post. \u00a0What I&#8217;d like to talk about here is\u00a0the way the uShip iOS app handles JSON parsing, which is a variation of Apple&#8217;s approach.<\/p>\n<h2>Sharing maintainable Swift code is a Challenge<\/h2>\n<p>Since its introduction just over two years ago, Apple\u2019s Swift programming language has shown itself to be a language that can be really fun to work with. \u00a0\u00a0uShip\u2019s iOS team chose to adopt the language very early on and in the process has had to learn to deal with some interesting challenges as a result.<\/p>\n<p>One particular challenge comes with adopting 3rd party libraries. \u00a0If you\u2019re working with Swift, one thing to consider in adopting a library is how frequently such libraries are kept up to date. \u00a0It\u2019s important to consider how bad it may be if these libraries stop compiling when you have to move on to a new version of XCode. \u00a0And unlike some other development environments, it is extremely challenging to produce and distribute reusable pre-compiled libraries. \u00a0Instead it is common for shared code modules to be distributed as source code for others to compile themselves. \u00a0Even if you use cocoapods, you\u2019re still having to compile the pods yourself. \u00a0Another dependency management system, Carthage, claims to support shared binary libraries, but until very recently this was only true if you were sharing the binaries with yourself on a single machine. \u00a0This all becomes a pretty big issue when the actual source code language is changing drastically over time.<\/p>\n<p>With all this in mind, sometimes it can be a big timesaver to replace a third-party library we\u2019re using with simpler, in-house code that we can easily maintain ourselves. \u00a0One place we\u2019ve chosen to do this is in parsing JSON.<\/p>\n<h2>The iOS SDK doesn\u2019t parse JSON for you<\/h2>\n<p>One thing that may surprise developers from other languages is that there\u2019s no built-in way to instantly parse JSON objects into Data Transfer Objects (strongly typed data structures). \u00a0There ARE some third party libraries out there for doing this: \u00a0SwiftyJSON, and dankogai\u2019s swift-json, among others. \u00a0But as mentioned above, if you\u2019re trying to avoid depending on 3rd party code, it\u2019s worth considering doing it yourself.<\/p>\n<p>As it turns out JSON parsing is not such a bad candidate for a more manual approach. \u00a0Throughout the rest of this article, I\u2019ll be sharing with you the technique we use in the uShip app for taking raw JSON data and converting it into strongly typed, Swift structs. \u00a0These structs clearly expose the structure and meaning of data from a given endpoint. \u00a0They are also easily composable and reusable with other similar endpoints, and can even help you build in sensible default values for missing values in a structure.<br \/>\nHow we set up our Swift JSON parsing<br \/>\nLet&#8217;s step through the approach the uShip app is currently using.<\/p>\n<h2>Create A DTO Protocol<\/h2>\n<p>The first major piece of the puzzle was in creating a special protocol for all of our DTOs (Data Transfer Objects) to adopt, which simply specifies that these DTOs must have a constructor that allows them to be built with one of the basic JSON data types (Dictionary, Array, String, Number or Bool). \u00a0At uShip all of our APIs provide NSDictionary or NSArray objects, so we\u2019ll focus on those.<br \/>\npublic enum JSONType {<\/p>\n<p>case array(array:[AnyObject])<br \/>\ncase dictionary(dictionary:[String:AnyObject])<\/p>\n<p>\/\/&#8230;<\/p>\n<p>public static func create(jsonObject: AnyObject?) -&amp;gt; JSONType {<\/p>\n<p>\/\/&#8230;<\/p>\n<p>}<br \/>\n}<\/p>\n<p>public protocol JSONObjectConvertable {<br \/>\ninit?(JSON: JSONType?)<br \/>\n}<br \/>\nCreate\u00a0Collection Extensions<br \/>\nThe second part was in creating a set of extensions on the Swift Dictionary and Array types. \u00a0These extensions have a set of functions which accept a key value or index into the collection object and return the strongly-typed value associated with that key or index. \u00a0Through the power of Swift Generics, we\u2019re able to give all of these functions the same exact name. \u00a0Because of this, all you need to know in order to use these functions is that one function name. \u00a0We also created one additional override of this function which could grab the collection value and return it as a type conforming to our JSON data protocol (described in the last paragraph). \u00a0For example:<br \/>\npublic extension Dictionary where Key: ExpressibleByStringLiteral, Value: AnyObject<br \/>\n{<br \/>\n\/\/looks for a value with the given key<br \/>\n\/\/if the value exists with the expected type, returns the value as that type<br \/>\n\/\/returns nil otherwise<br \/>\npublic func jsonValue&amp;lt;T&amp;gt;(_ key: String) -&amp;gt; T?<br \/>\n{<br \/>\nreturn (self[key as! Key] as? T)<br \/>\n}<\/p>\n<p>\/\/\u2026<\/p>\n<p>}<br \/>\nIf you\u2019re unused to Swift generics that code may be a little difficult to wrap your head around. The function \u00a0jsonValue&amp;lt;T&amp;gt;(key:) works based on what you assign its return value to. \u00a0If you assign the result to a string, it will return the dictionary value if it happens to actually be a string. \u00a0If it isn\u2019t a string, it will return nil. \u00a0If you instead assign it to an NSNumber, it will only return the value if it is actually an NSNumber. \u00a0\u00a0If we want to pull out values of a non-object, primitive type like Float or UInt, we need more specialized overrides of this function.<br \/>\npublic func jsonValue(_ key: String) -&amp;gt; Float?<br \/>\n{<br \/>\nreturn (self[key as! Key] as? NSNumber)?.floatValue<br \/>\n}<\/p>\n<p>public func jsonValue(_ key: String) -&amp;gt; UInt?<br \/>\n{<br \/>\nreturn (self[key as! Key] as? NSNumber)?.uintValue<br \/>\n}<br \/>\nCreate DTO Types<br \/>\nFinally we combine the first two pieces within a concrete DTO designed to mirror the expected contents of a JSON object. \u00a0We do this by creating a struct, having it conform to our DTO Protocol, and then in the protocol-required constructor, we use our collection extensions to parse whatever data is passed in by the arguments.<br \/>\nstruct User : JSONObjectConvertable {<\/p>\n<p>var id : UInt?<br \/>\nvar username : String?<\/p>\n<p>init?(JSON: JSONObject?) {<\/p>\n<p>guard let json = json else { return nil }<br \/>\nguard case .dictionary(let dictionary) = JSON else { return nil }<\/p>\n<p>id = dictionary.jsonValue(\u201cid\u201d)<br \/>\nusername = dictionary.jsonValue(\u201cusername\u201d)<\/p>\n<p>}<br \/>\n}<br \/>\n&amp;nbsp;<\/p>\n<p>This shows everything really coming together. \u00a0Our User DTO adopts the custom JSONObjectConvertable protocol, so it must have the two required initializers. \u00a0In the second initializer, we ensure that the JSON object we build from is of the expected dictionary type. \u00a0And finally we populate the \u201cid\u201d and \u201cusername\u201d properties with our jsonValue extension function. \u00a0Each call to jsonValue calls a different version of the function because we have one version that handles String optionals and one that handles UInt optionals.<br \/>\nUse the DTOs<br \/>\nOnce this DTO is set up, we can put it to use wherever we get data back from a JSON document. \u00a0Below we get data from an API endpoint and parse it using one of our DTO types:<br \/>\nlet JSONObject = try? JSONSerialization.jsonObject(with: data, options:[])<br \/>\nlet JSON = JSONType.create(jsonObject: JSONObject)<br \/>\nThat\u2019s it. \u00a0And after you set up one endpoint, adding DTOs for more endpoints becomes increasingly easier. \u00a0Most of the work is done by the collection extensions, which are reusable. \u00a0Our actual networking code is a bit more complex than that, but explaining that is outside the scope of this particular article.<\/p>\n<h2>For more details on the code, check out our sample project, which uses this technique to parse JSON Data from NASA\u2019s API for downloading photos from the Curiosity Mars Rover.<\/h2>\n<p>And if you want to try running\u00a0the project for yourself, you might even see some cool martian landscapes!<\/p>\n<p>Or Mars rover selfies.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>There&#8217;s plenty of articles out there\u00a0about how to parse JSON in Swift, what libraries to use. \u00a0Apple even posted one\u00a0themselves as I was finishing up this post. \u00a0What I&#8217;d like to talk about here is\u00a0the way the uShip iOS app handles JSON parsing, which is a variation of Apple&#8217;s approach. Sharing maintainable Swift code is&#8230;<a class=\"read-more\" href=\"https:\/\/ushipblogsubd.wpengine.com\/shipping-code\/streamlined-manual-json-parsing-swift\/\"> Read More<\/a><\/p>\n","protected":false},"author":5,"featured_media":6455,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[295,2],"tags":[266,297,267],"class_list":["post-6424","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-shipping-code","category-company-news","tag-json","tag-shipping-code","tag-swift"],"acf":{"blog_post_content":null},"_links":{"self":[{"href":"https:\/\/ushipblogsubd.wpengine.com\/wp-json\/wp\/v2\/posts\/6424","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ushipblogsubd.wpengine.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ushipblogsubd.wpengine.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ushipblogsubd.wpengine.com\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/ushipblogsubd.wpengine.com\/wp-json\/wp\/v2\/comments?post=6424"}],"version-history":[{"count":0,"href":"https:\/\/ushipblogsubd.wpengine.com\/wp-json\/wp\/v2\/posts\/6424\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/ushipblogsubd.wpengine.com\/wp-json\/wp\/v2\/media\/6455"}],"wp:attachment":[{"href":"https:\/\/ushipblogsubd.wpengine.com\/wp-json\/wp\/v2\/media?parent=6424"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ushipblogsubd.wpengine.com\/wp-json\/wp\/v2\/categories?post=6424"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ushipblogsubd.wpengine.com\/wp-json\/wp\/v2\/tags?post=6424"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}