26

I have a property

public lazy var points: [(CGFloat,CGFloat,CGFloat)] = {
        var pointsT = [(CGFloat,CGFloat,CGFloat)]()
        let height = 100.0
        for _ in 1...10 {
            pointsT.append((someValue,someValue,100.0))
        }
        return pointsT
    }()

And i want to add a didSet method, is it possible?

William Hu
  • 15,423
  • 11
  • 100
  • 121

4 Answers4

27

Short answer: no.

Try out this simple example in some class or method of yours:

lazy var myLazyVar: Int = {
    return 1
} () {
    willSet {
        print("About to set lazy var!")
    }
}

This gives you the following compile time error:

Lazy properties may not have observers.


With regard to the let statement in the other answer: lazy variable are not necessary just "let constants with delayed initialisation". Consider the following example:

struct MyStruct {
    var myInt = 1

    mutating func increaseMyInt() {
        myInt += 1
    }

    lazy var myLazyVar: Int = {
        return self.myInt
    } ()
}

var a = MyStruct()
print(a.myLazyVar) // 1
a.increaseMyInt()
print(a.myLazyVar) // 1: "initialiser" only called once, OK
a.myLazyVar += 1
print(a.myLazyVar) // 2: however we can still mutate the value
                   //    directly if we so wishes
dfrib
  • 70,367
  • 12
  • 127
  • 192
  • 1
    You are absolutely right about that, will remove that from my answer which then basically reads "no" :) up you go! – luk2302 Dec 23 '15 at 01:53
9

The short answer is as other have said is "no" but there is a way to get the effect using a private lazy var and computed var.

private lazy var _username: String? = {
    return loadUsername()
}()
var username: String? {
    set {
        // Do willSet stuff in here
        if newValue != _username {
            saveUsername(newValue)
        }
        // Don't forget to set the internal variable
        _username = newValue
        // Do didSet stuff here
        // ...
    }
    get {
        return _username
    }
}
Joseph Lord
  • 6,446
  • 1
  • 28
  • 32
3

No

points is a constant, you cannot set anything to it. The only difference to a let constant is that it is (potentially) initialized later.

This answer provides a little more information as to why you use var instead of let in the lazy case.

Edit: to make the answer not look to empty Take a look at this blog post where the author raises a few valid points regarding why observing lazy vars might not yet be supported. What should oldValue be in the observer? nil? That would not be a good idea in your non-optional case.

Community
  • 1
  • 1
luk2302
  • 55,258
  • 23
  • 97
  • 137
  • *"The only difference to a let constant is that it is (potentially) initialised later"*, this is not really true. The lazy vars can be used as `var`:s just as well, see my added example. – dfrib Dec 23 '15 at 01:41
  • Regarding the edit: I agree, good addition. Also, you were somewhat on point before in that lazy var *should* generally behave quite immutable after initialisation, since generally, we use lazy vars on (large?) class or structure instances that themselves will not change (even if their members mutate; but we can have separate observers for this). Hence, we have our own `once-only-willSet` in our initialiser of the lazy variables (e.g. print "Will be set!"). So perhaps Apple intends (convention-wise) for lazy vars to not change after init., hence another reason why there's no prop. observers. – dfrib Dec 23 '15 at 02:08
3

Yes, Started by version Swift 5.3 Property observers can now be attached to lazy properties.

class C {
  lazy var property: Int = 0 {
    willSet { print("willSet called!") } // Okay
    didSet { print("didSet called!") } // Okay
  }
}

or

class C {
  lazy var property: Int = { return 0 }() {
    willSet { print("willSet called!") } // Okay
    didSet { print("didSet called!") } // Okay
  }
}

Please take a look at the links below for more:

  1. https://www.swiftbysundell.com/tips/lazy-property-observers/
  2. https://github.com/apple/swift/blob/main/CHANGELOG.md