Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SR-5381] Codable with array of base class #47955

Closed
swift-ci opened this issue Jul 5, 2017 · 1 comment
Closed

[SR-5381] Codable with array of base class #47955

swift-ci opened this issue Jul 5, 2017 · 1 comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself

Comments

@swift-ci
Copy link
Collaborator

swift-ci commented Jul 5, 2017

Previous ID SR-5381
Radar None
Original Reporter Okui (JIRA User)
Type Bug
Status Resolved
Resolution Duplicate

Attachment: Download

Environment

Xcode 9 beta 2

Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug
Assignee None
Priority Medium

md5: 037f67612a560e973f40d26c4e04b646

duplicates:

  • SR-5331 Swift 4 Decodable Loses Subclass Type Information

Issue Description:

I'm trying to migrate my app using NSCoder into Swift4's Codable. However, I'm struggling using Codable for classes.

Say, you have Shape class and 2 subclasses Rectangle and Oval. And Canvas contains multiple shapes as {{ var shapes: [Shape] }}.

class Shape {}
class Rectangle : Shape {
  var frame: CGRect
}
class Oval : Shape {
  var position: CGPoint
  var radius: CGFloat
}
struct Canvas {
   var shapes: [Shape]
}

Then implement Codable for all shapes. How do you encode/decode Canvas with these class types?

I can't encode class type into Shape itself because by the time Shape's required init(from decoder: Decoder) throws is called, it is already in Shape's initializer, so I can't initialize Rectangle nor Oval.

So, I ended up having wrapper struct that hold class type as string, and class reference to encode something like this:

struct ShapeEncoder : Codable {
  var shape: Shape

  init(shape: Shape) {
    self.shape = shape
  }

  private enum ShapeType : String, Codable {
    case rectangle
    case oval
  }

  func encode(to encoder: Encoder) throws {

    var container = encoder.unkeyedContainer()

    let shapeType: ShapeType
    if shape is Rectangle {
      shapeType = .rectangle
    }
    else {
      shapeType = .oval
    }

    try container.encode(shapeType)
    try container.encode(shape)
  }
  init(from decoder: Decoder) throws {
    // do similar thing
  }
}

struct Canvas : Codable {
  var shapes: [Shape]
  private enum CodingKeys: String, CodingKey {  case shapes  }

  init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    let encoders = try container.decode([ShapeEncoder].self, forKey: .shapes)
    self.shapes = encoders.map { $0.shape }
  }
  func encode(to encoder: Encoder) throws { ... }

[{"rectangle", "{"frame":[0, 0, 100, 100]}"}, {"oval":, "{position: [100, 100], radius: 50}"}]

(Playground file is attached.)

This would work, but I'm wondering... is there any better way to encode/decode with array base class type of array?

@belkadan
Copy link
Contributor

belkadan commented Jul 6, 2017

See the discussion in SR-5331 for why Codable doesn't work this way.

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself
Projects
None yet
Development

No branches or pull requests

2 participants