問題
Memberwise Initializer
Swiftの構造体(struct)にはメンバ変数の初期化を行うイニシャライザ(Memberwise Initializer)がデフォルトで自動的に生成されます。この機能によって新しいメンバ変数を構造体に追加したとしても自動的にイニシャライザへの引数の追加が行われるため、イニシャライザの変更を手動でする必要もなく、対応漏れもありません。
struct Person {
let name: String
let age: Int
}
let john = Person(name:"John" , age: 18)
Memberwise Initializerが使えなくなる
ただこの便利な_Memberwise Initializer_は、イニシャライザを追加すると上書きされて使えなくなってしまいます。
下の例では新しく追加したbirthday:
を引数にとるイニシャライザは問題なく使えるのですが、_Memberwise Initializer_が上書きされてしまい、使おうとするとエラーが出てしまいます。Xcodeではサジェストもされません。
struct Person {
let name: String
let age: Int
init?(name: String, birthday: Date) {
let today = Date()
let dateComponents = Calendar.current.dateComponents([.year], from: birthday, to: today)
guard let age = dateComponents.year, age >= 0 else {
return nil
}
self.name = name
self.age = age
}
}
let john = Person(name: "John" , age: 18) // error: extra argument 'age' in call
let paul = Person(name: "Paul", birthday: someBirthday) // ok
解決方法
この問題はイニシャライザをextension
で追加すると解決します。
下の例ではbirthday:
を引数に取る新しいイニシャライザはもちろんですが、デフォルトの_Memberwise Initializer_も使えるようになっています。
struct Person {
let name: String
let age: Int
}
extension Person {
init?(name: String, birthday: Date) {
let today = Date()
let dateComponents = Calendar.current.dateComponents([.year], from: birthday, to: today)
guard let age = dateComponents.year, age >= 0 else {
return nil
}
self.name = name
self.age = age
}
}
let john = Person(name: "John", age: 18) // ok
let paul = Person(name: "Paul", birthday: someBirthday) // ok
良いですね 👍