2

I am learning Swift. I am designing a class that needs to do parameter validation in it's initializer. How should I handle this if the value passed falls out of range ? I am really finding it difficult to find an appropiate way to design this, considering that:

  • Swift does not have exceptions, in languages with exceptions and built-in try/catch mechanism, I would have thrown an exception.
  • Swift does not allow returning nil / null / nothing from the initializer to indicate an error condition, like we can do in Objective-C.
  • I feel passing an NSErrorPointer to an initializer is cumbersome and places an unneccessary burden on the consumer of the class.

How would you validate a parameter for an initializer in Swift ?

driis
  • 161,458
  • 45
  • 265
  • 341
  • You cannot really pass to `NSErrorPointer` since initializer in Swift can't return `nil`... – Sulthan Jun 16 '14 at 20:35
  • Personally I just use assertions, if I try to init an object with a non-valid parameter it is highly likely that I need to deal with this at an earlier point of the stack. – T. Benjamin Larsen Jun 17 '14 at 04:29

3 Answers3

3

You can do it using class functions. See below. There are two points to note - the class function has to return Self? not Self, to allow the nil return, and the class must have an @required init().

class Validate {
    class func instanceOrNil(valid: Bool) -> Self? {
        if valid {
            return self()
        } else {
            return nil
        }
    }
    @required init() {
    }
}

let iv = Validate.instanceOrNil(false) // nil
let v = Validate.instanceOrNil(true) // Validate instance

An actual "practical" example might look more like

class NumberLessThanTen {
    var mySmallNumber: Int?
    class func instanceOrNil(number: Int) -> NumberLessThanTen? {
        if number < 10 {
            return NumberLessThanTen(number: number)
        } else {
            return nil
        }
    }
    @required init() {
    }
    init(number: Int) {
        self.mySmallNumber = number
    }
}

let iv = NumberLessThanTen.instanceOrNil(17) // nil
let v = NumberLessThanTen.instanceOrNil(5) // valid instance
let n = v!.mySmallNumber // Some 5
Grimxn
  • 22,115
  • 10
  • 72
  • 85
3

Now with Swift 2.0 you can throw exceptions. For example:

enum Parameter: ErrorType {
    case Empty
    case Short
}

And then in your functions you can use the super useful guard to check if the received stuff is valid or not and do something like this:

guard parameter.characters.count > 0 else { throw Parameter.Empty }

And then you have to catch those exceptions:

do {
    // call your method with try for example something like 'let var = try MyClass("test")'
} catch .Empty {

} catch .Short {

} catch {
    print("Something else went wrong!")
}
Andres
  • 11,439
  • 12
  • 48
  • 87
0

One technique: make a Thing.createThing( ... ) class method that wraps the initializer and returns an optional Thing?. Perform the validation inside that method: if the parameters pass validation, call the initializer with them and return the result. If the validation fails, return nil.

Wes Campaigne
  • 4,060
  • 3
  • 22
  • 17