Swift enum opaque return type

163 Views Asked by At

I've been experimenting with this code example from John Sundell and want to be able to get an enum value from the array.

protocol ItemVariant: Decodable {
    var title: String { get }
    var imageURL: URL { get }
}

struct Video: ItemVariant {
    var title: String
    var imageURL: URL
    var url: URL
    var duration: String
    var resolution: String
}

struct Recipe: ItemVariant {
    var title: String
    var imageURL: URL
    var text: String
    var ingredients: [String]
}

enum Item {
    case video(Video)
    case recipe(Recipe)
}


extension Item: Decodable {
    struct InvalidTypeError: Error {
        var type: String
    }

    private enum CodingKeys: CodingKey {
        case type
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let type = try container.decode(String.self, forKey: .type)

        switch type {
        case "video":
            self = .video(try Video(from: decoder))
        case "recipe":
            self = .recipe(try Recipe(from: decoder))
        default:
            throw InvalidTypeError(type: type)
        }
    }
}

struct ItemCollection: Decodable {
    var items: [Item]
}

let json = """
{
    "items": [
        {
            "type": "video",
            "title": "Making perfect toast",
            "imageURL": "https://image-cdn.com/toast.png",
            "url": "https://videoservice.com/toast.mp4",
            "duration": "00:12:09",
            "resolution": "720p"
        },
        {
            "type": "recipe",
            "title": "Tasty burritos",
            "imageURL": "https://image-cdn.com/burritos.png",
            "text": "Here's how to make the best burritos...",
            "ingredients": [
                "Tortillas",
                "Salsa",
            ]
        }
    ]
}
"""

let result = try JSONDecoder().decode(ItemCollection.self, from: json.data(using: .utf8)!)
print(result.items)

I want to add the following:

extension Item {
    var value: some ItemVariant {
        switch self {
        case .recipe(let recipe):
            return recipe
        case .video(let video):
            return video
        }
    }
}

Allow for something like this:

print(result.items.first { $0.value is Video })

But I end up with this compiler error: function declares an opaque return type, but the return statements in its body do not have matching underlying types

I'm not sure how I can resolve this error and find an elegant way to retrieve a enum value from an enum array. Appreciate any help.

0

There are 0 best solutions below