Downcasting optionals in Swift: as? Type, or as! Type?

Given the following in Swift:

var optionalString: String?
let dict = NSDictionary()

What is the practical difference between the following two statements:

  • Downcasting in Swift with as and as?
  • protocol typed array can't be downcast to concrete type array
  • wrong generic type in swift
  • optionalString = dict.objectForKey("SomeKey") as? String
    

    vs

    optionalString = dict.objectForKey("SomeKey") as! String?
    

    9 Solutions Collect From Internet About “Downcasting optionals in Swift: as? Type, or as! Type?”

    The practical difference is this:

    var optionalString = dict.objectForKey("SomeKey") as? String
    

    optionalString will be a variable of type String?. If the underlying type is something other than a String this will harmlessly just assign nil to the optional.

    var optionalString = dict.objectForKey("SomeKey") as! String?
    

    This says, I know this thing is a String?. This too will result in optionalString being of type String?, but it will crash if the underlying type is something else.

    The first style is then used with if let to safely unwrap the optional:

    if let string = dict.objectForKey("SomeKey") as? String {
        // If I get here, I know that "SomeKey" is a valid key in the dictionary, I correctly
        // identified the type as String, and the value is now unwrapped and ready to use.  In
        // this case "string" has the type "String".
        println(string)
    }
    

    as? Type – means the down casting process is optional. The process can be successful or not(system will return nil if down casting fails).Any way will not crash if down casting fails.

    as! Type? – Here the process of down casting should be successfull(“!” indicates that) . The ending question mark indicates whether final result can be nil or not.

    More info regarding “!” and “?”

    Let us take 2 cases

    1)
    let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as? UITableViewCell.
    – here we don’t know whether the result of down casting of cell with identifier “Cell” to UITableViewCell is success or not. If unsuccessful then it returns nil( so we avoid crash here). Here we can do as given below.

    if let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as? UITableViewCell {
    //If we reached here it means the down casting was successfull
    }
    else {
    // unsuccessful down casting
    }
    

    So let us remember it like this – If “?” it means we are not sure whether value is nil or not(question mark comes when we don’t know things).

    2)

     let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! UITableViewCell. 
    
    • here we tell the compiler that down casting should be successful. If it fails the system will crash. So we give “!” when we are sure that value is non nil.

    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
    

    They are two different forms of Downcasting in Swift.

    (as?), which is know to be the Conditional Form, returns an optional value of the type you are trying to downcast to.

    You can use it when you are not sure if the downcast will succeed.
    This form of the operator will always return an optional value, and
    the value will be nil if the downcast was not possible. This enables
    you to check for a successful downcast.


    (as!), which is know to be the Forced Form, attempts the downcast and force-unwraps the result as a single compound action.

    You should use it ONLY when you are sure that the downcast will
    always succeed. This form of the operator will trigger a runtime
    error
    if you try to downcast to an incorrect class type.

    For more details, please check Type Casting section of Apple’s documentation.

    • as used for upcasting and type casting to bridged type
    • as? used for safe casting, return nil if failed
    • as! used to force casting, crash if failed

    Note:

    • as! can’t cast raw type to optional

    Examples:

    let 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.

    When we should use “?” and when “!”

    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

    Maybe this code example will help someone grok the principle:

    var dict = [Int:Any]()
    dict[1] = 15
    
    let x = dict[1] as? String
    print(x) // nil because dict[1] is an Int
    
    dict[2] = "Yo"
    
    let z = dict[2] as! String?
    print(z) // optional("Yo")
    let zz = dict[1] as! String // crashes because a forced downcast fails
    
    
    let m = dict[3] as! String?
    print(m) // nil. the forced downcast succeeds, but dict[3] has no value
    

    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.

    It may be easiest to remember the pattern for these operators in Swift as: ! implies “this might trap,” while ? indicates “this might be nil.”

    refer to:
    https://developer.apple.com/swift/blog/?id=23

    I am novice to Swift and writing this example trying to explain as i understand about ‘optionals’. If i am wrong please correct me.

    Thanks.


    class Optional {
    
        var lName:AnyObject! = "1"
    
        var lastName:String!
    }
    
    let obj = Optional()
    
    print(obj.lName)
    
    print(obj.lName!)
    
    obj.lastName = obj.lName as? String
    
    print(obj.lastName)
    

    (1) : obj.lastName = obj.lName as! String

    vs

    (2) : obj.lastName = obj.lName as? String

    Ans : (1) Here programmer is damm sure that “obj.lName” contains string type object. So just give that value to “obj.lastName”.

    Now, if programmer is correct means "obj.lName" is string type object, then no problem. “obj.lastName” will set to the same value.

    But if programmer is wrong means "obj.lName" is not string type object i.e. it contains some other type object like “NSNumber” etc. Then CRASH (Run Time Error).

    (2) Programmer is not sure that “obj.lName” contains string type object or any other type object. So set that value to “obj.lastName” if it is string type.

    Now, if programmer is correct means “obj.lName” is string type object, then no problem. “obj.lastName” will set to the same value.

    But if programmer is wrong means obj.lName is not string type object i.e. it contains some other type object like "NSNumber" etc. Then “obj.lastName” will set to the nil value. So, No Crash (Happy:)