9

I am looking for a proper way to handle a invalid argument during a initialization.

I am unsure how to do it using Swift as the init has't a return type. How can I tell whoever is trying to initialize this class that you are doing something wrong?

init (timeInterval: Int) {
    if timeInterval > 0
        self.timeInterval = timeInterval
    else
        //???? (return false?)
}

Thank you!

Marcelo
  • 1,176
  • 1
  • 10
  • 29
  • I would say force it by making `timeInterval` unsigned? Or you would have to assert it.. – Jack Jun 18 '14 at 22:07
  • Making it unsigned would kind of solve this particular case, but no many others. Assets sounds like a option. It kind of sucks tho... – Marcelo Jun 18 '14 at 22:10
  • You could use a class function. See http://stackoverflow.com/questions/24250363/how-should-i-handle-parameter-validation-swift/24250645#24250645. – Grimxn Jun 19 '14 at 18:22

4 Answers4

19

Use a failable initializer. Such an initializer looks very similar to a regular designated initializer, but has a '?' character right after init and is allowed to return nil. A failable initializer creates an optional value.

struct Animal {
    let species: String
    init?(species: String) {
        if species.isEmpty { return nil }
        self.species = species
    }
}

See Apple's documentation on failable initializers for more detail.

jefe2000
  • 406
  • 6
  • 13
3

In swift, you can't really abort a task half way through execution. There are no exceptions in swift and in general the philosophy is that aborting a task is dangerous and leads to bugs, so it just should't be done.

So, you verify a value like this:

assert(timeInterval > 0)

Which will terminate the program if an invalid value is provided.

You should also change timeInterval to be a UInt so that there will be a compiler error if anybody tries to give a < 0 value or an integer value that could be < 0.

It's probably not the answer you're looking for. But the goal is to check for bad parameters as early as possible, and that means doing it before you create any objects with those parameters. Ideally the check should be done at compile time but that doesn't always work.

Abhi Beckert
  • 32,787
  • 12
  • 83
  • 110
  • Nice! also you can provide a message for your assertion just like an error message – Mohsen Jun 18 '14 at 22:28
  • Assertion is fine if you want to terminate the app when it fails. However, there are many scenarios in which this would be inappropriate. If a user tries to create an instance with invalid arguments, I may want to inform that user, not crash the app. – HenryRootTwo Aug 04 '14 at 00:45
  • @codingTheHole in swift you are not ever meant to fail to init an object. Anything that could cause a failure must be checked for before calling init, and assert is used to crash the app if somebody failed to do so. – Abhi Beckert Aug 04 '14 at 00:55
2

I think this is the best solution, took it from:How should I handle parameter validation Swift

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
Community
  • 1
  • 1
Marcelo
  • 1,176
  • 1
  • 10
  • 29
1

In the Swift book by Apple, at the very bottom of this section:https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-XID_399

They say:

When to Use Assertions

Use an assertion whenever a condition has the potential to be false, but must definitely be true in order for your code to continue execution. Suitable scenarios for an assertion check include:

An integer subscript index is passed to a custom subscript implementation, but the subscript index value could be too low or too high. A value is passed to a function, but an invalid value means that the function cannot fulfill its task. An optional value is currently nil, but a non-nil value is essential for subsequent code to execute successfully.

This sounds exactly like your situation!

Thus your code should look like:

init (timeInterval: Int) {
    assert (timeInterval > 0, "Time Interval Must be a positive integer")

    // Continue your execution normally
}
Jack
  • 16,677
  • 8
  • 47
  • 51