JSON Data

JSON data, especially when it is condensed like it is in your console, may seem daunting. However, it is actually a very simple syntax. JSON can contain the most basic types used to represent model objects: arrays, dictionaries, strings, numbers, Booleans, and null (nil). JSON dictionaries must have keys that are strings, but the values can be any other JSON type. Finally, arrays can contain any JSON type. Thus, a JSON document is a nested set of these types of values.

Here is an example of some really simple JSON:

    {
        "name" : "Christian",
        "friends" : ["Stacy", "Mikey"],
        "job" : {
            "company" : "Big Nerd Ranch",
            "title" : "Senior Nerd"
        }
    }

This JSON document begins and ends with curly braces ({ and }), which in JSON delimit a dictionary. Within the curly braces are the key-value pairs that belong to the dictionary. This dictionary contains three key-value pairs (name, friends, and job).

A string is represented by text within quotation marks. Strings are used as the keys within a dictionary and can be used as values, too. Thus, the value associated with the name key in the top-level dictionary is the string Christian.

Arrays are represented with square brackets ([ and ]). An array can contain any other JSON information. In this case, the friends key holds an array of strings (Stacy and Mikey).

A dictionary can contain other dictionaries, and the final key in the top-level dictionary, job, is associated with a dictionary that has two key-value pairs (company and title).

Photorama will parse out the useful information from the JSON data and store it in a Photo instance.

JSONDecoder and JSONEncoder

Apple has built-in classes for decoding JSON data into instances of some type (generally model objects) and generating JSON data from instances of some type. These are the JSONDecoder and JSONEncoder classes, respectively.

For decoding, you hand JSONDecoder a chunk of JSON data and tell it what type you expect to decode that data to, and it will create the instances for you. This works by leveraging the Codable protocol that you learned about in Chapter 13 and works exactly like the PropertyListDecoder that you used in that chapter. Let’s see how this class helps you.

In Photo.swift, update the Photo type to conform to Codable.

Listing 20.17  Conforming Photo to Codable (Photo.swift)

class Photo: Codable {
    let title: String
    let remoteURL: URL
    let photoID: String
    let dateTaken: Date
}

All the types contained within Photo are themselves codable, so no further work is needed.

Parsing the data that comes back from the server could go wrong in a number of ways: The data might not contain JSON. The data could be corrupt. The data might contain JSON but not match the format that you expect. To manage the possibility of failure, you will use an enumeration with associated values (which we will explain shortly) to represent the success or failure of the parsing.

Parsing JSON data

When working with JSONDecoder, the structure of the type you are decoding into must match the structure of the JSON that is returned from the server. If you look at the JSON string that you logged to the console earlier, you will notice that the JSON structure that is returned by Flickr has the following format:

    { "photos":
        { "page": 1,
          "pages": 4,
          "perpage": 100,
          "total": "386",
          "photo": [
              { "id": "123456",
                "title": "Photo title",
                "datetaken":"2019-09-28 17:34:22",
                "url_z":"https://live.staticflickr.com/123/123456.jpg",
                ...
              },
              {
                ...
              },
              {
                ...
              }
            ]
        }
    }

As you can see, the photo data itself is nested a bit within the JSON structure. You will need to define a type to represent each layer of that structure for JSONDecoder to unpack.

Open FlickrAPI.swift and define structures for each of the layers.

Listing 20.18  Defining the response structures (FlickrAPI.swift)

struct FlickrResponse: Codable {
    let photos: FlickrPhotosResponse
}
struct FlickrPhotosResponse: Codable {
    let photo: [Photo]
}

The type names (FlickrResponse and FlickrPhotosResponse) do not matter, but the property names (photos and photo) need to match the names of the keys coming back within the JSON.

Often, the key names in the JSON are not what you want the property names to be in your data structure. For example, a web service might return a key named first_name, but you would like to name the property firstName. To accomplish this, you can create an enumeration that conforms to the CodingKey protocol that maps the preferred property name to the key name in the JSON.

In the Flickr JSON, the photos and photo keys do not accurately describe their contents. The photos key is associated with metadata information about the photos, and the photo key is associated with all the information for the photos themselves. Let’s update how those keys are referenced in the Swift code.

Update FlickrResponse and FlickrPhotosResponse to use custom property names.

Listing 20.19  Adding coding keys to the response structures (FlickrAPI.swift)

struct FlickrResponse: Codable {
    let photos: FlickrPhotosResponse
    let photosInfo: FlickrPhotosResponse

    enum CodingKeys: String, CodingKey {
        case photosInfo = "photos"
    }
}
struct FlickrPhotosResponse: Codable {
    let photo: [Photo]
    let photos: [Photo]

    enum CodingKeys: String, CodingKey {
        case photos = "photo"
    }
}

You will want to do the same mapping for Photo, since the property names do not match the JSON keys.

Open Photo.swift and add a CodingKeys enumeration.

Listing 20.20  Adding coding keys to the Photo class (Photo.swift)

class Photo: Codable {
    let title: String
    let remoteURL: URL
    let photoID: String
    let dateTaken: Date

    enum CodingKeys: String, CodingKey {
        case title
        case remoteURL = "url_z"
        case photoID = "id"
        case dateTaken = "datetaken"
    }
}

(Do not miss the capitalization difference between dateTaken and "datetaken".)

With the Codable response structures in place, you can now use JSONDecoder to parse the JSON into instances of the types you just created.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.223.170.223