Why doesn't didSet work?

9.6k Views Asked by At

In many examples of didSet I see on SO, this code will return 0, however, I can't get it to return anything other than the original value. What am I doing wrong?

Swift

struct Circle {
    var radius: Double {
        didSet {
            if radius < 0 {
                radius = 0
            }
        }
    }
}

var circ = Circle(radius: -25)

print(circ.radius)

Output

-25
5

There are 5 best solutions below

0
On BEST ANSWER

didSet isn't called during initialization, only afterwards. If you want to validate data during initialization, your initializer should do it.

If you add:

circ.radius = -50
print(circ.radius)

you'll see it working as you'd expect, and the output will be 0.0.

0
On

Use init for this:

struct Circle {
    var radius: Double

    init(radius: Double) {
        self.radius = radius < 0 ? 0 : radius
    }
}

var circ = Circle(radius: -25)

print(circ.radius)

0.0
2
On

As written by Paul in the comments, property observers didSet and willSet are not called during value initialization.

If you want to call them for a value also on initialization, you could add a function call to re-set the radius property after it has been initially set in the initializer:

struct Circle {
    var radius: Double {
        didSet {
            if radius < 0 {
                radius = 0
            }
        }
    }

    init(radius: Double) {
        self.radius = radius
        setRadius(radius) // <-- this will invoke didSet
    }

    mutating func setRadius(radius: Double) {
        self.radius = radius
    }
}

var circ = Circle(radius: -25)

print(circ.radius) // 0.0
1
On

Also you can use lazy and init(radius ....

struct Circle {
    lazy var radius: Double = 0.0 {
        didSet {
            print("didSet called")
            if radius < 0 {
                radius = 0
            }
        }
    }
    init(radius:Double) {
        self.radius = radius
    }
}
0
On

You can make sure didSet in the init, if you put it in a defer statement. Also works in deinit.

class Circle {
    var radius: Double {
       didSet {
          if radius < 0 {
             radius = 0
          }
       }
    }

    init(radius: Double) {
        defer { self.radius = radius }
    }
}