Swift Nil Has a Numeric Value

Swift nil has a numeric value?

I believe what is happening is that the literal 1 is being implicitly typecast to the Int? type by the comparison to nil. For those who aren't used to Swift, I'll explain a little further. Swift has a concept called "optionals", which can either have a value or be nil. (For anyone familiar with Haskell, this is basically the Maybe monad.) It's illegal to assign nil to a variable that wasn't explicitly defined as optional, so let i: Int = nil will be rejected by the compiler. This allows for several benefits which are out of the scope of this answer, and it's a rather clever way to do it.

What's happening here, though, is that the literal 1 is a valid value of several types: Int, Int32, Int64, UInt32, UInt64, etc., etc., etc. And it's also a valid value of the optional versions of those types: Int?, Int32?, etc.

So when the Swift compiler sees a comparison between a literal value and nil, it tries to find a type that both these values would be valid for. 1 is a valid value of the Int? type, and nil is also a valid value of the Int? type, so it applies the comparison operator with the type signature (Int?, Int?) -> Bool. (That's the comparison operator that takes two Int? values and returns a Bool). That operator's rules say that nil values sort lower than anything else, even Int.min, and so you get the result seen in the OP's question.

Why am I getting nil for the Int64 value?

You should look at the raw JSON. For example, grab the data and

let string = response.data.flatMap { String(data: $0, encoding: .utf8) }
print(string ?? "Unable to get String representation of the data")

I'm wagering that the numbers will be in quotes meaning that they're really string representations of numeric values, not actually numbers:

[{"noofcars": "2", "cllstname": "Smith", ...

Note, I'm asking you to look at the raw JSON, not just the result which has already been decoded. We need to see how the raw JSON represented these numeric values.

Let's assuming for a second that my supposition is correct, that you're getting these values as strings. Clearly, the correct solution is that the web service should send numeric value as numbers, not as strings. But you want to work with the existing JSON, you first get the String representation for numofcars (etc.), and then convert that to an numeric value. For example:

AF.request("xxxxxxxxxxxxx", headers: headers).responseJSON { response in
guard let dictionaries = response.value as? [[String: Any]] else { return }

self.allClients = dictionaries.compactMap { dictionary -> ClientsData? in
let cllstname = dictionary["cllstname"] as? String ?? "Error"
let awards = (dictionary["noofawards"] as? String).flatMap { Int($0) } ?? 0
let cars = (dictionary["noofcars"] as? String).flatMap { Int($0) } ?? 0
let dogs = (dictionary["noofdogs"] as? String).flatMap { Int($0) } ?? 0
return ClientsData(cllstname: cllstname, noofawards: awards, noofcars: cars, noofdogs: dogs)
}
.sorted { $0.noofawards > $1.noofawards }
}

FWIW, please forgive the refactoring above, as I would recommend map or compactMap rather than building an empty array and appending values to it. It's just a bit more concise

Normal typed variable cannot compare with nil but a literal can, why is that?

This is from the Standard Library

/// A type that can represent either a `Wrapped` value or `nil`, the absence
/// of a value.
public enum Optional<Wrapped> : _Reflectable, NilLiteralConvertible {
case None
case Some(Wrapped)

The point to look out for is that Optional can be nil literal convertible.

Second this is how the comparision is defined in the Swift library which can be seen on github on this link:Github Swift Optional Implementation Code

public func < <T : Comparable> (lhs: T?, rhs: T?) -> Bool {
switch (lhs, rhs) {
case let (l?, r?):
return l < r
case (nil, _?):
return true
default:
return false
}
}

public func > <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
switch (lhs, rhs) {
case let (l?, r?):
return l > r
default:
return rhs < lhs
}
}

Which when combined can be used to reason your code.

The strategy is as follows:

 0 > nil
  1. 0 is implicity deduced to Optional or Int?.
  2. Swift Optinal has < which says if the lhs is nil then return true
  3. Swift Optional has > which says if the rhs is nil then return true

To simplify step 2 and 3: nil is always smaller than .some

The comparision of the >/< doesnot take into account the element type when either side is nil.

And in the case of

  var a = 7
a != nil

Its obvious that Int cannot be compared with Optional.None and this is exactly what that compiler shouts out.

Hope this explains it.

Swift: Setting variables if not equalling nil

How about

var myVariable = a ?? b

This sets myVariable to a except for the case when a == nil, then it sets myVariable to b. didSet would be called once with the respective value. It is called a "nil coalescing operator".

Your edited questions shows that a in your case cannot be nil because you force unwrap the array (with as!). Instead you should conditionally unwrap it (as?), so that your code can check for nil and use the hard-coded default instead.

Does anybody know the rationale behind (nil 0) == true and (nil = 0) == true in Swift?

Optionals are comparable, so they can be sorted, for example. The rules are very simple:

  1. Any two optionals that are nil compare equal.
  2. If one of the optionals is nil and the other is not, nil is less than non-nil.
  3. If both optionals are not nil, then the unwrapped values are compared.

As a consequence, nil equals nil, and nil is less than any non-nil optional.

It has nothing to do with the value 0 that you assigned. Assign -1000, or +100, or whatever you like, and you get the same result.

When should I compare an optional value to nil?

It is almost always unnecessary to check if an optional is not nil. Pretty much the only time you need to do this is if its nil-ness is the only thing you want to know about – you don’t care what’s in the value, just that it’s not nil.

Under most other circumstances, there is a bit of Swift shorthand that can more safely and concisely do the task inside the if for you.

Using the value if it isn’t nil

Instead of:

let s = "1"
let i = Int(s)

if i != nil {
print(i! + 1)
}

you can use if let:

if let i = Int(s) {
print(i + 1)
}

You can also use var:

if var i = Int(s) {
print(++i) // prints 2
}

but note that i will be a local copy - any changes to i will not affect the value inside the original optional.

You can unwrap multiple optionals within a single if let, and later ones can depend on earlier ones:

if let url = NSURL(string: urlString),
data = NSData(contentsOfURL: url),
image = UIImage(data: data)
{
let view = UIImageView(image: image)
// etc.
}

You can also add where clauses to the unwrapped values:

if let url = NSURL(string: urlString) where url.pathExtension == "png",
let data = NSData(contentsOfURL: url), image = UIImage(data: data)
{ etc. }

Replacing nil with a default

Instead of:

let j: Int
if i != nil {
j = i
}
else {
j = 0
}

or:

let j = i != nil ? i! : 0

you can use the nil-coalescing operator, ??:

// j will be the unwrapped value of i,
// or 0 if i is nil
let j = i ?? 0

Equating an optional with a non-optional

Instead of:

if i != nil && i! == 2 {
print("i is two and not nil")
}

you can check if optionals are equal to non-optional values:

if i == 2 {
print("i is two and not nil")
}

This also works with comparisons:

if i < 5 { }

nil is always equal to other nils, and is less than any non-nil value.

Be careful! There can be gotchas here:

let a: Any = "hello"
let b: Any = "goodbye"
if (a as? Double) == (b as? Double) {
print("these will be equal because both nil...")
}

Calling a method (or reading a property) on an optional

Instead of:

let j: Int
if i != nil {
j = i.successor()
}
else {
// no reasonable action to take at this point
fatalError("no idea what to do now...")
}

you can use optional chaining, ?.:

let j = i?.successor()

Note, j will also now be optional, to account for the fatalError scenario. Later, you can use one of the other techniques in this answer to handle j’s optionality, but you can often defer actually unwrapping your optionals until much later, or sometimes not at all.

As the name implies, you can chain them, so you can write:

let j = s.toInt()?.successor()?.successor()

Optional chaining also works with subscripts:

let dictOfArrays: ["nine": [0,1,2,3,4,5,6,7]]
let sevenOfNine = dictOfArrays["nine"]?[7] // returns {Some 7}

and functions:

let dictOfFuncs: [String:(Int,Int)->Int] = [
"add":(+),
"subtract":(-)
]

dictOfFuncs["add"]?(1,1) // returns {Some 2}

Assigning to a property on an optional

Instead of:

if splitViewController != nil {
splitViewController!.delegate = self
}

you can assign through an optional chain:

splitViewController?.delegate = self

Only if splitViewController is non-nil will the assignment happen.

Using the value if it isn’t nil, or bailing (new in Swift 2.0)

Sometimes in a function, there’s a short bit of code you want to write to check an optional, and if it’s nil, exit the function early, otherwise keep going.

You might write this like this:

func f(s: String) {
let i = Int(s)
if i == nil { fatalError("Input must be a number") }
print(i! + 1)
}

or to avoid the force unwrap, like this:

func f(s: String) {
if let i = Int(s) {
print(i! + 1)
}
else {
fatalErrr("Input must be a number")
}
}

but it’s much nicer to keep the error-handling code at the top by the check. This can also lead to unpleasant nesting (the "pyramid of doom").

Instead you can use guard, which is like an if not let:

func f(s: String) {
guard let i = Int(s)
else { fatalError("Input must be a number") }

// i will be an non-optional Int
print(i+1)
}

The else part must exit the scope of the guarded value, e.g. a return or fatalError, to guarantee that the guarded value will be valid for the remainder of the scope.

guard isn’t limited to function scope. For example the following:

var a = ["0","1","foo","2"]
while !a.isEmpty {
guard let i = Int(a.removeLast())
else { continue }

print(i+1, appendNewline: false)
}

prints 321.

Looping over non-nil items in a sequence (new in Swift 2.0)

If you have a sequence of optionals, you can use for case let _? to iterate over all the non-optional elements:

let a = ["0","1","foo","2"]
for case let i? in a.map({ Int($0)}) {
print(i+1, appendNewline: false)
}

prints 321. This is using the pattern-matching syntax for an optional, which is a variable name followed by ?.

You can also use this pattern matching in switch statements:

func add(i: Int?, _ j: Int?) -> Int? {
switch (i,j) {
case (nil,nil), (_?,nil), (nil,_?):
return nil
case let (x?,y?):
return x + y
}
}

add(1,2) // 3
add(nil, 1) // nil

Looping until a function returns nil

Much like if let, you can also write while let and loop until nil:

while let line = readLine() {
print(line)
}

You can also write while var (similar caveats to if var apply).

where clauses also work here (and terminate the loop, rather than skipping):

while let line = readLine() 
where !line.isEmpty {
print(line)
}

Passing an optional into a function that takes a non-optional and returns a result

Instead of:

let j: Int
if i != nil {
j = abs(i!)
}
else {
// no reasonable action to take at this point
fatalError("no idea what to do now...")
}

you can use optional’s map operator:

let j = i.map { abs($0) }

This is very similar to optional chaining, but for when you need to pass the non-optional value into the function as an argument. As with optional chaining, the result will be optional.

This is nice when you want an optional anyway. For example, reduce1 is like reduce, but uses the first value as the seed, returning an optional in case the array is empty. You might write it like this (using the guard keyword from earlier):

extension Array {
func reduce1(combine: (T,T)->T)->T? {

guard let head = self.first
else { return nil }

return dropFirst(self).reduce(head, combine: combine)
}
}

[1,2,3].reduce1(+) // returns 6

But instead you could map the .first property, and return that:

extension Array {
func reduce1(combine: (T,T)->T)->T? {
return self.first.map {
dropFirst(self).reduce($0, combine: combine)
}
}
}

Passing an optional into a function that takes an optional and returns a result, avoiding annoying double-optionals

Sometimes, you want something similar to map, but the function you want to call itself returns an optional. For example:

// an array of arrays
let arr = [[1,2,3],[4,5,6]]
// .first returns an optional of the first element of the array
// (optional because the array could be empty, in which case it's nil)
let fst = arr.first // fst is now [Int]?, an optional array of ints
// now, if we want to find the index of the value 2, we could use map and find
let idx = fst.map { find($0, 2) }

But now idx is of type Int??, a double-optional. Instead, you can use flatMap, which “flattens” the result into a single optional:

let idx = fst.flatMap { find($0, 2) }
// idx will be of type Int?
// and not Int?? unlike if `map` was used

Swift operators and nil

Two things happen here (whether we like it or not): First, there is an
operator

public func <<T : Comparable>(lhs: T?, rhs: T?) -> Bool

which compares two optionals if the underlying type is comparable.
The behavior is not documented (as far as I know), but it seems that
nil aka Optional<T>.None is considered less than all non-nil
values Optional<T>.Some(value).

Second, enum Optional has a constructor

/// Construct a non-`nil` instance that stores `some`.
public init(_ some: Wrapped)

Now in

if x < 10 { ... }

the lhs has the type Optional<Int>. The only candidate for the <
operator is the above-mentioned one comparing two optionals.
Therefore the rhs is inferred as an optional as well, so this
is equivalent to

if x < Optional<Int>.Some(10) { ... }

Update:

This feature has been removed in Swift 3 (SE-0121 – Remove Optional Comparison Operators) and that code no longer compiles with Xcode 8
(currently beta 6).

Generic with default value of nil

I would do this with two separate intitializers, one of which doesn't need to have an unused generic Z:

struct Vector {
var x: Double
var y: Double
var z: Double?
}

extension Vector {
public init<X: Numeric, Y: Numeric>(_ x: X, _ y: Y) {
self.init(x: x as! Double, y: y as! Double, z: nil as Double?)
}

public init<X: Numeric, Y: Numeric, Z: Numeric>(_ x: X, _ y: Y, _ z: Z? = nil) {
self.init(x: x as! Double, y: y as! Double, z: z as? Double)
}
}


Related Topics



Leave a reply



Submit