Failable init!

class Circle {
    var x: Double , y: Double
    var radius: Double

    init!(radius: Double) {
        x = 0.0
        y = 0.0
        if radius<=0 { self.radius = 1.0}
        self.radius = radius
    }
}

let circle = Circle(radius: 10.0)

print(circle.radius)

Swift compiler complains: Value of optional type 'Circle?' must be unwrapped to refer to member 'radius' of wrapped base type 'Circle'.

Does init! mean implicit unwrapped optional? So we do not need ?.

Could someone explain it?

Thanks and Have a nice Day!

It's a consequence of tightening of implicitly unwrapped optional types, so IUO's are not propagated throughout the code base without a friction.

class Circle {
    ...
    init!(radius: Double) { ... }
    ...
}

let circle = Circle(radius: 10.0)
print(type(of: circle))             // Optional<Circle>
print(circle.radius)                // 🛑 error
print(circle!.radius)               // ✅ OK
print(Circle(radius: 10.0).radius)  // ✅ OK
print(Circle(radius: 10.0)!.radius) // ✅ OK

let circle2: Circle! = Circle(radius: 10.0)
print(type(of: circle2))            // still Optional<Circle>
print(circle2.radius)               // ✅ OK
print(circle2!.radius)              // ✅ OK

Thank you, tera! You've saved my day!

I can’t recall another example of someone using init!, and didn’t even recall that this is a supported feature in Swift. Out of curiosity, why are you using init! here in the first place?

Also:

The first line does nothing here.

You can overload a static func's return with optionality…

extension Circle {
  static func `init`(radius: Double) -> Circle { .init(radius: radius) }
  static func `init`(radius: Double) -> Circle? { .init(radius: radius) }
}

…but you can't overload an initializer the same way, so init! would be the only option for this pattern.

class Circle {
  init(radius: Double) { }
  init?(radius: Double) { } // Invalid redeclaration of 'init(radius:)'
}
2 Likes

You are right! It should return after setting radius to 1.0.

Thank you for pointing this out.