Extract Piece of JSON using Swift Codable | Fission Labs
iOS

Extract Piece of JSON using Swift Codable

Codable protocol is really helpful in making developers’ life easy. It allows us to convert JSON response into the model object instead of going through the cumbersome process of parsing each object. How do we use the Codable protocol in Swift? To understand the concept better let’s get our hands dirty with some coding samples: Flickr search […]

Fission Labs • July 11, 2019

Codable protocol is really helpful in making developers’ life easy. It allows us to convert JSON response into the model object instead of going through the cumbersome process of parsing each object.

How do we use the Codable protocol in Swift?

To understand the concept better let’s get our hands dirty with some coding samples:

Flickr search JSON example:

{
 “title”: “Recent Uploads tagged searchtag”,
 “link”: “https:\/\/
www.flickr.com\/photos\/tags\/searchtag\/",
 “description”: “”,
 “modified”: “2014–05–06T05:45:56Z”,
 “generator”: “https:\/\/
www.flickr.com",
 “items”: [{
 “title”: “Bird \/ Pleasanton — CA”,
 “link”: “https:\/\/
www.flickr.com\/photos\/geziyozbiz\/14140039213\/",
 “media”: {
 “m”: “https:\/\/farm8.staticflickr.com\/7440\/14140039213_fe9c6fd0aa_m.jpg”
 },
 “date_taken”: “2014–05–04T11:48:29–08:00”,
 “description”: “ <p><a href=\”https:\/\/
www.flickr.com\/people\/geziyozbiz\/\">geziyozbiz<\/a> posted a photo:<\/p> <p><a href=\”https:\/\/www.flickr.com\/photos\/geziyozbiz\/14140039213\/\" title=\”Bird \/ Pleasanton — CA\”><img src=\”https:\/\/farm8.staticflickr.com\/7440\/14140039213_fe9c6fd0aa_m.jpg\” width=\”240\” height=\”160\” alt=\”Bird \/ Pleasanton — CA\” \/><\/a><\/p> “,
 “published”: “2014–05–06T05:45:56Z”,
 “author”: “
nobody@flickr.com (\”geziyozbiz\”)”,
 “author_id”: “122145083@N05”,
 “tags”: “sony birding birdwatching pleasanton searchtag a6000 55210mm e55210mmf4563oss”
 }]
}

Let’s go ahead and implement the codable protocol for the above JSON. Flickr feed response has a dictionary with a title, link, description, modified generator and a list of items.

struct FlickrSearch : Codable {
let title : String?
let link : String?
let description : String?
let modified : String?
let generator : String?
let items : [Items]?
enum CodingKeys: String, CodingKey {
case title = "title"
case link = "link"
case description = "description"
case modified = "modified"
case generator = "generator"
case items = "items"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
title = try values.decodeIfPresent(String.self, forKey: .title)
link = try values.decodeIfPresent(String.self, forKey: .link)
description = try values.decodeIfPresent(String.self, forKey: .description)
modified = try values.decodeIfPresent(String.self, forKey: .modified)
generator = try values.decodeIfPresent(String.self, forKey: .generator)
items = try values.decodeIfPresent([Items].self, forKey: .items)
}
}

Similarly, an item is a dictionary which has some properties and media dictionary as below:

struct Items : Codable {
let title : String?
let link : String?
let media : Media?
let date_taken : String?
let description : String?
let published : String?
let author : String?
let author_id : String?
let tags : String?
enum CodingKeys: String, CodingKey {
case title = "title"
case link = "link"
case media = "media"
case date_taken = "date_taken"
case description = "description"
case published = "published"
case author = "author"
case author_id = "author_id"
case tags = "tags"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
title = try values.decodeIfPresent(String.self, forKey: .title)
link = try values.decodeIfPresent(String.self, forKey: .link)
media = try values.decodeIfPresent(Media.self, forKey: .media)
date_taken = try values.decodeIfPresent(String.self, forKey: .date_taken)
description = try values.decodeIfPresent(String.self, forKey: .description)
published = try values.decodeIfPresent(String.self, forKey: .published)
author = try values.decodeIfPresent(String.self, forKey: .author)
author_id = try values.decodeIfPresent(String.self, forKey: .author_id)
tags = try values.decodeIfPresent(String.self, forKey: .tags)
}
}

Conversion of media dictionary to Swift media codable model object:

struct Media : Codable {
let m : String?
enum CodingKeys: String, CodingKey {
case m = "m"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
m = try values.decodeIfPresent(String.self, forKey: .m)
}
}

Base object FlickrSearch is a complete representation of JSON and we’ll use the base object to map JSON to Swift model:

let url = URL(string: "https://api.flickr.com/services/feeds/photos_public.gne?tags=%22%20+%20searchTag%20+%20%22&format=json&nojsoncallback=1")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in	
  let jsonDecoder = JSONDecoder()
  
let responseModel = try jsonDecoder.decode(FlickrSearch.self, from: data!)
}
task.resume()

As per the above example, we can write object mapping from JSON to Swift models and easily decode the complete JSON into the model object. What if we don’t need the complete object and require only a portion of it. Let’s say we only require the items array from the JSON response. Do you have an idea how one can achieve it?

How do we use the Codable protocol to extract part of JSON?

You might be thinking that if require only some part of JSON, then why not break the JSON into the pieces as per the requirement. Yes, it would be great if we could receive JSON responses into smaller parts. It will use a lower data bandwidth.

Above solution is possible only if you are using APIs that are in-house implemented. What if you are utilizing a third-party APIs?

Do we need to write complete JSON structure using Codable protocol?

No, we can achieve this without having a complete JSON object mapping. Write mapping object for Items and Media and convert mapping list of Item as below:

let url = URL(string: "https://api.flickr.com/services/feeds/photos_public.gne?tags=%22%20+%20searchTag%20+%20%22&format=json&nojsoncallback=1")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in	
  let jsonDecoder = JSONDecoder()
        
let dictionaryFromJSON = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! [String:Any]
        let jsonItem = dictionaryFromJSON["items"] as? NSArray
        let jsonData = try JSONSerialization.data(withJSONObject: jsonItem!, options: [])
        let responseModel = try jsonDecoder.decode([Items].self, from: jsonData!)

}
task.resume()

As we can see in the above code, first of all, we need to convert data into a JSON dictionary and extract items list as NSArray. Convert items list into data and then again decode data into Item’s list.

Originally published at https://medium.com/@coolanil.saini/extract-piece-of-json-using-swift-codable-protocol-fdfb6c767cfe