Swift Generic "Numeric" Protocol that Validates Numeric Values - swift

Swift 3.
Ultimately my functions need to receive UInt8 data types, but I'm never sure if the arguments I will receive from callers will be Int, Int64, UInt, Float, etc. I know they will be numeric types, I just don't know which flavor.
I could do:
func foo(value: Int) { }
func foo(value: Float) {}
func foo(value: UInt) {}
But that's crazy. So I thought I could do something like create a protocol
protocol ValidEncodable {
}
And then pass in types that conform:
func foo(value: ValidEncodable) { }
And then in that function I could get my values into the correct format
func foo(value: ValidEncoable) -> UInt8 {
let correctedValue = min(max(floor(value), 0), 100)
return UInt8(correctedValue)
}
I'm really struggling to figure out
1) How to create this ValidEncodable protocol that contains all the numeric types
2) And how to do things like floor(value) when the value I get is an Int without iterating over every possible numeric type (i.e. (floor(x) is only available on floating-point types)
Ultimately I need the values to be UInt8 in the range of 0-100. The whole reason for this madness is that I'm parsing XML files to my own internal data structures and I want to bake in some validation to my values.

This can be done without a protocol, and by making use of compiler checks, which greatly reduces the changes of bugs.
My recommendation is to use a partial function - i.e. a function that instead of taking an int, it takes an already validated value. Check this article for a more in-depth description of why partial functions are great.
You can build a Int0to100 struct, which has either a failable or throwable initializer (depending on taste):
struct Int0to100 {
let value: UInt8
init?(_ anInt: Int) {
guard anInt >= 0 && anInt <= 100 else { return nil }
value = UInt8(anInt)
}
init?(_ aFloat: Float) {
let flooredValue = floor(aFloat)
guard flooredValue >= 0 && flooredValue <= 100 else { return nil }
value = UInt8(flooredValue)
}
// ... another initializers can be added the same way
}
and change foo to allow to be called with this argument instead:
func foo(value: Int0to100) {
// do your stuff here, you know for sure, at compile time,
// that the function can be called with a valid value only
}
You move to the caller the responsibility of validating the integer value, however the validation resolves to checking an optional, which is easy, and allows you to handle the scenario of an invalid number with minimal effort.
Another important aspect is that you explicitly declare the domain of the foo function, which improves the overall design of the code.
And not last, enforcements set at compile time greatly reduce the potential of having runtime issues.

If you know your incoming values will lie in 0..<256, then you can just construct a UInt8 and pass it to the function.
func foo(value: UInt8) -> UInt8 { return value }
let number = arbitraryNumber()
print(foo(UInt8(number)))
This will throw a runtime exception if the value is too large to fit in a byte, but otherwise will work. You could protect against this type of error by doing some bounds checking between the second and third lines.

Related

Is `inout` the correct way to prevent large array copies when passing parameters? [duplicate]

When passing a class or primitive type into a function, any change made in the function to the parameter will be reflected outside of the class. This is basically the same thing an inout parameter is supposed to do.
What is a good use case for an inout parameter?
inout means that modifying the local variable will also modify the passed-in parameters. Without it, the passed-in parameters will remain the same value. Trying to think of reference type when you are using inout and value type without using it.
For example:
import UIKit
var num1: Int = 1
var char1: Character = "a"
func changeNumber(var num: Int) {
num = 2
print(num) // 2
print(num1) // 1
}
changeNumber(num1)
func changeChar(inout char: Character) {
char = "b"
print(char) // b
print(char1) // b
}
changeChar(&char1)
A good use case will be swap function that it will modify the passed-in parameters.
Swift 3+ Note: Starting in Swift 3, the inout keyword must come after the colon and before the type. For example, Swift 3+ now requires func changeChar(char: inout Character).
From Apple Language Reference: Declarations - In-Out Parameters:
As an optimization, when the argument is a value stored at a physical
address in memory, the same memory location is used both inside and
outside the function body. The optimized behavior is known as call by
reference; it satisfies all of the requirements of the copy-in
copy-out model while removing the overhead of copying. Do not depend
on the behavioral differences between copy-in copy-out and call by
reference.
If you have a function that takes a somewhat memory-wise large value type as argument (say, a large structure type) and that returns the same type, and finally where the function return is always used just to replace the caller argument, then inout is to prefer as associated function parameter.
Consider the example below, where comments describe why we would want to use inout over a regular type-in-return-type function here:
struct MyStruct {
private var myInt: Int = 1
// ... lots and lots of stored properties
mutating func increaseMyInt() {
myInt += 1
}
}
/* call to function _copies_ argument to function property 'myHugeStruct' (copy 1)
function property is mutated
function returns a copy of mutated property to caller (copy 2) */
func myFunc(var myHugeStruct: MyStruct) -> MyStruct {
myHugeStruct.increaseMyInt()
return myHugeStruct
}
/* call-by-reference, no value copy overhead due to inout opimization */
func myFuncWithLessCopyOverhead(inout myHugeStruct: MyStruct) {
myHugeStruct.increaseMyInt()
}
var a = MyStruct()
a = myFunc(a) // copy, copy: overhead
myFuncWithLessCopyOverhead(&a) // call by reference: no memory reallocation
Also, in the example above---disregarding memory issues---inout can be preferred simply as a good code practice of telling whomever read our code that we are mutating the function caller argument (implicitly shown by the ampersand & preceding the argument in the function call). The following summarises this quite neatly:
If you want a function to modify a parameter’s value, and you want
those changes to persist after the function call has ended, define
that parameter as an in-out parameter instead.
From Apple Language Guide: Functions - In-Out Parameters.
For details regarding inout and how it's actually handled in memory (name copy-in-copy-out is somewhat misleading...)---in additional to the links to the language guide above---see the following SO thread:
Using inout keyword: is the parameter passed-by-reference or by copy-in copy-out (/call by value result)
(Edit addition: An additional note)
The example given in the accepted answer by Lucas Huang above tries to---in the scope of the function using an inout argument---access the variables that were passed as the inout arguments. This is not recommended, and is explicitly warned not to do in the language ref:
Do not access the value that was passed as an in-out argument, even if
the original argument is available in the current scope. When the
function returns, your changes to the original are overwritten with
the value of the copy. Do not depend on the implementation of the
call-by-reference optimization to try to keep the changes from being
overwritten.
Now, the access in this case is "only" non-mutable, e.g. print(...), but all access like this should, by convention, be avoided.
At request from a commenter, I'll add an example to shine light upon why we shouldn't really do anything with "the value that was passed as an in-out argument".
struct MyStruct {
var myStructsIntProperty: Int = 1
mutating func myNotVeryThoughtThroughInoutFunction (inout myInt: Int) {
myStructsIntProperty += 1
/* What happens here? 'myInt' inout parameter is passed to this
function by argument 'myStructsIntProperty' from _this_ instance
of the MyStruct structure. Hence, we're trying to increase the
value of the inout argument. Since the swift docs describe inout
as a "call by reference" type as well as a "copy-in-copy-out"
method, this behaviour is somewhat undefined (at least avoidable).
After the function has been called: will the value of
myStructsIntProperty have been increased by 1 or 2? (answer: 1) */
myInt += 1
}
func myInoutFunction (inout myInt: Int) {
myInt += 1
}
}
var a = MyStruct()
print(a.myStructsIntProperty) // 1
a.myInoutFunction(&a.myStructsIntProperty)
print(a.myStructsIntProperty) // 2
a.myNotVeryThoughtThroughInoutFunction(&a.myStructsIntProperty)
print(a.myStructsIntProperty) // 3 or 4? prints 3.
So, in this case, the inout behaves as copy-in-copy-out (and not by reference). We summarize by repeating the following statement from the language ref docs:
Do not depend on the behavioral differences between copy-in copy-out
and call by reference.
Function parameters are constants by default. Trying to change the value of a function parameter from within the body of that function results in a compile-time error. This means that you can’t change the value of a parameter by mistake. If you want a function to modify a parameter’s value, and you want those changes to persist after the function call has ended, define that parameter as an in-out parameter instead.
inout param: modifies passing and local variable values.
func doubleInPlace(number: inout Int) {
number *= 2
print(number)
}
var myNum = 10 doubleInPlace(number: &myNum)
inout parameter allow us to change the data of a value type parameter and to keep changes still after the function call has finished.
If you work with classes then, as you say, you can modify the class because the parameter is a reference to the class. But this won't work when your parameter is a value type (https://docs.swift.org/swift-book/LanguageGuide/Functions.html - In-Out Parameters Section)
One good example of using inout is this one (defining math for CGPoints):
func + (left: CGPoint, right: CGPoint) -> CGPoint {
return CGPoint(x: left.x + right.x, y: left.y + right.y)
}
func += (left: inout CGPoint, right: CGPoint) {
left = left + right
}
Basically it is useful when you want to play with addresses of variable its very useful in data structure algorithms
when use inout parameter swift 4.0 Work
class ViewController: UIViewController {
var total:Int = 100
override func viewDidLoad() {
super.viewDidLoad()
self.paramTotal(total1: &total)
}
func paramTotal(total1 :inout Int) {
total1 = 111
print("Total1 ==> \(total1)")
print("Total ==> \(total)")
}
}

When to use inout parameters?

When passing a class or primitive type into a function, any change made in the function to the parameter will be reflected outside of the class. This is basically the same thing an inout parameter is supposed to do.
What is a good use case for an inout parameter?
inout means that modifying the local variable will also modify the passed-in parameters. Without it, the passed-in parameters will remain the same value. Trying to think of reference type when you are using inout and value type without using it.
For example:
import UIKit
var num1: Int = 1
var char1: Character = "a"
func changeNumber(var num: Int) {
num = 2
print(num) // 2
print(num1) // 1
}
changeNumber(num1)
func changeChar(inout char: Character) {
char = "b"
print(char) // b
print(char1) // b
}
changeChar(&char1)
A good use case will be swap function that it will modify the passed-in parameters.
Swift 3+ Note: Starting in Swift 3, the inout keyword must come after the colon and before the type. For example, Swift 3+ now requires func changeChar(char: inout Character).
From Apple Language Reference: Declarations - In-Out Parameters:
As an optimization, when the argument is a value stored at a physical
address in memory, the same memory location is used both inside and
outside the function body. The optimized behavior is known as call by
reference; it satisfies all of the requirements of the copy-in
copy-out model while removing the overhead of copying. Do not depend
on the behavioral differences between copy-in copy-out and call by
reference.
If you have a function that takes a somewhat memory-wise large value type as argument (say, a large structure type) and that returns the same type, and finally where the function return is always used just to replace the caller argument, then inout is to prefer as associated function parameter.
Consider the example below, where comments describe why we would want to use inout over a regular type-in-return-type function here:
struct MyStruct {
private var myInt: Int = 1
// ... lots and lots of stored properties
mutating func increaseMyInt() {
myInt += 1
}
}
/* call to function _copies_ argument to function property 'myHugeStruct' (copy 1)
function property is mutated
function returns a copy of mutated property to caller (copy 2) */
func myFunc(var myHugeStruct: MyStruct) -> MyStruct {
myHugeStruct.increaseMyInt()
return myHugeStruct
}
/* call-by-reference, no value copy overhead due to inout opimization */
func myFuncWithLessCopyOverhead(inout myHugeStruct: MyStruct) {
myHugeStruct.increaseMyInt()
}
var a = MyStruct()
a = myFunc(a) // copy, copy: overhead
myFuncWithLessCopyOverhead(&a) // call by reference: no memory reallocation
Also, in the example above---disregarding memory issues---inout can be preferred simply as a good code practice of telling whomever read our code that we are mutating the function caller argument (implicitly shown by the ampersand & preceding the argument in the function call). The following summarises this quite neatly:
If you want a function to modify a parameter’s value, and you want
those changes to persist after the function call has ended, define
that parameter as an in-out parameter instead.
From Apple Language Guide: Functions - In-Out Parameters.
For details regarding inout and how it's actually handled in memory (name copy-in-copy-out is somewhat misleading...)---in additional to the links to the language guide above---see the following SO thread:
Using inout keyword: is the parameter passed-by-reference or by copy-in copy-out (/call by value result)
(Edit addition: An additional note)
The example given in the accepted answer by Lucas Huang above tries to---in the scope of the function using an inout argument---access the variables that were passed as the inout arguments. This is not recommended, and is explicitly warned not to do in the language ref:
Do not access the value that was passed as an in-out argument, even if
the original argument is available in the current scope. When the
function returns, your changes to the original are overwritten with
the value of the copy. Do not depend on the implementation of the
call-by-reference optimization to try to keep the changes from being
overwritten.
Now, the access in this case is "only" non-mutable, e.g. print(...), but all access like this should, by convention, be avoided.
At request from a commenter, I'll add an example to shine light upon why we shouldn't really do anything with "the value that was passed as an in-out argument".
struct MyStruct {
var myStructsIntProperty: Int = 1
mutating func myNotVeryThoughtThroughInoutFunction (inout myInt: Int) {
myStructsIntProperty += 1
/* What happens here? 'myInt' inout parameter is passed to this
function by argument 'myStructsIntProperty' from _this_ instance
of the MyStruct structure. Hence, we're trying to increase the
value of the inout argument. Since the swift docs describe inout
as a "call by reference" type as well as a "copy-in-copy-out"
method, this behaviour is somewhat undefined (at least avoidable).
After the function has been called: will the value of
myStructsIntProperty have been increased by 1 or 2? (answer: 1) */
myInt += 1
}
func myInoutFunction (inout myInt: Int) {
myInt += 1
}
}
var a = MyStruct()
print(a.myStructsIntProperty) // 1
a.myInoutFunction(&a.myStructsIntProperty)
print(a.myStructsIntProperty) // 2
a.myNotVeryThoughtThroughInoutFunction(&a.myStructsIntProperty)
print(a.myStructsIntProperty) // 3 or 4? prints 3.
So, in this case, the inout behaves as copy-in-copy-out (and not by reference). We summarize by repeating the following statement from the language ref docs:
Do not depend on the behavioral differences between copy-in copy-out
and call by reference.
Function parameters are constants by default. Trying to change the value of a function parameter from within the body of that function results in a compile-time error. This means that you can’t change the value of a parameter by mistake. If you want a function to modify a parameter’s value, and you want those changes to persist after the function call has ended, define that parameter as an in-out parameter instead.
inout param: modifies passing and local variable values.
func doubleInPlace(number: inout Int) {
number *= 2
print(number)
}
var myNum = 10 doubleInPlace(number: &myNum)
inout parameter allow us to change the data of a value type parameter and to keep changes still after the function call has finished.
If you work with classes then, as you say, you can modify the class because the parameter is a reference to the class. But this won't work when your parameter is a value type (https://docs.swift.org/swift-book/LanguageGuide/Functions.html - In-Out Parameters Section)
One good example of using inout is this one (defining math for CGPoints):
func + (left: CGPoint, right: CGPoint) -> CGPoint {
return CGPoint(x: left.x + right.x, y: left.y + right.y)
}
func += (left: inout CGPoint, right: CGPoint) {
left = left + right
}
Basically it is useful when you want to play with addresses of variable its very useful in data structure algorithms
when use inout parameter swift 4.0 Work
class ViewController: UIViewController {
var total:Int = 100
override func viewDidLoad() {
super.viewDidLoad()
self.paramTotal(total1: &total)
}
func paramTotal(total1 :inout Int) {
total1 = 111
print("Total1 ==> \(total1)")
print("Total ==> \(total)")
}
}

Can a condition be used to determine the type of a generic?

I will first explain what I'm trying to do and how I got to where I got stuck before getting to the question.
As a learning exercise for myself, I took some problems that I had already solved in Objective-C to see how I can solve them differently with Swift. The specific case that I got stuck on is a small piece that captures a value before and after it changes and interpolates between the two to create keyframes for an animation.
For this I had an object Capture with properties for the object, the key path and two id properties for the values before and after. Later, when interpolating the captured values I made sure that they could be interpolated by wrapping each of them in a Value class that used a class cluster to return an appropriate class depending on the type of value it wrapped, or nil for types that wasn't supported.
This works, and I am able to make it work in Swift as well following the same pattern, but it doesn't feel Swift like.
What worked
Instead of wrapping the captured values as a way of enabling interpolation, I created a Mixable protocol that the types could conform to and used a protocol extension for when the type supported the necessary basic arithmetic:
protocol SimpleArithmeticType {
func +(lhs: Self, right: Self) -> Self
func *(lhs: Self, amount: Double) -> Self
}
protocol Mixable {
func mix(with other: Self, by amount: Double) -> Self
}
extension Mixable where Self: SimpleArithmeticType {
func mix(with other: Self, by amount: Double) -> Self {
return self * (1.0 - amount) + other * amount
}
}
This part worked really well and enforced homogeneous mixing (that a type could only be mixed with its own type), which wasn't enforced in the Objective-C implementation.
Where I got stuck
The next logical step, and this is where I got stuck, seemed to be to make each Capture instance (now a struct) hold two variables of the same mixable type instead of two AnyObject. I also changed the initializer argument from being an object and a key path to being a closure that returns an object ()->T
struct Capture<T: Mixable> {
typealias Evaluation = () -> T
let eval: Evaluation
let before: T
var after: T {
return eval()
}
init(eval: Evaluation) {
self.eval = eval
self.before = eval()
}
}
This works when the type can be inferred, for example:
let captureInt = Capture {
return 3.0
}
// > Capture<Double>
but not with key value coding, which return AnyObject:\
let captureAnyObject = Capture {
return myObject.valueForKeyPath("opacity")!
}
error: cannot invoke initializer for type 'Capture' with an argument list of type '(() -> _)'
AnyObject does not conform to the Mixable protocol, so I can understand why this doesn't work. But I can check what type the object really is, and since I'm only covering a handful of mixable types, I though I could cover all the cases and return the correct type of Capture. Too see if this could even work I made an even simpler example
A simpler example
struct Foo<T> {
let x: T
init(eval: ()->T) {
x = eval()
}
}
which works when type inference is guaranteed:
let fooInt = Foo {
return 3
}
// > Foo<Int>
let fooDouble = Foo {
return 3.0
}
// > Foo<Double>
But not when the closure can return different types
let condition = true
let foo = Foo {
if condition {
return 3
} else {
return 3.0
}
}
error: cannot invoke initializer for type 'Foo' with an argument list of type '(() -> _)'
I'm not even able to define such a closure on its own.
let condition = true // as simple as it could be
let evaluation = {
if condition {
return 3
} else {
return 3.0
}
}
error: unable to infer closure type in the current context
My Question
Is this something that can be done at all? Can a condition be used to determine the type of a generic? Or is there another way to hold two variables of the same type, where the type was decided based on a condition?
Edit
What I really want is to:
capture the values before and after a change and save the pair (old + new) for later (a heterogeneous collection of homogeneous pairs).
go through all the collected values and get rid of the ones that can't be interpolated (unless this step could be integrated with the collection step)
interpolate each homogeneous pair individually (mixing old + new).
But it seems like this direction is a dead end when it comes to solving that problem. I'll have to take a couple of steps back and try a different approach (and probably ask a different question if I get stuck again).
As discussed on Twitter, the type must be known at compile time. Nevertheless, for the simple example at the end of the question you could just explicitly type
let evaluation: Foo<Double> = { ... }
and it would work.
So in the case of Capture and valueForKeyPath: IMHO you should cast (either safely or with a forced cast) the value to the Mixable type you expect the value to be and it should work fine. Afterall, I'm not sure valueForKeyPath: is supposed to return different types depending on a condition.
What is the exact case where you would like to return 2 totally different types (that can't be implicitly casted as in the simple case of Int and Double above) in the same evaluation closure?
in my full example I also have cases for CGPoint, CGSize, CGRect, CATransform3D
The limitations are just as you have stated, because of Swift's strict typing. All types must be definitely known at compile time, and each thing can be of only one type - even a generic (it is resolved by the way it is called at compile time). Thus, the only thing you can do is turn your type into into an umbrella type that is much more like Objective-C itself:
let condition = true
let evaluation = {
() -> NSObject in // *
if condition {
return 3
} else {
return NSValue(CGPoint:CGPointMake(0,1))
}
}

Implementing an enum ForwardIndexType

I have been struggling to properly implement the ForwardIndexType protocol for an enum, in particular the handling of the end case (i.e for the last item without a successor). This protocol is not really covered in the Swift Language book.
Here is a simple example
enum ThreeWords : Int, ForwardIndexType {
case one=1, two, three
func successor() ->ThreeWords {
return ThreeWords(rawValue:self.rawValue + 1)!
}
}
The successor() function will return the next enumerator value, except for the last element, where it will fail with an exception, because there is no value after .three
The ForwardTypeProtocol does not allow successor() to return a conditional value, so there seems to be no way of signalling that there is no successor.
Now using this in a for loop to iterate over the closed range of all the possible values of an enum, one runs into a problem for the end case:
for word in ThreeWords.one...ThreeWords.three {
print(" \(word.rawValue)")
}
println()
//Crashes with the error:
fatal error: unexpectedly found nil while unwrapping an Optional value
Swift inexplicably calls the successor() function of the end value of the range, before executing the statements in the for loop. If the range is left half open ThreeWords.one..<ThreeWords.three then the code executes correctly, printing 1 2
If I modify the successor function so that it does not try to create a value larger than .three like this
func successor() ->ThreeWords {
if self == .three {
return .three
} else {
return ThreeWords(rawValue:self.rawValue + 1)!
}
}
Then the for loop does not crash, but it also misses the last iteration, printing the same as if the range was half open 1 2
My conclusion is that there is a bug in swift's for loop iteration; it should not call successor() on the end value of a closed range. Secondly, the ForwardIndexType ought to be able to return an optional, to be able to signal that there is no successor for a certain value.
Has anyone had more success with this protocol ?
Indeed, it seems that successor will be called on the last value.
You may wish to file a bug, but to work around this you could simply add a sentinel value to act as a successor.
It seems, ... operator
func ...<Pos : ForwardIndexType>(minimum: Pos, maximum: Pos) -> Range<Pos>
calls maximum.successor(). It constructs Range<T> like
Range(start: minimum, end: maximum.successor())
So, If you want to use enum as Range.Index, you have to define the next of the last value.
enum ThreeWords : Int, ForwardIndexType {
case one=1, two, three
case EXHAUST
func successor() ->ThreeWords {
return ThreeWords(rawValue:self.rawValue + 1) ?? ThreeWords.EXHAUST
}
}
This is an old Question but I would like to sum up some things and post another possible solution.
As #jtbandes and #rintaro already stated a closed range created with the start...end operator is internally created with start..<end.successor()
Afaik this is an intentional behavior in Swift.
In many cases you can also use an Interval where you thought about using a Range or where Swift declared a Range by default. The point here is that intervals aren't collections.
So this is not possible with Intervals
for word in ThreeWords.one...ThreeWords.three {...}
================
For the following I assume the snippet above was just a debug case to cross-check the values.
To declare an Interval you need to explicitly specify the type. Either a HalfOpenInterval (..<) or a ClosedInterval (...)
var interval:ClosedInterval = ThreeWords.one...ThreeWords.four
This requires you to make your enumeration Comparable. Although Int is Comparable already, you still need to add it to the inheritance list
enum ThreeWords : Int, ForwardIndexType, Comparable {
case one=1, two, three, four
func successor() ->ThreeWords {
return ThreeWords(rawValue:self.rawValue + 1)!
}
}
And finally the enumeration need to conform to Comparable. This is a generic approach since your enumeration also conforms to the protocol RawRepresentable
func <<T: RawRepresentable where T.RawValue: Comparable>(lhs: T, rhs: T) -> Bool {
return lhs.rawValue < rhs.rawValue
}
Like I wrote you can't iterate over it in a loop anymore, but you can have a quick cross-check using a switch:
var interval:ClosedInterval = ThreeWords.one...ThreeWords.four
switch(ThreeWords.four) {
case ThreeWords.one...ThreeWords.two:
print("contains one or two")
case let word where interval ~= word:
print("contains: \(word) with raw value: \(word.rawValue)")
default:
print("no case")
}
prints "contains: four with raw value: 4"

Proper way to handle a fail to init

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!
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.
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.
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
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
}