Swift changed a lot with Xcode 6.3. I had to replace dozens of places in each of my app as
-> as!
. Why, what are the rules now?
Prior to Swift 1.2, the as
operator could be used to carry out two different kinds of conversion, depending on the type of expression being converted and the type it was being converted to:
Guaranteed conversion of a value of one type to another, whose success can be verified by the Swift compiler. For example, upcasting (i.e., converting from a class to one of its superclasses) or specifying the type of a literal expression, (e.g., 1 as Float).
Forced conversion of one value to another, whose safety cannot be guaranteed by the Swift compiler and which may cause a runtime trap. For example downcasting, converting from a class to one of its subclasses.
Swift 1.2 separates the notions of guaranteed conversion and forced conversion into two distinct operators. Guaranteed conversion is still performed with the as
operator, but forced conversion now uses the as!
operator. The !
is meant to indicate that the conversion may fail. This way, you know at a glance which conversions may cause the program to crash.
Source: https://developer.apple.com/swift/blog/?id=23
According to the release notes:
The notions of guaranteed conversion and “forced failable” conversion are now separated into two operators. Forced failable conversion now uses the as! operator. The ! makes it clear to readers of code that the cast may fail and produce a runtime error. The “as” operator remains for upcasts (e.g. “someDerivedValue as Base”) and type annotations (“0 as Int8”) which are guaranteed to never fail.
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)
}