57

Before I upgraded to Swift 1.2, I could write the following line:

if let width = imageDetails["width"] as Int?

Now it forces me to write this line:

if let width = imageDetails["width"] as! Int?

My question is, if I'm forced to write it as above, couldn't I just write the code below and it would do the same thing? Would it give me the same result in all values of imageDetails?

if let width = imageDetails["width"] as Int
Alex
  • 663
  • 1
  • 8
  • 13

3 Answers3

71

The as keyword used to do both upcasts and downcasts:

// Before Swift 1.2
var aView: UIView = someView()

var object = aView as NSObject // upcast 

var specificView = aView as UITableView // downcast

The upcast, going from a derived class to a base class, can be checked at compile time and will never fail.

However, downcasts can fail since you can’t always be sure about the specific class. If you have a UIView, it’s possible it’s a UITableView or maybe a UIButton. If your downcast goes to the correct type – great! But if you happen to specify the wrong type, you’ll get a runtime error and the app will crash.

In Swift 1.2, downcasts must be either optional with as? or “forced failable” with as!. If you’re sure about the type, then you can force the cast with as! similar to how you would use an implicitly-unwrapped optional:

// After Swift 1.2
var aView: UIView = someView()

var tableView = aView as! UITableView

The exclamation point makes it absolutely clear that you know what you’re doing and that there’s a chance things will go terribly wrong if you’ve accidentally mixed up your types!

As always, as? with optional binding is the safest way to go:

// This isn't new to Swift 1.2, but is still the safest way
var aView: UIView = someView()

if let tableView = aView as? UITableView {
  // do something with tableView
}

Got this from a site: SOURCE

Eendje
  • 8,815
  • 1
  • 29
  • 31
  • Thanks Jacobson! One additional question - sometimes I get confused between downcasting to an Int versions "converting" to an Float. If you have an Integer value but you want to convert to a Float, would you downcast or convert? What about a Float with a integer value? (i.e. 2.0) – Alex Apr 16 '15 at 02:26
48

as

In Swift 1.2 and later, as can only be used for upcasting (or disambiguation) and pattern matching:

// 'as' for disambiguation
let width = 42 as CGFloat
let block = { x in x+1 } as Double -> Double
let something = 3 as Any?  // optional wrapper can also be added with 'as'


// 'as' for pattern matching
switch item {
case let obj as MyObject:
    // this code will be executed if item is of type MyObject
case let other as SomethingElse:
    // this code will be executed if item is of type SomethingElse
...
}

as?

The conditional cast operator as? tries to perform a conversion, but returns nil if it can't. Thus its result is optional.

let button = someView as? UIButton  // button's type is 'UIButton?'

if let label = (superview as? MyView)?.titleLabel {
    // ...
}

as!

The as! operator is for forced type conversion.

Use the forced form of the type cast operator (as!) 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.

// 'as!' for forced conversion.
// NOT RECOMMENDED.
let buttons = subviews as! [UIButton]  // will crash if not all subviews are UIButton
let label = subviews.first as! UILabel
jtbandes
  • 115,675
  • 35
  • 233
  • 266
  • 3
    Thanks @jtbandes. This answer is better than the accepted answer. – Junfeng Dec 28 '15 at 08:38
  • `as?` can also be used for upcasting (as well as downcasting), so it seems to me that having both `as` and `as?` for upcasting is redundant. Apple's Swift Guide doesn't even mention `as` for casting. – 7stud Jun 22 '16 at 08:47
  • 1
    In reply to 7stud, it makes a difference when you're doing something like typing a literal: taking the first example from the answer `let width = 42 as? CGFloat` produces an `Optional`, whereas `let width = 42 as CGFloat` is just a `CGFloat`. There's no good reason to use an optional there; the value is guaranteed present. – jscs Oct 14 '16 at 19:13
  • @JoshCaswell I'd expect a compiler warning there telling you the cast always succeeds. If not, maybe worth filing a bug :) – jtbandes Oct 14 '16 at 19:18
  • For the conditional cast, yes indeed, @jtbandes, I do get a warning. That's another good point, thanks. I still don't completely understand Swift casting, which led me to this answer, which was very helpful! – jscs Oct 14 '16 at 19:22
  • @JoshCaswell, It seems to me that `as!` does the exact same thing as `as` in your example. So, once again using `as` for casting seems superfluous. – 7stud Oct 19 '16 at 05:54
  • I was always confused as to where is `as` used. But this makes it clear. `as?` is optional. `as!` is force casting. But about `as`, that's for if it's a *specific* case, then do xyz, if not move on to another case...or just do `default`. It seems to be easier than doing multiple `if-let` + `else-if-let` right? – mfaani Oct 05 '17 at 18:07
0

The correct idiom that should do exactly what you want (in all versions of Swift at least upto and including 1.2) is the as? optional cast.

if let width = imageDetails["width"] as? Int

The optional cast returns an optional (Int? in this case) and is tested at runtime. Your original code probably forced a cast to the optional type.

Joseph Lord
  • 6,446
  • 1
  • 28
  • 32