Parsing JSON Using Decodable in Swift 4 became super easy - Swift 4, iOS 11 - Swift 4 Tutorials W3Schools

Hot

Post Top Ad

7 Aug 2017

Parsing JSON Using Decodable in Swift 4 became super easy - Swift 4, iOS 11

Parsing JSON in Swift 4 using Decodable Protocol. Parsing a complicate JSON is difficult because of redoing whole process again and again. In swift 4 it's not like swift 2,3, parsing became super easy and fast by just using single line of code. In this tutorial we are diving into Decodable protocol for JSON Parsing.

Parsing JSON Using Decodable in Swift 4 became super easy - Swift 4, iOS 11

Getting Started:

Create new project -> open Xcode -> File -> New -> Project -> Single View App, then tap next button. Type product name as 'JSONParseSwift4' then tap next and select the folder to save project.

In this article we will discuss about struct. Structs are very helpful for storing heterogeneous data unlike Array. For example in struct we can store Int, String etc

Creating a simple Struct. Write the following struct next to 'import UIKit'.


struct Tutorials {
    let id : Int
    let name : String
    let link : String
    let imageUrl : String
}
Cool, we created structure . Then how to use these Struct? Let write code for testing. Inside viewDidLoad() method write the following code.

let myTutorial = Tutorials(id: 1, name: "my tutorial", link: "some link", imageUrl: "some image")
print(myTutorial)
Build and Run, See the logs in console. Log should like following image.

Real time objects detected list

First thing we need sample JSON for parsing. So we need sample url for that. This is our sample url :

https://gist.githubusercontent.com/iosRevisited/bf28f444c262591b1807949ff40a2222/raw/695b2de9bc364762377d64595bf2b25a8de69786/sample.json 


Now how to get data from that url?

Here we go with simple code using URLSession with completion handler. Here is code for getting data from url. Delete all code inside viewDidLoad() method and replace with following code.


let urlString = "https://gist.githubusercontent.com/iosRevisited/bf28f444c262591b1807949ff40a2222/raw/695b2de9bc364762377d64595bf2b25a8de69786/sample.json"
    
    guard let url = URL(string: urlString) else {
    return
    }
    
    URLSession.shared.dataTask(with: url) { (data, response, err) in
    guard let data = data else { return }
    
    
    // Old School method for parsing Swift 2/3/Objective-C
    do {
    let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers)
    print(json)
    } catch let jsonErr {
    print("error in parsing",jsonErr)
    }
    }.resume()
Now Build and Run we will see data as following.

Parsing JSON Using Decodable Protocol
Now store the data in struct. So need to initialize with dictionary. Replace old Struct with new Struct.

struct Tutorials {
    let id : Int
    let name : String
    let link : String
    let imageUrl : String
    
    init(json:[String: Any]) {
        id = json["id"] as? Int ?? 0
        name = json["name"] as? String ?? ""
        link = json["link"] as? String ?? ""
        imageUrl = json["imageUrl"] as? String ?? ""
    }
}
Then replace 'print(json)' line with following code.

let tutorial = Tutorials(json: json)
print(tutorial.name)
Build and Run we will see the name in the console.

Output

Up to now what we did is old methods we used in Swift 2/3/Objective-C.

Now step into the new method introduced by apple in iOS 11 is Decodable Protocol. Remove init method in structure and provide Decodable Protocol to struct.
Replace structure with new struct with protocol.


struct Tutorial: Decodable {
    let id : Int
    let name : String
    let link : String
    let imageUrl : String
}
 Replace the code inside do { } block with following code.

let tutorial = try JSONDecoder().decode(Tutorial.self, from: data)
print(tutorial.name)
console output
Now Build and Run we will get the name as before but we never initialized the structure as before.

Important - we should match the data keys with the structure properties.

 

Array of Dictionaries :


Now let see this JSON:

[
 {
  "id": 1,
  "name": "Face Detection",
  "link": "http://iosrevisited.blogspot.com/2017/08/face-detection-using-vision-framework.html",
  "imageUrl": "https://support.apple.com/library/content/dam/edam/applecare/images/en_US/iOS/move-to-ios-icon.png"
 },
 {
  "id": 2,
  "name": "Real Time Object Detection",
  "link": "http://iosrevisited.blogspot.com/2017/08/real-time-camera-object-detection-with.html",
  "imageUrl": "https://support.apple.com/library/content/dam/edam/applecare/images/en_US/iOS/move-to-ios-icon.png"
 }
]
This is a bit complex than before. No worries, using swift 4 this became super easy. That JSON contains array of tutorials.

Change url to https://gist.githubusercontent.com/iosRevisited/7e6cd23e2a267a2503698411a834fe6c/raw/890f5842181369b83c820b4a6702b42776a9aabf/tutorials.json

Replace old url with new url. Then it looks like this.


let urlString = "https://gist.githubusercontent.com/iosRevisited/7e6cd23e2a267a2503698411a834fe6c/raw/890f5842181369b83c820b4a6702b42776a9aabf/tutorials.json"
Then inside do block change decode code to array like this [Tutorial]. Replace do block with following code.

let tutorials = try JSONDecoder().decode([Tutorial].self, from: data)
print("\(tutorials)")
Build and Run, Array of data appears like this:

Array of Dictionaries parsing using Decodable Protocol

Great, array of dictionaries also parsed using decodable protocol.

 

Complex Data Parsing: 


Now we will parse the following data from url - https://gist.githubusercontent.com/iosRevisited/65fff3a78a684909a7b64761799a02d3/raw/0850d72db8315ff25d2a24219d6592fd404deb35/Complex.json

{
 "name": "iOS Revisited",
 "description": "Blog about,Objective c language and Swift language programming questions, Tutorials, Bugs solving ....",
 "tutorials": [
  {
   "id": 1,
   "name": "Face Detection",
   "link": "http://iosrevisited.blogspot.com/2017/08/face-detection-using-vision-framework.html",
   "imageUrl": "https://support.apple.com/library/content/dam/edam/applecare/images/en_US/iOS/move-to-ios-icon.png"
  },
  {
   "id": 2,
   "name": "Real Time Object Detection",
   "link": "http://iosrevisited.blogspot.com/2017/08/real-time-camera-object-detection-with.html",
   "imageUrl": "https://support.apple.com/library/content/dam/edam/applecare/images/en_US/iOS/move-to-ios-icon.png"
  }
 ]
}
This structure is totally different from our struct model. We need to create one more model as following.

struct siteDescription: Decodable {
    let name: String
    let description: String
    let tutorials: [Tutorial]
}
Here we reused Tutorial Struct.

Now change urString. It looks like following.

let urlString = "https://gist.githubusercontent.com/iosRevisited/65fff3a78a684909a7b64761799a02d3/raw/0850d72db8315ff25d2a24219d6592fd404deb35/Complex.json"
Then replace code inside do block.

let siteData = try JSONDecoder().decode(SiteDescription.self, from: data)

print("\nname = \(siteData.name)")
print("\ndescription = \(siteData.description)")
print("\ntutorials = \(siteData.tutorials)")
Complex Data Parsing output

Build and Run , we will see following as output in console.

Cool, we parsed data easily in a efficient way using Decodable Protocol.




1 comment:

  1. import UIKit

    struct detail : Decodable {
    let id: Int
    let name: String
    let tel: String
    let address: String
    let longitude: Float
    let latitude: Float
    }

    struct Scenics : Decodable {
    let result: String
    let message: String
    let recordset: [detail]
    }

    class ViewController: UIViewController {
    var countries = [Country]()

    override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    let jsonURL = "http://research.cumi.co/scenics.php"
    let url = URL(string: jsonURL)
    URLSession.shared.dataTask(with: url!) { (data, response, error) in
    guard let data = data else {return}
    do {
    let scenics = try JSONDecoder().decode(Scenics.self, from: data)
    print(scenics.message)
    }
    catch {
    print("error")
    }
    }.resume()
    }

    override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
    }
    }

    Hi, above is my code.
    But always error at runtime.
    how to fix it?
    Thanks Advances

    ReplyDelete

Post Top Ad