How to Use Willset and Didset with Getter and Setter

Property observers willSet and didSet; Property getters and setters

When and why should I use willSet/didSet

  • willSet is called just before the value is stored.
  • didSet is called immediately after the new value is stored.

Consider your example with outputs:


var variable1 : Int = 0 {
didSet{
print("didSet called")
}
willSet(newValue){
print("willSet called")
}
}

print("we are going to add 3")

variable1 = 3

print("we added 3")

Output:

we are going to add 3
willSet called
didSet called
we added 3

it works like pre/post -condition

On the other hand, you can use get if you want to add, for example, a read-only property:

var value : Int {
get {
return 34
}
}

print(value)

value = 2 // error: cannot assign to a get-only property 'value'

Getters and Setters in Swift - Does it make sense to use WillSet and DidSet instead?

Your question boils down to compile time vs. run time error. To address your 3 questions:

  1. Yes, willCheck is your only option here
  2. Readonly properties fall into 2 types: (a) those whose value derive from other properties, for example, their sum; and (b) those that you want to be able to change by yourself, but not by the users. The first type truly have no setter; the second type has a public getter and a private setter. The compiler can help you check for that and the program will not compile. If you throw a fatalError in didSet you get a runtime error and your application will crash.
  3. There can be state objects that you don't want the user to freely mess with, and yes, you can completely hide those from the users.

Your code first example was too verbose in defining the backing variables - you don't need to do that. To illustrate these points:

class Test
{
// 1. Validate the new value
var mustBeginWithA: String = "A word" {
willSet {
if !newValue.hasPrefix("A") {
fatalError("This property must begin with the letter A")
}
}
}

// 2. A readonly property
var x: Int = 1
var y: Int = 2
var total: Int {
get { return x + y }
}

private(set) var greeting: String = "Hello world"
func changeGreeting() {
self.greeting = "Goodbye world" // Even for private property, you may still
// want to set it, just not allowing the user
// to do so
}

// 3. Hide implementation detail
private var person = ["firstName": "", "lastName": ""]
var firstName: String {
get { return person["firstName"]! }
set { person["firstName"] = newValue }
}

var lastName: String {
get { return person["lastName"]! }
set { person["lastName"] = newValue }
}

var fullName: String {
get { return self.firstName + " " + self.lastName }
set {
let components = newValue.componentsSeparatedByString(" ")
self.firstName = components[0]
self.lastName = components[1]
}
}
}

Usage:

let t = Test()
t.mustBeginWithA = "Bee" // runtime error

t.total = 30 // Won't compile

t.greeting = "Goodbye world" // Won't compile. The compiler does the check for you
// instead of a crash at run time

t.changeGreeting() // OK, greeting now changed to "Goodbye world"

t.firstName = "John" // Users have no idea that they are actually changing
t.lastName = "Smith" // a key in the dictionary and there's no way for them
// to access that dictionary

t.fullName = "Bart Simpsons" // You do not want the user to change the full name
// without making a corresponding change in the
// firstName and lastName. With a custome setter, you
// can update both firstName and lastName to maintain
// consistency

A note about private in Swift 2 vs. Swift 3: if you try this in a Swift 2 playground, you will find t.greeting = "Goodbye world" works just fine. This is because Swift 2 has a strange access level specifier: private means "only accessible within the current file". Separate the class definition and the sample code into different files and Xcode will complain. In Swift 3, that was changed to fileprivate which is both clearer and save the private keyword for something more similar to to Java and .NET

What is the purpose of willSet and didSet in Swift?

The point seems to be that sometimes, you need a property that has automatic storage and some behavior, for instance to notify other objects that the property just changed. When all you have is get/set, you need another field to hold the value. With willSet and didSet, you can take action when the value is modified without needing another field. For instance, in that example:

class Foo {
var myProperty: Int = 0 {
didSet {
print("The value of myProperty changed from \(oldValue) to \(myProperty)")
}
}
}

myProperty prints its old and new value every time it is modified. With just getters and setters, I would need this instead:

class Foo {
var myPropertyValue: Int = 0
var myProperty: Int {
get { return myPropertyValue }
set {
print("The value of myProperty changed from \(myPropertyValue) to \(newValue)")
myPropertyValue = newValue
}
}
}

So willSet and didSet represent an economy of a couple of lines, and less noise in the field list.

Variable with getter/setter cannot have initial value, on overridden stored property

In swift you are able to override properties only with computed properties (which are not able to have default values) with same type. In your case, if you wish override test property in SecondViewController you need write something like this:

override var test: Float {
get {
return super.test
}
set {
super.test = newValue
}
}

And there is no way to override didSet/willSet observers directly; you may do this by write other methods invoked in observers and just override them:

FirstViewController:

internal var test: Float = 32.0 {
willSet {
test_WillSet(newValue)
}
didSet {
test_DidSet(oldValue)
}
}

func test_WillSet(newValue: Float) {}
func test_DidSet(oldValue: Float) {}

SecondViewController:

override func test_WillSet(newValue: Float) {
super.test_WillSet(newValue)
}
override func test_DidSet(oldValue: Float) {
super.test_DidSet(oldValue)
}

Can stored property in Swift have getter and setter?

You have willSet and didSet.

That should be enough of an answer, but stackoverflow thinks it is too short :-)

Changing Values with WillSet Property Observer

it is not possible to change the values within the willSet observer. overriding the getter and setter could work:

override var frame: CGRect {
get { return super.frame }
set {
var tempFrame = newValue
tempFrame.origin.y = min(max(tempFrame.origin.y, minimumOriginY), maximumOriginY)
super.frame = tempFrame
}
}

Can I use willSet and/or didSet to trigger a transition to a new view controller based on the value of a variable?

This the method is inside the class than you should be able to do!. Check this answer!

//True model data
var _test : Int = 0 {

//First this
willSet {
println("Old value is \(_test), new value is \(newValue)")
}

//value is set

//Finaly this
didSet {
println("Old value is \(oldValue), new value is \(_test)")
}
}

Does setting the same value for a property still call willSet and didSet?

Yes, willSet and didSet get called even when setting to the same value. I tested it in a playground:

class Class1 {
var willSetCount = 0
var didSetCount = 0
var var1: String = "A" {
willSet {
willSetCount++
}
didSet {
didSetCount++
}
}
}

let aClass1 = Class1() // {0 0 "A"}
aClass1.var1 = "A" // {1 1 "A"}
aClass1.var1 = "A" // {2 2 "A"}

If you want to prevent side effects from happening when the same value is being set, you can compare the value to newValue/oldValue:

class Class2 {
var willSetCount = 0
var didSetCount = 0
var var1: String = "A" {
willSet {
if newValue != var1 {
willSetCount++
}
}
didSet {
if oldValue != var1 {
didSetCount++
}
}
}
}

let aClass2 = Class2() // {0 0 "A"}
aClass2.var1 = "A" // {0 0 "A"}
aClass2.var1 = "B" // {1 1 "B"}


Related Topics



Leave a reply



Submit