In Swift 3, this is a compile error, if I use > or <
let a: Int?
guard a > 0 else {return}
guard a < 0 else {return}
Compile error:
Value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'?
But it's okay if I compare with == or !=
let a: Int?
guard a == 0 else {return}
guard a != 0 else {return}
It makes perfect sense for the equality operator to support optionals, because it's absolutely clear that for any integer valued variable i:
nil == nil
nil != i
i != nil
i == i if and only if their values are the same
On the other hand, it's not clear how comparison to nil should act:
Is i less than nil?
If I want to sort an array so that all the nils come out at the end, then I would want i to be less than nil.
But if I want to sort an array so that all the nils come out at the start, then I would want i to be greater than nil.
Since either of these are equally valid, it wouldn't make sense for the standard library to favor one over the other. It's left to the programmer to implement whichever comparison makes sense for their use-case.
Here's a toy implementation that generates a comparison operator to suite either case:
func nilComparator<T: Comparable>(nilIsLess: Bool) -> (T?, T?) -> Bool {
return {
switch ($0, $1) {
case (nil, nil): return false
case (nil, _?): return nilIsLess
case (_?, nil): return !nilIsLess
case let (a?, b?): return a < b
}
}
}
let input = (0...10).enumerated().map {
$0.offset.isMultiple(of: 2) ? Optional($0.element) : nil
}
func concisePrint<T>(_ optionals: [T?]) -> String {
return "[" + optionals.map { $0.map{ "\($0)?" } ?? "nil" }.joined(separator: ", ") + "]"
}
print("Input:", concisePrint(input))
print("nil is less:", concisePrint(input.sorted(by: nilComparator(nilIsLess: true))))
print("nil is more:", concisePrint(input.sorted(by: nilComparator(nilIsLess: false))))
Output:
Input: [0?, nil, 2?, nil, 4?, nil, 6?, nil, 8?, nil, 10?]
nil is less: [nil, nil, nil, nil, nil, 0?, 2?, 4?, 6?, 8?, 10?]
nil is more: [0?, 2?, 4?, 6?, 8?, 10?, nil, nil, nil, nil, nil]
Optional equality works logically, comparison doesn't.
5 == 5 = true
5 == nil = false
5 == 6 = false
nil == nil = true
Those all make sense, but these don't:
6 > 5 = true
5 > 5 = false
5 > nil = ??
nil > 5 = ??
This type of comparison does not have a simple answer, nor will that answer be the same depending on the use case.
This is because Int and Int? are 2 different things.
According to the documentation, Int has overloads for < and > and some other operators, while Optional only has overloads for == and !=, see the documentation on Optional, the section talking about Comparing Optional Values.
Related
With this code I enable a button if any form value is not nil:
myButton.isEnabled = !myForm.values.contains(where: { $0 == nil })
Is there any way to add multiple checks? I want to enable my button:
if the value of any field is nil (done, as you can see above);
if the value of any field is > 0;
if the value of any field is < 2;
(that is, the value should NOT be nil and should NOT be outside the range 0.0 - 2.0)
Can I still use contains(where:) to do it?
MyForm is a dictionary like this one:
["oct": nil,
"jan": Optional(3666.0),
"nov": nil,
"apr": nil,
"sep": nil,
"feb": nil,
"jul": nil,
"mar": nil,
"dec": nil,
"may": nil,
"jun": nil,
"aug": nil]
(strange enough, due to the behavior of a 3rd party library, it accepts nil values)
Yes, you can. If you want to enable the button if there is at least one Double value in range 0.0..<2.0 you don't need to call values. You can call contains on the dictionary directly. The where keyword is syntactic sugar and can be omitted when using trailing closure syntax.
The code uses the pattern matching operator to check the range.
myButton.isEnabled = myForm.contains { (_, value) in
guard let value = value as? Double else { return false }
return 0.0..<2.0 ~= value
}
If you want to enable the button if all values are Double in the given range you have to check inverted
myButton.isEnabled = !myForm.contains { (_, value) in
guard let value = value as? Double else { return true }
return !(0.0..<2.0 ~= value)
}
You can do it just by filtering all values like this:
myButton.isEnabled = !myForm.values.filter {
if let value = $0 { return value.field > 0.0 && value.field < 2.0 }
return false
}.count > 0
contains function is not appropriate solution in your case, because it should be used when you need to check if array contain some specific element. But in your case you need to check if values is in specific range.
It is possible to use contains function but it is not correct way
In Swift 3, this is a compile error, if I use > or <
let a: Int?
guard a > 0 else {return}
guard a < 0 else {return}
Compile error:
Value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'?
But it's okay if I compare with == or !=
let a: Int?
guard a == 0 else {return}
guard a != 0 else {return}
It makes perfect sense for the equality operator to support optionals, because it's absolutely clear that for any integer valued variable i:
nil == nil
nil != i
i != nil
i == i if and only if their values are the same
On the other hand, it's not clear how comparison to nil should act:
Is i less than nil?
If I want to sort an array so that all the nils come out at the end, then I would want i to be less than nil.
But if I want to sort an array so that all the nils come out at the start, then I would want i to be greater than nil.
Since either of these are equally valid, it wouldn't make sense for the standard library to favor one over the other. It's left to the programmer to implement whichever comparison makes sense for their use-case.
Here's a toy implementation that generates a comparison operator to suite either case:
func nilComparator<T: Comparable>(nilIsLess: Bool) -> (T?, T?) -> Bool {
return {
switch ($0, $1) {
case (nil, nil): return false
case (nil, _?): return nilIsLess
case (_?, nil): return !nilIsLess
case let (a?, b?): return a < b
}
}
}
let input = (0...10).enumerated().map {
$0.offset.isMultiple(of: 2) ? Optional($0.element) : nil
}
func concisePrint<T>(_ optionals: [T?]) -> String {
return "[" + optionals.map { $0.map{ "\($0)?" } ?? "nil" }.joined(separator: ", ") + "]"
}
print("Input:", concisePrint(input))
print("nil is less:", concisePrint(input.sorted(by: nilComparator(nilIsLess: true))))
print("nil is more:", concisePrint(input.sorted(by: nilComparator(nilIsLess: false))))
Output:
Input: [0?, nil, 2?, nil, 4?, nil, 6?, nil, 8?, nil, 10?]
nil is less: [nil, nil, nil, nil, nil, 0?, 2?, 4?, 6?, 8?, 10?]
nil is more: [0?, 2?, 4?, 6?, 8?, 10?, nil, nil, nil, nil, nil]
Optional equality works logically, comparison doesn't.
5 == 5 = true
5 == nil = false
5 == 6 = false
nil == nil = true
Those all make sense, but these don't:
6 > 5 = true
5 > 5 = false
5 > nil = ??
nil > 5 = ??
This type of comparison does not have a simple answer, nor will that answer be the same depending on the use case.
This is because Int and Int? are 2 different things.
According to the documentation, Int has overloads for < and > and some other operators, while Optional only has overloads for == and !=, see the documentation on Optional, the section talking about Comparing Optional Values.
Lets say I have two variables, both optionals:
var a:Int? = 42
var b:Int? = 13
I have a condition where it's OK to proceed as long as these are not BOTH currently nil. I thoughtlessly put together something like:
guard let _ = a, let _ = b else { return }
I was absentmindedly thinking the conditions would be OR'ed, rather than AND'ed. Obviously that was wrong. The question then becomes, is there an idiomatic/preferred way to test that? Or do I just regress to the basics:
if a == nil && b == nil { return }
Aside
If I use the message extensions added by this post, then I might happily write something like
guard a.notNil || b.notNil else { return }
Which is about is close as I can come to "make certain (guard) that a is not nil or b is not nil"
A guard is an if, really, so you can do this the same way. This is clear and uses guard, which seems to be part of the fun. I present OR and AND options so you can pick one.
func doItOr(a: Int?, b:Int?) {
guard (a != nil || b != nil) else { return }
print("either A and B is not nil");
}
func doItAnd(a: Int?, b:Int?) {
guard (a != nil && b != nil) else { return }
print("both A and B are not nil");
}
doItOr(nil, b: nil)
doItOr(nil, b: 5)
doItOr(4, b: 5) // prints
doItAnd(nil, b: nil)
doItAnd(nil, b: 5)
doItAnd(nil, b: nil)
doItAnd(4, b: 5) // prints
it's OK to proceed as long as these are not BOTH currently nil
Your question poses two quite different possible conditions, so I will assume, for purposes of discussion, that you mean this one, namely "not both currently nil". Okay, then: "Not" is !. "Both" is &&. So, like this:
guard !(a == nil && b == nil) else {return}
If you really don't need to bind a or b as non-optionals or discern which of the two is non-nil, I believe the idiomatic approach would still be to use the if statement.
I generally use guard to narrow the specificity of my parameters by binding optionals, downcasting, etc.
You don't seem to be interested in doing that here. You just want to check them, so if seems to appropriately express that.
I have two different scenarios where I need to test the "optionality" of an optional type. I have not been able to figure how to explicitly test if the variable is a .None or a .Some other than with an unwieldy switch statement. How can I test for Someness with an if statement?
Scenario 1
I am writing an address formatter and my inputs are a number of String? types. In this example a simple test for (str != nil) will work. However, since my other need is when dealing with a 'double optional' and a nil test can't distinguish between .Some(.None) and .None a solution to this problem will solve that problem too.
Here's a version that works using a switch
let address1:String? = "123 Main St"
let address2:String? = nil
let apt:String? = "101"
let components = [address1, address2, apt].filter( { (c) -> Bool in
switch c {
case .Some: return true
case .None: return false
}
}).map { return $0! } //Had to map because casting directly to [String] crashes
print(", ".join(components)) //"123 Main St, 101"
What's I'd like to see is something like with an if:
let nice = ["123 Main St", nil, "303"].filter { (c) -> Bool in
return (c == .Some)
}
print(", ".join(nice))
Scenario 2
This is where a nil test won't work. If something is a String?? it can be any of .None, .Some(.None), or .Some(.Some(String)). In my case, the variable is carrying the recordID from an api call which might either be missing entirely (.None), a value (.Some(.Some("ABDEFG")), or explicitly NULL (.Some(.None)).
let teamNoneNone: String?? = .None
let teamSomeNone: String?? = .Some(.None)
let teamSomeSome: String?? = "My favorite local sportsball team"
if teamNoneNone == nil {
print("teamNoneNone is nil but is it .None? We don't know!") //prints
} else {
print("teamNoneNone is not nil")
}
if teamSomeNone == nil {
print("teamSomeNone is nil")
} else {
print("teamSomeNone is not nil but is it .Some(.None)? We don't know!") //prints
}
if teamSomeSome == nil {
print("teamSomeSome is nil but is it .None? We don't know!")
} else {
print("teamSomeSome is not nil but is it .Some(.None) or .Some(.Some())? We don't know!") //prints
}
Via another SO post I found a workaround like this, but it's not very clear what's happening to a casual reader:
if let team: String? = teamSomeNone {
print("teamSomeNone is Some(.None)") //prints
} else {
print("teamSomeNone is .Some(.Some())")
}
if let tests if a value is .None, and if it isn’t, it unwraps it and binds it to a local variable within an if statement.
Using switch with .Some and .None is really a secondary way of handling optionals, if if let doesn’t cut it. But it almost always does, especially now you can do multiple if lets in a single statement, following the latest release of Swift 1.2 to production.
Wanting to filter out the nils in a collection is a common-enough task that Haskell has a standard function for it, called catMaybe. Here’s a version, which I’ll call catSome, that would do the trick in Swift:
func catSome<T>(source: [T?]) -> [T] {
var result: [T] = []
// iterate over the values
for maybe in source {
// if this value isn’t nil, unwrap it
if let value = maybe {
// and append it to the array
result.append(value)
}
}
return result
}
let someStrings: [String?] = ["123 Main St", nil, "101"]
catSome(someStrings) // returns ["123 Main St", "101"]
Doubly-wrapped optionals are a bit of a pain, so the best solution is to avoid them in the first place – often, via use of optional chaining or flatMap.
But if you do find yourself with some, and all you care about is the inner value, you can unwrap them using a double if let:
// later parts of the let can rely on the earlier
if let outer = teamSomeSome, teamName = outer {
println("Fully unwrapped team is \(teamName)")
}
If you want to explicitly know if a double-optional has an inner nil inside an outer value, but isn’t nil itself, you can use if let with a where clause:
if let teamSomeMaybe = teamSomeNone where teamSomeMaybe == nil {
// this will be executed only if it was .Some(.None)
println("SomeNone")
}
The where clause is an extra conditional that can be applied to the unwrapped value.
I have this:
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(queue, {
if let data = NSURLConnection.sendSynchronousRequest(self.buildRequestForVenueLocation(location, identifier), returningResponse: &response, error: &error) {
let responseDictionary: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: .allZeros, error:&error) ?? error?.localizedDescription
dispatch_async(dispatch_get_main_queue(), {
completion(venues: responseDictionary, error: error)
})
} else {
println("There was a problem with the request...")
}
})
My question is regarding this line in particular:
let responseDictionary: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: .allZeros, error:&error) ?? error?.localizedDescription
Is this correct usage of nil coalescing?
According to Apple's documentation this is the equvivalent:
a != nil ? a! : b
If a is nil, b is returned, but does is matter if the b is an optional type?
EDIT
In case anyone was wondering, here's what it looks like when I call the function:
v.performVenueLocationRequest(l.location, identifier: "4d4b7105d754a06374d81259") {
if let result = $0 as? [String: AnyObject] ?? $1 {
println(result)
}
}
It depends what you mean by "matter". It will work. It just might not be what you want - if b is optional, then the result will also be optional i.e. even if a has a value it still won't be unwrapped:
let a: Int? = 2
let b: Int? = 3
let c = i ?? j
// c is {Some 2}
versus:
let i: Int? = nil
let j: Int? = 3
let k = i ?? j
// k is {Some 3}
See this article for probably more than you want to know about why, but in short: the result must be fixed as a specific type at compile time regardless of what the values of a and b are at runtime. Since b is an optional, a! is implicitly wrapped in an optional to make the two types compatible. Put it this way – if the result were not optional, and b were nil, what would the result be?
Bear in mind you can chain ?? so you could put a third value on the end that isn't optional that would make the result non-optional.
This is nil coalescing.
If b is not optional :
a ?? b will return unwrapped a, if a is not nil. If a is nil, it will return b.
If b is non optional :
a ?? b will return a, if a is not nil. If a is nil, it will return b.
This code will work.
But since you are capturing the error separately anyway, I would recommend leaving responseDictionary as nil instead of assigning it to error.localDescription(if it fails). It's just better design. Like below.
let responseDictionary: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: .allZeros, error:&error)