Exclamation mark prefix on variables during if statements and other as well? - swift

I'm very confused even after looking through similar questions of what the(!) operator does when it is prefixed on a variable or other object in if statements, functions, etc?
Example:
mutating func add(value: T)
{
if !contains(items, value)
{
items.append(value)
}
}

the exclamation mark ! is used for two purposes. When you see it appear at the beginning of an object, such as in !contains(items, values), it means "NOT". For example...
let x = 10
let y = 5
if x == y {
print("x is equal to y")
} else if x != y {
print("x is NOT equal to y")
}
The above code will print => "x is NOT equal to y" .
The logical NOT (!) operator can be used to reverse boolean values. For example...
var falseBoolValue = false
falseBoolValue = !falseBoolValue
print(falseBoolValue)
The above code will print => "true"
In addition to the usage as the logical NOT operator, the exclamation mark is also used to implicitly unwrap optional values. Whenever you see the exclamation mark appear at the end of an object name, such as in someVariable!, it is being used to implicitly unwrap an optional value. Read about optionals to gain a better understanding of how ! is used with optional values.

it is NOT prefix there. Meaning your if looks for "items NOT containing value".

Related

Instantiating a class with "!" in Swift? [duplicate]

I understand that in Swift all variables must be set with a value, and that by using optionals we can set a variable to be set to nil initially.
What I don't understand is, what setting a variable with a ! is doing, because I was under the impression that this "unwraps" a value from an optional. I thought by doing so, you are guaranteeing that there is a value to unwrap in that variable, which is why on IBActions and such you see it used.
So simply put, what is the variable being initialized to when you do something like this:
var aShape : CAShapeLayer!
And why/when would I do this?
In a type declaration the ! is similar to the ?. Both are an optional, but the ! is an "implicitly unwrapped" optional, meaning that you do not have to unwrap it to access the value (but it can still be nil).
This is basically the behavior we already had in objective-c. A value can be nil, and you have to check for it, but you can also just access the value directly as if it wasn't an optional (with the important difference that if you don't check for nil you'll get a runtime error)
// Cannot be nil
var x: Int = 1
// The type here is not "Int", it's "Optional Int"
var y: Int? = 2
// The type here is "Implicitly Unwrapped Optional Int"
var z: Int! = 3
Usage:
// you can add x and z
x + z == 4
// ...but not x and y, because y needs to be unwrapped
x + y // error
// to add x and y you need to do:
x + y!
// but you *should* do this:
if let y_val = y {
x + y_val
}

Comparing Decimals (not floats) in Swift

I have a test case with the following line:
XCTAssert(testVal == value)
value and testVal are both Decimals. In most cases the equivalence test gives the expected result, but not always. For instance
let value = Decimal(0xFFFF)
... do stuff that generates testVal
XCTAssert(testVal == value) // evaluates false
BUT, when I look at value and testVal, they appear to be the same.
(lldb) print testVal == value
(Bool) $R0 = false // the condition causing the test to fail
(lldb) print value.description
(String) $R1 = "65535" // what you would expect, given the init
(lldb) print testVal.description
(String) $R2 = "65535" // the same as value. Hmmm...
(lldb) print (testVal - value).isZero
(Bool) $R3 = true // the difference is zero, but they are not equal?
I checked all the attributes of the two Decimals and even the hash values are the same, yet they evaluate to not being equal. The only difference I see is that one is compact and the other is not. I don't see a way to force compaction, so I don't know if this is a factor.
When initializing with other values, like 0xFF, 65535.1, and a host of others, the tests compare successfully.
While this sort of behavior is typical of floats, it should not happen for Decimals, should it?
OK, found the answer not long after posting this: It does have to do with the Decimal being compacted. From the docs:
All the NSDecimal... arithmetic functions expect compact NSDecimal arguments.
Once I added the line
NSDecimalCompact(&testVal)
The comparisons worked as expected.

Checking if values in 2 NSTextFields are positive integers in a single If statement

I have two textboxes. When the user clicks on a button, I need to check if the values entered into the textfields are positive integers.
I try to do this using the following statement:
if ((Int(txta.stringValue))! < 0 ||(Int(txtb.stringValue))! < 0)
{
// Display error message
}
But it seems there is an issue with the if statement syntax.
Expected ',' separator is the error produced.
What I'm I doing wrong?
Please advise.
There are two problems:
Binary operators (such as ||) must have whitespace on both sides
(or on none), that's what causes the syntax error. (See what are the rules for spaces in swift.)
if (Int(txta.stringValue))! < 0 cannot be used to check for integer
input because the forced unwrap crashes at runtime if the string is
not an integer.
Int(txta.stringValue) returns nil if the string is not an integer, so
a possible check for integer input would be
if Int(txta.stringValue) == nil || Int(txtb.stringValue) == nil {
// display error message
}
But most probably you need the integer values themselves (in the case of valid input),
that's what optional binding is for
(see also When should I compare an optional value to nil?):
if let a = Int(txta.stringValue), let b = Int(txtb.stringValue) {
// Valid input, values are in `a` and `b` ...
} else {
// display error message
}
Additional conditions can be added to check the valid range, e.g.
if let a = Int(txta.stringValue), let b = Int(txtb.stringValue), a > 0, b > 0 {
// Valid positive input, values are in `a` and `b` ...
} else {
// display error message
}
Or with guard:
guard let a = Int(txta.stringValue), let b = Int(txtb.stringValue), a > 0, b > 0 else {
// display error message
return
}
// Valid positive input, values are in `a` and `b` ...
This is an ideal case for Optional.map and Optional.flatMap:
if (Int(textA.stringValue).map{ 0 <= $0 } ?? false) &&
(Int(textB.stringValue).map{ 0 <= $0 } ?? false) {
}
To break it down:
textA is a NSTextField.
textA.stringValue is some non-optional String value
Int(textA.stringValue) converts the text field's non-optional string value into an Int? value (a.k.a. Optional<Int>). It's optional, since the conversion from String to Int can fail (when the string doesn't encode an integer)
map{ 0 <= $0 } is called on that Int?. This is Optional.map, not to be confused with more common map functions, like Array.map. If the optional has some value, then the provided closure is applied to it. Otherwise, the nil is propagated onwards. Thus, the result of Int(textA.stringValue).map{ 0 <= $0 } is Bool?, with three possible values:
true (Optional.some(true)), meaning the text field encodes a positive (non-negative) integer.
false (Optional.some(false)), meaning the text field encodes a negative integer.
nil (Optional.none), meaning the text field doesn't encode an integer at all.
The nil coalescing operator is used (??), to merge the nil case into the false case. Thus, the result of Int(textA.stringValue).map{ 0 <= $0 } ?? false has 2 values:
true meaning the text field encodes a positive integer.
false meaning the text field encodes a negative integer, or doesn't encode a number at all. In either case, it doesn't encode a positive integer
The process is repeated for textB, and the two expressions are anded together (&&)

"If" statement with an optional value not working

I have an if statement with a variable, however the if statement does not work correctly and I am guessing it is because of an optional value on the variable.
The statement goes something like this
If (x == 6) {
}
x does = 6 but I cannot get the if statement to work.
When I do a "print x", the result is
Optional("6")
So I know the number is 6 but it seems that the optional value is making this if statement not work. I cannot get this unwrapped so I'm looking for another option.
See the double quotes? It means that x is String type not Int. You can do this way to make be more standard
if let x = x where x == "6" {
}
If you are getting:
Optional("6")
it means that the value is actually a string and not an int. If it was an int you would get:
Optional(6)
To double check you can try:
if x == "6"
{
}
I hope that helps.
How have you defined x
You’ll not get an optional if it is like
let x = 6 \\or var x = 6
if x == 6 {
print(x)
}
Will return you 6 not Optional("6")

Assigning last array element to a variable in Swift

I have this very simple line of code
var dblArray : [Double] = [0.01]
var x = dblArray.last
println(x * x)
The '.last' module returns the last element of the array, which is 0.01. However, based on the playground assistant view, it shows that the actual assignment to var x is (Some 0.01). And doing a println will lead to "Optional 0.01"
What I'm hoping to accomplish is merely capturing the value of the last element and placing it in x.
What am I doing wrong here?
I'm pretty certain .last would have to be an optional, if only to handle the edge case of an empty array, where .last would make no sense as a "solid" value.
In any case, if you're sure the array won't be empty, just unwrap the value. If you're not sure then you'll need to check intelligently such as with:
var x = 0
if let junk = dblArray.last {
x = junk
}
I think that's the correct syntax, I don't have my Mac with me at the moment, but it should hopefully be close enough to show the concept.