Blog

Encoding Custom Types with Encodable In Swift 4

Photo by  Steve Johnson  on  Unsplash

This post is the third of a ten-part series called Exploring Codable and Core Data in Swift 4.


Encoding Custom Types Automatically

To be consistent with our previous posts in the series, we’re going to continue to use The Movie Database API for this series, because it will provide the best real-world example for using Codable in your own application(s). For instance, let’s start with a JSON object representing a favorited item in a user account’s favorites list—say, Favorite:

Action: Genre
    {
   "media_type": "movie",
   "media_id": 500,
   "favorite": true
}

An account favorite model consists of a media_type, media_id, and favorite. Here’s an example of this model in Swift:

Action: Genre
    struct FavoritedMedia {
   let id: Int
   let type: String
   let isFavorited: Bool
}

As we said in the previous post, because Int, String and Bool already conform to Codable, by having FavoritedMedia conform to Encodable, we get an automatic conformance that satisfies all requirements of Encodable:

Action: Genre
    struct FavoritedMedia: Encodable {
   let id: Int
   let type: String
   let isFavorited: Bool
   // The Encodable method encode(to:) is implemented by default.
}

Encoding Custom Types Manually

In the example below, the Movie structure is extended to conform to the Decodable protocol by implementing its required function, encode(to:):

Action: Genre
    extension FavoritedMedia: Encodable {
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(id, forKey: .id)
        try container.encode(type, forKey: .type)
        try container.encode(isFavorited, forKey: .isFavorited)
    }
}

Now that we’ve gone over how to encode these values 1:1, let’s see how we can use the power of the Swift enum to make our logic a bit more safe. Next, we’ll convert our type property into a nested enum called FavoritedMedia.MediaType:

Action: Genre
    struct FavoritedMedia {
    let id: Int
    let type: MediaType
    let isFavorited: Bool
}

extension FavoritedMedia {
    enum MediaType: String {
        case movie
        case tv
    }
}

extension FavoritedMedia: Encodable {
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(id, forKey: .id)
        try container.encode(type.rawValue, forKey: .type)
        try container.encode(isFavorited, forKey: .isFavorited)
    }
}