-
Notifications
You must be signed in to change notification settings - Fork 10.6k
Description
| 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?