user4258987
user4258987

Reputation:

Why does this code work when an optional is added?

In a program I'm making, Im trying to access web content to display the weather. I'm struggling with the concept of optionals and I'm wondering why this line of code works when a "?" is added, but not without.

   @IBAction func pressedSearch(sender: AnyObject) {

        var urlString = "http://www.weather-forecast.com/locations/" + cityField.text.stringByReplacingOccurrencesOfString(" ", withString: "") + "/forecasts/latest"

        var url = NSURL(string: urlString)

        println(urlString)

        let task = NSURLSession.sharedSession().dataTaskWithURL(url!) { (data, response, error) in

            var urlContent = NSString(data: data, encoding: NSUTF8StringEncoding)

            var contentArray = urlContent?.componentsSeparatedByString("<span class=\"phrase\">") as Array<String>

            println(contentArray[1])

        }
        task.resume()
    }

Here is the line of code where the "?" was added to make the code work:

var contentArray = urlContent?.componentsSeparatedByString("<span class=\"phrase\">") as Array<String>

What does the "?" do to fix the error, and why do we have to add "?" and "!" to some variables and not others throughout swift?

Upvotes: 1

Views: 77

Answers (4)

matt
matt

Reputation: 536047

The problem is that in this line:

var urlContent = NSString(data: data, encoding: NSUTF8StringEncoding)

...you are calling a method that is a failable initializer in Objective-C/Cocoa. It can return nil to indicate failure (e.g. because that data couldn't be decoded using that encoding).

Therefore, in Swift, if there is no failure, this method returns an Optional wrapping a string (because only when there is an Optional can nil be used in Swift).

Therefore, you must thereafter treat it as an Optional - because Optional wrapping a String is not a String - it is an Optional. Thus, you unwrap to access the String.

Upvotes: 1

Aaron Rasmussen
Aaron Rasmussen

Reputation: 13316

Your line of code is an example of "optional chaining". It is used when you try to access a method or a property of a variable that might be nil. Take this as an example:

class Person {
    let firstName = "John"
    var lastName: String? = nil
}

let person = Person()

Now suppose you try to perform an operation on person's last name...you want an uppercase version of it, or something. Your code might look like this:

let ucLastName = person.lastName.uppercaseString

But lastName is nil. You can't do anything with it because it doesn't exist. If the code above were able to compile, it would crash the program when you ran it.

To prevent the crash, the compiler requires you to insert a ? after lastName, like this:

let ucLastName = person.lastName?.uppercaseString

That will compile. Why? Because inserting the ? tells the compiler to ignore everything after the ? if lastName is nil and return nil to ucLastName - the uppercaseString property is never even accessed once the compiler determines that lastName == nil.

In your case, the variable urlContent is an optional - it is akin to lastName in my example. You may not realize it is an optional, because you didn't declare it as one, but it is, because it was returned by a function, NSString(data: data, encoding: NSUTF8StringEncoding) that returns an optional as a result. So even if you didn't realize it was optional, the compiler knew it, and told you so by requiring you to insert the ? value in the code.

Requiring the ? is good because:

  • it reduces the number of runtime crashes that would occur when you try to access properties or methods of nil variables
  • it requires you the programmer, to be aware of the fact that you are dealing with a value that might be nil. You are on notice that a runtime crash is coming if you handle it wrong.

Consider what would happen if you weren't required to insert the ?. Your program would crash at runtime, and you would have to debug it by searching through the code for something that went wrong, and sometimes very few clues as to what happened. The ?, and the compiler warnings that go along with it, allow you to solve the problem at compile time, instead of debugging runtime crashes.

Upvotes: 0

Ian MacDonald
Ian MacDonald

Reputation: 14040

Here's some code to help you understand ? and ! better (including what you're telling the machine):

var foo = resultOfAFunctionThatReturnsAQuestionMarkValue()
foo.accessFunction() // "<silence>..."
                     // this could break if foo is nil

var bar = resultOfAFunctionThatReturnsAQuestionMarkValue()
bar?.accessFunction() // "Hey, bro. This might be nil.
                      //       If it is, just ignore the line."
                      // accessFunction() might not be called,
                      // but it won't explode.

var asdf = resultOfAFunctionThatReturnsAQuestionMarkValue()
asdf!.accessFunction() // "Yo, dude. I know for sure this is not nil.
                       //      Don't bother checking first. It's cool."
                       // this could break if asdf if nil
                       // You just lied to the computer.
                       //            (They will remember.)

Upvotes: 2

Kalzem
Kalzem

Reputation: 7501

From the official documentation

Return Value

An NSString object initialized by converting the bytes in data into Unicode characters using encoding. The returned object may be different from the original receiver. Returns nil if the initialization fails for some reason (for example if data does not represent valid data for encoding).

Using NSString(data:, encoding:) doesn't always return a NSString, it can return nil. This is why the type of urlContent is NSString? instead of NSString. Therefore, you need that question mark in the next line.

For more info about ? and !, see here : What is an optional value in Swift?

TL;DR : "?" implies a variable can be nil, if you want to access it, ignoring that nil possibility, you need "!"

Upvotes: 0

Related Questions