James
James

Reputation: 1302

Swift trouble with optionals

I'm a swift newbie and going round in circles trying to get optionals to work. I've done a lot of Googling, read the Apple Docs etc but I'm struggling with this (simplified) snippet from my ViewController:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if (segue.identifier == "segueToWebView") {
        // check what we're passing around
        print(sender!.urlString) // Optional("http://www.bbc.co.uk")

        // do some url validation etc
        var destUrl:String
        if sender!.urlString == nil{
            destUrl = "http://www.google.com"
        } else {
            destUrl = sender!.urlString // *** Error here ***
        }

        let targetWebViewController:GDWebViewController = segue.destinationViewController as! GDWebViewController
        targetWebViewController.loadURLWithString(destUrl)
        targetWebViewController.showsToolbar = true
        targetWebViewController.title = sender!.busName // this works correctly too (so isn't that a String?)
    }
}

The error is Cannot assign value of type 'String?!' to type 'String' on the line marked above. I have tried unwrapping it differently but just cant figure it out.

More code

The sender is a custom UIButton subclass with the following structure:

import UIKit

class TestButton: UIButton {
    var urlString: String?
    var busName: String?
}

I have set the variables to be optional as I don't know whether the button will/won't have those properties.

The method that is called when the button is tapped is this:

func tapTap(sender: TestButton) {
    print(sender.busName, sender.urlString) // Optional("My Button") Optional("http://www.bbc.co.uk")
    self.performSegueWithIdentifier("segueToWebView", sender: sender)
}

I know Swift optionals is a common stumbling block for newbies like me and there's plenty of docs about them but I can't figure it out so any help would be great.

Upvotes: 4

Views: 137

Answers (3)

Martin R
Martin R

Reputation: 539745

There are optionals on three levels:

  • The sender: AnyObject? parameter is an optional.
  • Sending arbitrary messages to AnyObject behaves similar to optional chaining and returns an implicitly unwrapped optional (compare The strange behaviour of Swift's AnyObject).
  • The var urlString: String? property is an optional.

To resolve the first two optionals, use optional binding/casting to the concrete button class:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if (segue.identifier == "segueToWebView") {
        if let button = sender as? TestButton {
           // The type of `button`  is `TestButton`
           // ...
        }
    }
}

Now

var destUrl: String
if button.urlString == nil {
    destUrl = "http://www.google.com"
} else {
    destUrl = button.urlString!
}

would compile, but that is better handled with the nil-coalescing operator:

let destUrl = button.urlString ?? "http://www.google.com"

Note that there is no forced unwrapping ! operator anymore!

Upvotes: 10

Nikolai Ruhe
Nikolai Ruhe

Reputation: 81868

You are manually testing the string for nil but then you forget to unwrap it:

var destUrl:String
if sender!.urlString == nil{
    destUrl = "http://www.google.com"
} else {
    destUrl = sender!.urlString!
}

(See the added exclamation mark.)

But unwrapping a string like this is actually more easy like this:

let destUrl = sender!.urlString ?? "http://www.google.com"

The ?? operator tests for nil, unwraps or returns the right hand side, all in one line.

Upvotes: 3

Ionut
Ionut

Reputation: 394

You just have to do something like this:

var destUrl:String
if let durl = sender!.urlString {
   destUrl = durl
} else {
   destUrl = "http://www.google.com"
}

The problem in your code is that destUrl is a String but sender!.urlString is an optional. So you can't assign one to the other. You have to unwrap the optional before assign it. Either force unwrap it (with !) either with if let. I recommend if let but it's up to you.

Upvotes: 3

Related Questions