How to parse JSON Data from API with Swift 5
Tags: swift, ios
Published: 2020-04-13 11:14:16

As an iOS developer, parsing JSON is undoubtably something that you will have to do. With the growing number of APIs available, the ability to parse JSON and use that data in your app has never been more useful. I will show you how to parse JSON data and use it within your app in a few easy steps.

### **Setting up the structure (with standard types)**

Firstly, you need to figure out what data you will get back from the API call so that you can setup your structure in Swift. Most websites will show you an example API call or even provide you with the structure you will need, along with the data types.

In this example, I will consider an API that provides information about movies. We will consider a `Movie` structure that holds basic details about a movie. To make this structure encodable and decodable, we will conform to the `Codable` protocol.

```swift
struct Movie: Codable {

    let title: String
    let releaseDate: String
    let runtime: Int
    let ageRating: String?
    let posterUrl: String?

}
```
*Note:* Notice that some of the properties are optionals; this is because the API call may return them as null, in which case the app would crash if they were not optional. You can usually figure out which properties need to be optional by looking at the API documentation and example API calls.

### **Setting up the structure (with custom types)**

Most calls will have at least one property that is an array or dictionary. We can use custom types to deal with these cases.

I will expand the example above by adding a list of the cast members of the movie, which will be represented by an array of another structure, `CastMember`.


```swift
struct Movie: Codable {

    let title: String
    let releaseDate: String
    let runtime: Int
    let ageRating: String?
    let posterUrl: String?
    let cast: [CastMember]

}

struct CastMember: Codable {

    let name: String
    let character: String
    let imageUrl: String?

}
```

### **Result type in Swift**

Our API call will ultimately return a Result type, which was introduced in Swift 5. Essentially, it is a value that represents a success or a failure; if in our case there is a success, a Movie object will be returned. If there is a failure, an error will be returned. In order for an error to be returned, we need to make our own custom error that will let the user know what went wrong.

To make a custom error, we will make a simple enum with a few cases, reflecting the type of network errors we can receive. You can learn more about enums [here](https://docs.swift.org/swift-book/LanguageGuide/Enumerations.html).

```swift
enum CustomError: String, Error {

    case invalidResponse = "The response from the server was invalid."
    case invalidData = "The data received from the server was invalid."

}
```

### **Setting up the function**

Now that we have our structure ready, we can make our API call and use a `JSONDecoder` to decode the data received from the API into our structure.

To start with you need to find the endpoint of your API call, which will be a url with the different parameters depending on what information you want to receive. Most API's will require you to get an API key.

```swift
let endpoint = "https://movies/movie/138807?api_key=somekey1234583902"
```

Your url is currently a String so you need to convert it to Swift's `URL` type before you can make the call. A simple `guard` statement can be used for this.

```swift
guard let url = URL(string: endpoint) else {
    completed(.failure(.invalidId))
    return
}
```

Providing there is no error, a `URLSession` can be started. In this closure, we have access to `data`, `response` and `error`.

```swift
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in

    ...
    task.resume()

}
```

We can check for an error using an if let statement and then check for data using a guard let statement. If there is an error, the completion handler will be called with `.failure` and that will be the end of our function.

```swift
if let error = error {
    completed(.failure(.unableToComplete))
    return
}

guard let data = data else {
    completed(.failure(.invalidData))
    return
}
```

At this point, it is time to use our `Movie` structure to decode the data received.

```swift
do {
    let decoder = JSONDecoder()
    let movie = try decoder.decode(Movie.self, from: data)
    completion(.success(movie))
} catch {
    completion(.failure(.invalidData))
}
```

If everything has been successful, our completion handler will return the `Movie` object that has been decoded from the data.

### **Calling the function**

The function making the API call has now been completed. To access the value from this call, we can use a switch statement to check for whether there is a result (.success) or an error (.failure).

```swift
getMovie() { result in

    switch result {
        case .success(let movie):
            print(movie)

        case .failure(let error):
            print(error)
    }

}
```

I hope this article was useful for understanding API calls in Swift; feel free to leave any comments and any tips would be highly appreciated!