Well, well, well... You've clearly had enough of parsing JSON in Swift up until this point, having to build out extensions and other classes for something that always seemed fairly trivial in Obj-C? Me too.

With Swift 4, we're introduced to the Codable protocol. Now, rather than dealing with these additonal classes (perhaps libraries in some cases), we're now able to write JSON parsing logic with a small number of lines. Even better, we're able to create an Object (struct) that brings lovely dot-notation into our codebase for parsed JSON also.

To do this, we first have to write the Struct that matches our JSON response. Here's a copy of the JSON data we're going to receive.

 


{
  "id": "806338355",
  "name": "Andy Shephard",
  "picture": {
    "data": {
      "is_silhouette": false,
      "url": "https://scontent.xx.fbcdn.net/v/t1.0-1/p50x50/20620842_10156440032283356_5725066005693433666_n.jpg?oh=5125e486a6a53dd8f5b5fa78fad177db&oe=5A428F37"
    }
  }
}

As you can see, there are quite a few nested properties that we'll need to deal with.


struct FBUserResult: Codable {
  struct Picture: Codable {
    
    struct PhotoData: Codable {
      let isSilhouette: Bool
      let url: URL
      
      enum CodingKeys: String, CodingKey {
        case isSilhouette = "is_silhouette"
        case url
      }
    }
    
    let data: PhotoData
  }
  
  let email: String
  let firstName: String
  let lastName: String
  let identifier: String
  let picture: Picture
  
  enum CodingKeys: String, CodingKey {
    case email
    case firstName = "first_name"
    case lastName = "last_name"
    case identifier = "id"
    case picture
  }
}

In order to deal with the difference in our property names, and the property names we're receiving from JSON, we have to use CodingKeys.
Notice that we're following the structure of the JSON data by nesting our structs. This isn't a requirement, I just find that as I don't intend on using the same structs outside of my declared FBUserResult struct, I'll nest them.

Now, dealing with the JSON response can be somewhat tricky. If the JSON response is being received as a Dictionary (or in my case, Any?), it first needs to be converted into Data so that the JSONEncoder object can deal with it.


let connection = FBSDKGraphRequestConnection()
connection.add(FBSDKGraphRequest.init(graphPath: "me",
                                      parameters: ["fields": "first_name, last_name, email, picture"]),
                                      completionHandler: { (_, result: Any?, error: Error?) in

  do {
    // Convert JSON to Data
    var jsonData: Data?
    if let json = result {
      jsonData = try? JSONSerialization.data(withJSONObject: json, options: .prettyPrinted)
    }

    // Convert Data to FBUserResult Struct
    let decodedFBUser: FBUserResult
    if let data = jsonData {
      let decoder = JSONDecoder()
      decodedFBUser = try decoder.decode(FBUserResult.self, from: data)
    }
  } catch {
    print(error)
  }
})
connection.start()

After converting this, you should end up with a populated FBUserResult object called decodedFBUser that has easily accessible properties via dot-notation.