Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift trouble with optionals

Tags:

ios

swift

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.

like image 960
James Avatar asked Dec 04 '22 00:12

James


1 Answers

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!

like image 87
Martin R Avatar answered Dec 16 '22 20:12

Martin R