iosjsonswiftquicktype

What's the appropriate way to access an enum from Swift Decodables?


I have a really weird case where I took JSON and thought I would be able to substring all the way to the data I want to access, however, it's not working as expected.

Using QuickType, I was able to convert this JSON: https://kidsuper.fodalabs.com/wp-json/wp/v2/art

To the below.

When trying to access, it seems like I should be able to do .acf.gallery.id however once I get to acf.gallery, it says .id does not exist. This is strange but here's what it returns when I try

let temp = imageArray[indexPath.row].acf.gallery.id
Value of type 'GalleryUnion?' has no member 'id'

Just for fun I tried this one and had no luck as well:

let temp = imageArray[indexPath.row].acf.GalleryUnion.galleryElementArray
    Error

: Value of type 'Acf' has no member 'GalleryUnion'

The return when I print .acf.gallery starts like this:

Acf(company: "Season #3", 
     gallery: Optional(weddingszn.GalleryUnion.galleryElementArray([weddingszn.GalleryElement(
         id: 135, galleryID: 135, title: "1-791x1024", 
         filename: "1-791x1024.jpg", url: "https://kidsuper.fodalabs.com/wp-content/up

Full code is below for what I'm trying to parse. Any ideas?

struct Acf: Codable {
    let company: String
    let gallery: GalleryUnion?
    let tagline: String
    let featuredImg: Bool?

    enum CodingKeys: String, CodingKey {
        case company, gallery, tagline
        case featuredImg = "featured_img"
    }
}

enum GalleryUnion: Codable {
    case bool(Bool)
    case galleryElementArray([GalleryElement])

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let x = try? container.decode(Bool.self) {
            self = .bool(x)
            return
        }
        if let x = try? container.decode([GalleryElement].self) {
            self = .galleryElementArray(x)
            return
        }
        throw DecodingError.typeMismatch(GalleryUnion.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for GalleryUnion"))
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
        case .bool(let x):
            try container.encode(x)
        case .galleryElementArray(let x):
            try container.encode(x)
        }
    }
}

struct GalleryElement: Codable {
    let id, galleryID: Int
    let title, filename: String
    let url: String
    let alt, author, description, caption: String
    let name, date, modified: String
    let mimeType: MIMEType
    let type: TypeEnum
    let icon: String
    let width, height: Int
    let sizes: Sizes

    enum CodingKeys: String, CodingKey {
        case id = "ID"
        case galleryID = "id"
        case title, filename, url, alt, author, description, caption, name, date, modified
        case mimeType = "mime_type"
        case type, icon, width, height, sizes
    }
}

Solution

  • So, GalleryUnion can one of two things. It can either both .bool(_) or galleryElementArray(_). When you want access the actual underlying value, you need to determine which state it's in.

    To do this in Swift, you can use a switch statement. You can then use it to gain access to the internally contained values. Maybe something similar to:

    if let gallery = acf.gallery {
        switch gallery {
        case .galleryElementArray(let values):
            values.forEach {
                print($0.id)
            }
        case .bool(_):
            break
        }
    }
    

    You might like to have a read of Enumerations, look for the "Associated Values" sections