Reputation: 1302
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
Reputation: 539745
There are optionals on three levels:
sender: AnyObject?
parameter is an optional.AnyObject
behaves similar to optional
chaining and returns an implicitly unwrapped optional
(compare The strange behaviour of Swift's AnyObject).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
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
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