0

I'm working with Github's graphQL API in order to get Issues and Pull Requests for a macOS app. Since both of those types are wrapped in the objects of similar structure, I would like to user polimorphic serialization.

Here are my Codable DTOs:

struct Edge: Codable {
    var node: Node

    enum CodingKeys: String, CodingKey {
        case node
    }
}

enum Node: Codable {
    case pull(Pull)
    case issue(Issue)
    
    init (from decoder: Decoder) throws {
        if let pull = try? Pull(from: decoder) {
            self = .pull(pull)
        } else if let issue = try? Issue(from: decoder) {
            self = .issue(issue)
        }  else {
            try self.init(from: decoder) // this will fail!
        }
    }
    
    func encode(to encoder: Encoder) throws {
        switch self {
        case .issue(let issue):
            try issue.encode(to: encoder)
        case .pull(let pull):
            try pull.encode(to: encoder)
        }
    }
}

struct Pull: Codable {
    var title: String
    var isDraft: Bool
    
    enum CodingKeys: String, CodingKey {
        case isDraft
    }
}

struct Issue: Codable {
    var title: String
    var author: User
    
    enum CodingKeys: String, CodingKey {
        case title
        case author
    }
}

The JSON response is serialized without errors, however I cannot access the object. How can I get Pull or Issue object from the Node?

In debug mode I can see the Node somehow got casted to a Pull or to an Issue:
enter image description here enter image description here

But everything I tried so far gives error:

edge.node.title   //Value of type 'Node' has no member 'title'
Pull(edge.node)   //Argument type 'Node' does not conform to expected type 'Decoder'
edge.node as Pull // Cannot convert value of type 'Node' to type 'Pull' in coercion
Larme
  • 24,190
  • 6
  • 51
  • 81
streetturtle
  • 5,472
  • 2
  • 25
  • 43
  • 2
    Use a `switch` or a `if case let`. `switch edge.node { case .pull(let pullValue): print("It's a pull: \(pullValue.title) - \(pullValue.isDraft)"); case .issue(let issueValue): print("It's an issue: \(issueValue.title) - \(issueValue.author)"))` – Larme Mar 14 '23 at 21:24
  • 1
    See https://stackoverflow.com/questions/24263539/accessing-an-enumeration-association-value-in-swift https://docs.swift.org/swift-book/documentation/the-swift-programming-language/enumerations/#Associated-Values etc. – Larme Mar 14 '23 at 21:27
  • Can u show your raw json response ? – Wo_0NDeR ᵀᴹ Mar 14 '23 at 21:30
  • 1
    I agree with the first comment. If you want full detail on this. I wrote an article about polymorphic serialization with swift [here](https://jacobzivandesign.com/technology/polymorphic-serialization-in-swift/) There is a subheading titled `Enumerations with Associated Values` That will show you how it works. – Jake Mar 14 '23 at 21:48
  • Thanks @Larme, the switch worked! @Jake - I was actually following your article :) But there is no example of how to use deserialized objects, and this is where I got stuck. Maybe just a oneliner, which would print content of a `backpack` would help? Thanks anyways! – streetturtle Mar 14 '23 at 22:14

0 Answers0