3

Given that Swift does not have exception handling how do I communicate errors back to the caller when they pass an incorrect value for a property. Essentially, can properties have validation?

Here is an example. Consider numberOfShoes property below. It's computed

class Animal {
    var name : String
    var numberOfLegs : Int

    var numberOfShoes : Int {
        get {return numberOfLegs }
        set {
            if newValue < 0 {
                // TODO: WHAT GOES HERE?
            }

            numberOfLegs = newValue
        }
    }

    init(name : String, numberOfLegs : Int) {
        self.name = name
        self.numberOfLegs = numberOfLegs
    }
}

We could (should maybe) also have a willSet / didSet observer on numberOfLegs but I don't see how validation can be done here either.

Are we required to stick with things like:

var cat = Animal("Cat", 4)
if !cat.setNumberOfLegs(3) {
   // I guess that didn't work...
}

What is everyone else doing?

Michael Kennedy
  • 3,202
  • 2
  • 25
  • 34
  • define a _default_ value for the incorrect cases and set the property with that value if you found an invalid input – or call a `fatalError(...)` that throws an exception and your app crashes – or create an `enum` with predefined values which you accept in the setter. – holex Dec 19 '14 at 16:53
  • Thanks holex. I had more or less considered all of those. I was just hoping there was some middle ground between. fatalError (instead some kind of error that can be handled gracefully) and just silently not accepting the value and using another (default). Enum is a great idea but isn't always an option (string, double, etc). – Michael Kennedy Dec 19 '14 at 16:59

2 Answers2

5

Options:

  1. Call fatalError which will crash the app
  2. Provide a default value if validation fails
  3. Don't change numberOfLegs if validation fails and log out an error message
  4. Make numberOfShoes read only and instead provide a function to set the number of shoes that returns an optional error:

    func setNumberOfShoes(number: Int) -> NSError? { // ... }

Option 4 is the closest to an exception in that it allows the calling code to decide how it want to handle the invalid input. Of course, it does not however, force the caller to handle the error.

drewag
  • 93,393
  • 28
  • 139
  • 128
0

In Swift 2, you have a partial solution using a setter function, and error handling:

class Animal {

    enum Error: ErrorType {
        case Invalid
    }

    var name : String
    var numberOfLegs : Int
    var numberOfShoes : Int {
        get {return numberOfLegs }
    }

    func setNumberOfShoes(n: Int) throws {
        if n < 0 {
            throw Error.Invalid
        }
        self.numberOfLegs = n
    }
    init(name : String, numberOfLegs : Int) {
        self.name = name
        self.numberOfLegs = numberOfLegs
    }
}

var cat = Animal(name: "Cat", numberOfLegs: 4)
do {
    try cat.setNumberOfShoes(0)
    print("0 shoes is ok for an animal")
    try cat.setNumberOfShoes(-1)
} catch Animal.Error.Invalid {
    print("but -1 is not")
}

Eventually, swift will support declaring a computed property that "throws".

Community
  • 1
  • 1
CMont
  • 690
  • 6
  • 13