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-6890] Can't synthesize Decodable implementation for NSObject subclass #49439
Comments
Okay, looks like the issue here is real, but the diagnostic doesn't make it clear enough. The core problem is that Untitled 2.swift:3:20: error: class 'Object' has no initializers
public final class Object: NSObject {
^
Untitled 2.swift:4:16: note: stored property 'property' without initial value prevents synthesized initializers
public let property: String
^
= "" Because of this, // Note: not inheriting from anything.
public final class Object: Decodable, CustomStringConvertible {
public let property: String
public var description: String {
return "{\"property:\" \"\(property)\"}"
}
}
let json = """
{"property": "Hello!"}
""".data(using: .utf8)!
let decoder = JSONDecoder()
let object = try decoder.decode(Object.self, from: json)
print(object) // => {"property": "Hello!"} but since Note that this error is present for any class inheriting from another class (even if the property is a public class Super { init() {} }
public final class Object : Super {
public var property: String
} produces Untitled 2.swift:4:20: error: class 'Object' has no initializers
public final class Object : Super {
^
Untitled 2.swift:5:16: note: stored property 'property' without initial value prevents synthesized initializers
public var property: String
^
= "" The two simplest solutions to this are either:
|
What is worth investigating is what is producing this specific diagnostic, which normally indicates something like this: import Foundation
public final class Object : NSObject, Decodable {
public let property: String
override init() {
super.init()
}
} which produces Untitled 2.swift:7:15: error: property 'self.property' not initialized at super.init call
super.init()
^ |
@swift-ci Create |
I'm confused because if I were to hand-write a import Foundation
public final class Object: NSObject, Swift.Decodable {
private enum CodingKeys: String, CodingKey {
case property
}
public let property: String
public override var description: String {
return "{\"property:\" \"\(property)\"}"
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
property = try container.decode(String.self, forKey: .property)
super.init()
}
}
let json = """
{"property": "Hello!"}
""".data(using: .utf8)!
let decoder = JSONDecoder()
let object = try decoder.decode(Object.self, from: json)
print(object) // => {"property": "Hello!"} In this case, I'm setting the property before calling If the error said "we can't know that you don't want to encode properties from the superclass", then I'd be disappointed but satisfied. But it seems to me like Swift could satisfy its own error by setting the properties before calling |
@mdiep That's what bears investigation here — it should be doing that; we don't skip over decoding properties unless they're |
I don't get why there's an issue here. If you hadn't adopted Decodable, you'd have been told, rightly, that you needed an initializer, and you'd have written this: final class Object: NSObject {
let property: String
init(property:String) {
self.property = property
}
} That compiles fine. Then you adopt Decodable and all is well. |
@mattneub The issue is that the diagnostic isn't clear enough about what's going on — we're producing both a message that is misleading (the issue shouldn't be a |
The synthesized |
@mdiep Because |
See, that's my point. The error message may not have been very helpful, but that's true of a lot of Swift compiler error messages. It seems to me that the real problem here is just a matter of knowing the basic rules of how to write an object in Swift. Regardless of the error message, what @mdiep wrote was invalid, and the compiler rightly stopped him. |
— Because NSObject provides -[NSObject init] (NSObject.init() in Swift) which your subclass must either inherit, or override, regardless of Codable presence. If you can't inherit it (because inheriting it would leave your property uninitialized), you must override it. I don't think that's true.
What I'm suggesting is that the synthesized — Codable here provides an additional initializer, but you still must handle init(). I can't tell if this is supposed to be a statement of fact or a conclusion based on the previous arguments. Is this behavior part of the specficiation? i.e. is it the specified behavior for synthesized |
@mdiep
|
Comment by Dave Poirier (JIRA) Not sure if this can help the discussion, but Apple in their documentation seems to showcase no initializer being required if you use Codable/Decodable only: https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types Its quite a different beast if you also inherit from NSObject. |
Environment
Swift 4.0.2
Additional Detail from JIRA
md5: e659b73eb1675cdd0a354ee6f38ab113
Issue Description:
gives this error:
The text was updated successfully, but these errors were encountered: