Given the following in Swift:
var optionalString: String?
let dict = NSDictionary()
What is the practical difference between the following
as
used for upcasting and type casting to bridged typeas?
used for safe casting, return nil if failedas!
used to force casting, crash if failedNote:
as!
can’t cast raw type to optionallet rawString: AnyObject = "I love swift"
let optionalString: AnyObject? = "we love swift"
let nilString: AnyObject? = (nil as String?)
let rawInt: AnyObject = Int(3)
let optionalInt: AnyObject? = Int(3)
let nilInt: AnyObject? = (nil as Int?)
Example
var age: Int? = nil
var height: Int? = 180
By adding a ? immediately after the data type you tell the compiler that the variable might contain a number or not. Neat! Notice that it doesn’t really make sense to define Optional constants – you can set their value only once and therefore you would be able to say whether their value will be nil or not.
let’s say we have UIKit based simple app. we have some code in our view controller and wants to present a new view controller on top of it. and we need to decide to push the new view on screen using navigation controller.
As we know every ViewController instance has a property navigation controller. If you are building a navigation controller based app this property of your app’s master view controller is set automatically and you can use it to push or pop view controllers. If you use a single app project template – there won’t be a navigation controller created automatically for you, so your app’s default view controller will not have anything stored in the navigationController property.
I’m sure you already guessed that this is exactly a case for an Optional datatype. If you check UIViewController you will see that the property is defined as:
var navigationController: UINavigationController? { get }
So let’s go back to our use case. If you know for a fact that your view controller will always have a navigation controller you can go ahead and force unwrap it:
controller.navigationController!.pushViewController(myViewController, animated: true)
When you put a ! behind the property name you tell the compiler I don’t care that this property is optional, I know that when this code executes there always will be a value store so treat this Optional like a normal datatype. Well isn’t that nice? What would happen though if there isn’t a navigation controller to your view controller? If you suggestion that there always will be a value stored in navigationController was wrong? Your app will crash. Simple and ugly as that.
So, use ! only if you are 101% sure that this is safe.
How about if you aren’t sure that there always will be a navigation controller? Then you can use ? instead of a !:
controller.navigationController?.pushViewController(myViewController, animated: true)
What the ? behind the property name tells the compiler is I don’t know whether this property contains nil or a value, so: if it has value use it, and oterwise just consider the whole expression nil. Effectively the ? allows you to use that property just in the case there is a navigation controller. No if checks of any kind or castings of any sort. This syntax is perfect when you don’t care whether you have a navigation controller or not, and want to do something only if there is.
Huge thanks to Fantageek
The first is a "conditional cast" (look under "type-casting operators" in the documentation I've linked). If the cast succeeds, the value of the expression is wrapped in an optional and returned, otherwise the value returned is nil.
The second means that optionalString could be a string object or it might be nil.
More information found in this related question.
To clarify what vacawama said, here is an example...
Swift 3.0:
import UIKit
let str_value: Any = String("abc")!
let strOpt_value: Any? = String("abc")!
let strOpt_nil: Any? = (nil as String?)
let int_value: Any = Int(1)
let intOpt_value: Any? = Int(1)
let intOpt_nil: Any? = (nil as Int?)
// as String
//str_value as String // Compile-Time Error: 'Any' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//strOpt_value as String // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//strOpt_nil as String // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//int_value as String // Compile-Time Error: 'Any' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//intOpt_value as String // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//intOpt_nil as String // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
// as? String
str_value as? String // == "abc"
strOpt_value as? String // == "abc"
strOpt_nil as? String // == nil
int_value as? String // == nil
intOpt_value as? String // == nil
intOpt_nil as? String // == nil
// as! String
str_value as! String // == "abc"
strOpt_value as! String // == "abc"
//strOpt_nil as! String // Run-Time Error: unexpectedly found nil while unwrapping an Optional value.
//int_value as! String // Run-Time Error: Could not cast value of type 'Swift.Int' to 'Swift.String'.
//intOpt_value as! String // Run-Time Error: Could not cast value of type 'Swift.Int' to 'Swift.String'.
//intOpt_nil as! String // Run-Time Error: unexpectedly found nil while unwrapping an Optional value.
// as String?
//str_value as String? // Compile-Time Error: cannot convert value of type 'Any' to type 'String?' in coercion
//strOpt_value as String? // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?
//strOpt_nil as String? // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?
//int_value as String? // Compile-Time Error: cannot convert value of type 'Any' to type 'String?' in coercion
//intOpt_value as String? // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?
//intOpt_nil as String? // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?
// as? String?
//str_value as? String? // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
strOpt_value as? String? // == "abc"
strOpt_nil as? String? // == nil
//int_value as? String? // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
intOpt_value as? String? // == nil
intOpt_nil as? String? // == nil
// as! String?
//str_value as! String? // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
strOpt_value as! String? // == "abc"
strOpt_nil as! String? // == nil
//int_value as! String? // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
//intOpt_value as! String? // Run-Time Error: Could not cast value of type 'Swift.Int' to 'Swift.String'.
intOpt_nil as! String? // == nil
// let _ = ... as String
//if let _ = str_value as String { true } // Compile-Time Error: 'Any' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//if let _ = strOpt_value as String { true } // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//if let _ = strOpt_nil as String { true } // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//if let _ = int_value as String { true } // Compile-Time Error: 'Any' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//if let _ = intOpt_value as String { true } // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//if let _ = intOpt_nil as String { true } // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
// let _ = ... as? String
if let _ = str_value as? String { true } // true
if let _ = strOpt_value as? String { true } // true
if let _ = strOpt_nil as? String { true } // false
if let _ = int_value as? String { true } // false
if let _ = intOpt_value as? String { true } // false
if let _ = intOpt_nil as? String { true } // false
// let _ = ... as! String
//if let _ = str_value as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String'
//if let _ = strOpt_value as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String'
//if let _ = strOpt_nil as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String'
//if let _ = int_value as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String'
//if let _ = intOpt_value as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String'
//if let _ = intOpt_nil as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String'
// let _ = ... as String?
//if let _ = str_value as String? { true } // Compile-Time Error: cannot convert value of type 'Any' to type 'String?' in coercion
//if let _ = strOpt_value as String? { true } // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?
//if let _ = strOpt_nil as String? { true } // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?
//if let _ = int_value as String? { true } // Compile-Time Error: cannot convert value of type 'Any' to type 'String?' in coercion
//if let _ = intOpt_value as String? { true } // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?
//if let _ = intOpt_nil as String? { true } // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?
// let _ = ... as? String?
//if let _ = str_value as? String? { true } // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
if let _ = strOpt_value as? String? { true } // true
if let _ = strOpt_nil as? String? { true } // true
//if let _ = int_value as? String? { true } // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
if let _ = intOpt_value as? String? { true } // false
if let _ = intOpt_nil as? String? { true } // true
// let _ = ... as! String?
//if let _ = str_value as! String? { true } // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
if let _ = strOpt_value as! String? { true } // true
if let _ = strOpt_nil as! String? { true } // false
//if let _ = int_value as! String? { true } // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
//if let _ = intOpt_value as! String? { true } // Run-Time Error: Could not cast value of type 'Swift.Int' to 'Swift.String'.
if let _ = intOpt_nil as! String? { true } // false
Swift 2.0:
import UIKit
let str: AnyObject = String("abc")
let strOpt: AnyObject? = String("abc")
let strNil: AnyObject? = (nil as String?)
let int: AnyObject = Int(1)
let intOpt: AnyObject? = Int(1)
let intNil: AnyObject? = (nil as Int?)
str as? String // == "abc"
strOpt as? String // == "abc"
strNil as? String // == nil
int as? String // == nil
intOpt as? String // == nil
intNil as? String // == nil
str as! String? // Compile-Time Error: Cannot downcast from 'AnyObject' to a more optional type 'String?'
strOpt as! String? // == "abc"
strNil as! String? // == nil
int as! String? // Compile-Time Error: Cannot downcast from 'AnyObject' to a more optional type 'String?'
intOpt as! String? // Run-Time Error: Could not cast value of type '__NSCFNumber' to 'NSString'
intNil as! String? // == nil