How to access Class's property in required convenience init?

Hi, I'm new to Swift. I am reading Mastering macOS Programming Chapterc13, about NSCoding, here is the code:

Code Block
class Person: NSObject, NSCoding {
  
var name: String
let kName = "Name"
  
init(name: String) {
  self.name = name
  super.init()
}
  
required convenience init?(coder: NSCoder) {
  guard let name = coder.decodeObject(forKey: kName) as? String else { return nil }
self.init(name: name)
 }
  
func encode(with coder: NSCoder) {
coder.encode(name, forKey: kName)
}
}


And I got an error in line 12:
'self' used in property access 'kName' before 'self.init' call

Of course, I just find rules in the Swift Language Guide - Initialization :

Safety check 3
A convenience initializer must delegate to another initializer before assigning a value to any property (including properties defined by the same class).

Maybe I am stuck here because I should init() before I can access the property kName. But I need to get the name and assigning to the init(name:) after

Can someone tell me the best practice to solve it is?

Thanks!





Accepted Reply


Safety check 4
An initializer cannot call any instance methods, read the values of any instance properties, or refer to self as a value until after the first phase of initialization is complete.

You cannot access the instance propertykName before self.init(name:) is called.

One way is making it a class property.
Code Block
class Person: NSObject, NSCoding {
var name: String
static let kName = "Name"
init(name: String) {
self.name = name
super.init()
}
required convenience init?(coder: NSCoder) {
guard let name = coder.decodeObject(forKey: Person.kName) as? String else { return nil }
self.init(name: name)
}
func encode(with coder: NSCoder) {
coder.encode(name, forKey: Person.kName)
}
}


Replies


Safety check 4
An initializer cannot call any instance methods, read the values of any instance properties, or refer to self as a value until after the first phase of initialization is complete.

You cannot access the instance propertykName before self.init(name:) is called.

One way is making it a class property.
Code Block
class Person: NSObject, NSCoding {
var name: String
static let kName = "Name"
init(name: String) {
self.name = name
super.init()
}
required convenience init?(coder: NSCoder) {
guard let name = coder.decodeObject(forKey: Person.kName) as? String else { return nil }
self.init(name: name)
}
func encode(with coder: NSCoder) {
coder.encode(name, forKey: Person.kName)
}
}


Or you could use the String directly

Code Block
required convenience init?(coder: NSCoder) {
guard let name = coder.decodeObject(forKey: "Name") as? String else { return nil }
self.init(name: name)
}

@Claude31
I have also considered this solution, but I think it's better to set the kName as a constant so that it can be reused


That's great.! So many thanks to you @OOPer