I had this code in my Swift App
func parseJSON() {
let urlString = "www.websitethatlinkstoJSONfile.com"
if NSURL(string: urlString) == true {
let url = NSURL(string: urlString)
let data = try? NSData(contentsOfURL: url!, options: []) as NSData
let json = NSData(data: data!)
// more code
However, even though the link actually worked and was true, the if statement was never met and it kept skipping it and moving to else. So I changed the code to
if NSURL(string: urlString) != false
and it worked perfectly. I'm not sure why though?
As already explained in the other answers, comparing the optional
NSURL? against true or false is not what you want, and you should
use optional binding instead.
But why does it compile at all? And how can the result be interpreted?
In NSURL(string: urlString) == true, the left-hand side has the type
NSURL?, and NSURL is a subclass of NSObject.
There is a == operator taking two optional operands:
public func ==<T : Equatable>(lhs: T?, rhs: T?) -> Bool
The compiler uses the implicit conversion of Bool to NSNumber
to make that compile. So your code is equivalent to
if NSURL(string: urlString) == NSNumber(bool: true)
and that will always be false, and
if NSURL(string: urlString) != NSNumber(bool: false)
will always be true, simply because the left-hand side is not a number.
Here is a demonstration of the effect:
func foo(x: NSObject?) {
print(x == true, x == false)
}
foo(NSNumber(bool: true)) // true, false
foo(NSNumber(bool: false)) // false, true
foo(NSObject()) // false, false !!!
The last case is what you observed: Both x == true and x == false
return false.
For classes not inheriting from NSObject it would not compile:
class A { }
let a: A? = A()
if a == true { } // cannot convert value of type 'A?' to expected argument type 'Bool'
Remark: This is another argument for not comparing boolean values
against true or false, i.e.
if a == true && b == false { ... }
is better written as
if a && !b { ... }
Applied to your case, you would get a compiler error indicating the problem:
let urlString = "http://www.websitethatlinkstoJSONfile.com"
if NSURL(string: urlString) { }
// error: optional type '_' cannot be used as a boolean; test for '!= nil' instead
You don't really want to check for a boolean value when creating a NSURL, but rather make sure the NSURL you create is non-nil. Try wrapping it it in an if let statement like so to make sure whatever URL's you create are non-nil before executing further code.
let urlString = "www.websitethatlinkstoJSONfile.com"
if let url = NSURL(string: urlString) {
if let data = try? NSData(contentsOfURL: url, options: []) {
let json = NSData(data: data)
} else {
//Handle case where data is nil
}
} else {
//Handle case where url is nil
}
Using if let statements in this way makes sure that the NSURL and NSData objects you are creating are non-nil and valid objects and then you can add an else statement to them to handle cases where your url or data objects are nil. This will save you from unwanted crashes due to force unwrapping with the ! operator.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With