willSet and didSet observers are not called when a property is first initialized. They are only called when the property's value is set outside of an initialization context.
In Swift, didSet and willSet methods act as property observers. willSet runs a piece of code right before a property changes. didSet runs a piece of code right after the property has changed.
All of a class's stored properties—including any properties the class inherits from its superclass—must be assigned an initial value during initialization. Swift defines two kinds of initializers for class types to help ensure all stored properties receive an initial value.
didSet is called right after the data is stored and it has a default constant oldValue which shows the previous value that is overwritten. willSet and didSet cannot be used at computed property since set function has already covered their functionalities and getter only stored property cannot be set actually!
If you use defer
inside of an initializer, for updating any optional properties or further updating non-optional properties that you've already initialized and after you've called any super.init()
methods, then your willSet
, didSet
, etc. will be called. I find this to be more convenient than implementing separate methods that you have to keep track of calling in the right places.
For example:
public class MyNewType: NSObject {
public var myRequiredField:Int
public var myOptionalField:Float? {
willSet {
if let newValue = newValue {
print("I'm going to change to \(newValue)")
}
}
didSet {
if let myOptionalField = self.myOptionalField {
print("Now I'm \(myOptionalField)")
}
}
}
override public init() {
self.myRequiredField = 1
super.init()
// Non-defered
self.myOptionalField = 6.28
// Defered
defer {
self.myOptionalField = 3.14
}
}
}
Will yield:
I'm going to change to 3.14
Now I'm 3.14
Create an own set-Method and use it within your init-Method:
class SomeClass {
var someProperty: AnyObject! {
didSet {
//do some Stuff
}
}
init(someProperty: AnyObject) {
setSomeProperty(someProperty)
}
func setSomeProperty(newValue:AnyObject) {
self.someProperty = newValue
}
}
By declaring
someProperty
as type:AnyObject!
(an implicitly unwrapped optional), you allow self to fully initialize withoutsomeProperty
being set. When you callsetSomeProperty(someProperty)
you're calling an equivalent ofself.setSomeProperty(someProperty)
. Normally you wouldn't be able to do this because self hasn't been fully initialized. SincesomeProperty
doesn't require initialization and you are calling a method dependent on self, Swift leaves the initialization context and didSet will run.
As a variation of Oliver's answer, you could wrap the lines in a closure. Eg:
class Classy {
var foo: Int! { didSet { doStuff() } }
init( foo: Int ) {
// closure invokes didSet
({ self.foo = foo })()
}
}
Edit: Brian Westphal's answer is nicer imho. The nice thing about his is that it hints at the intent.
I had the same problem and this works for me
class SomeClass {
var someProperty: AnyObject {
didSet {
doStuff()
}
}
init(someProperty: AnyObject) {
defer { self.someProperty = someProperty }
}
func doStuff() {
// do stuff now that someProperty is set
}
}
This works if you do this in a subclass
class Base {
var someProperty: AnyObject {
didSet {
doStuff()
}
}
required init() {
someProperty = "hello"
}
func doStuff() {
print(someProperty)
}
}
class SomeClass: Base {
required init() {
super.init()
someProperty = "hello"
}
}
let a = Base()
let b = SomeClass()
In a
example, didSet
is not triggered. But in b
example, didSet
is triggered, because it is in the subclass. It has to do something with what initialization context
really means, in this case the superclass
did care about that
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With