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

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 errort.total = 30                    // Won't compilet.greeting = "Goodbye world"    // Won't compile. The compiler does the check for you                                // instead of a crash at run timet.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 dictionaryt.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