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
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)
}
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.
Related
I go over some code written by other developers and now and then I encounter signatures like this one:
func didReceiveLogMessage(_ message: String!)
Would it be safe to convert this parameter type to String instead of String! ?
Basically never declare a custom function parameter as implicit unwrapped optional. Don't.
This is a pre Swift 3 legacy syntax only for Objective-C and Core Foundation compatibility.
It's absolutely safe and even highly recommended to remove the exclamation mark. However if you really need an optional use a regular optional (?)
This gives no value. It actually adds complexity as even an Inmplicitly unwrapped optional as such counts as an optional, meaning it can be nil (but will crash). a regular String can't be nil
In Swift, I would expect the following to be a perfectly valid code:
let snailCharacter: Character = "🐌"
let snailString = snailCharacter as String
But apparently, it produces an error:
Cannot convert value of type 'Character' to type 'String' in coercion
Solution to this is to use String initializer like this:
let snailString = String(snailCharacter)
I thought Character is kind of a subset of String, so that surprised me. Why is it forbidden to cast Character to String?
I'm using Swift 4 in Xcode 9 beta 4.
Looking at the documentation, you can see, that Character is just one of the several representations of String in Swift (emphasis added to the relevant parts by me)
A string is a series of characters, such as "hello, world" or
"albatross". Swift strings are represented by the String type. The
contents of a String can be accessed in various ways, including as a
collection of Character values.
In Swift, String is not just an array of Characters unlike in some other languages. In Swift, Character is just a way to represent a String instance in a certain way. Strings can be represented using Views, such as CharacterView, utf8View, etc.
One of the key principles behind the architecture of Swift's String type was Unicode correctness, which is one of the reasons Strings are not just simply an array of Characters.
For more information about the changes to String in Swift4, see the String Manifesto.
To be more specific about why casting doesn't work. There are two kinds of castings, type casting and bridge-casting. Type casting is only possible between classes, where inheritance is involved. You can either upcast a subclass to its superclass, which always succeeds or you can try to downcast a superclass to a subclass, which only works if a subclass instance was first upcasted to its superclass.
It should be quite clear from the above explanation why type casting doesn't work between Character and String, since the neither of the two types inherit from each other.
For bridge casting, this is a method Apple introduced for interoperability between some Swift and Foundation types, such as String and NSString, but since both String and Character are Swift types, bridge casting has nothing to do with this problem either.
First you need a textual representation of that. You can convert to String only the description of a Character like this
let snailCharacter: Character = "🐌"
let snailString = snailCharacter.description as String
I am attempting to cast a variable to a different type that is held in a variable or is the return type of a function. Here is the idea:
let i = 1 as type(of: 3.14)
But when I do this, I get a couple of errors:
Use of undeclared type 'type'
Consecutive statements on a line must be separated by ';' (This inserts a semi-colon between type and (of: 3.14))
And a warning:
Expression of type (of: Double) is unused
How to I cast a value to a type that is held in a variable?
Swift (currently) requires the Type assignment at compile time. You can do some things like this, but you will need to write a converter for each type combination you want to use, e.g:
func convertType(from item: Int) -> Float {
return Float(item)
}
var item: Float = convertType(from: 1)
I would caution going down this road and try and get used to Swift's way of doing things. If you absolutely need it you should be able to use some generic functions with a protocol like FloatConvertable to handle this more simply.
The Swift grammar doesn't allow for these types of expressions. The expression after as must be the name of a type, ie. 1 as Double (though, as vadian points out, you can't cast numeric types to each other, so a better example would be mySubClassObject as MySuperClass). Swift is a strongly typed language, which means it needs to know the types of all variables at compile time.
I want to pass an optional dictionary parameter in Swift function, I tried declaring the function as
func myFunction(_ type:Int, params:Dictionary<String,Any?>?)
and also
func myFunc(_ type:Int, params:[String:Any?]?)
I get warning "Expression implicitly coerced from Any? to Any" with first declaration when I try to pass a dictionary but not with second. I need to know what is the difference between the two and why the warning. FYI, here is how I pass the dictionary in both cases:
myFunc(1, params:["key1": value1, "key2": value2])
According to Swift documentation, they are identical:
The type of a Swift dictionary is written in full as Dictionary<Key,
Value>, where Key is the type of value that can be used as a
dictionary key, and Value is the type of value that the dictionary
stores for those keys.
You can also write the type of a dictionary in shorthand form as [Key:
Value]. Although the two forms are functionally identical, the
shorthand form is preferred and is used throughout this guide when
referring to the type of a dictionary.
I'm using Xcode Version 8.0 and i couldn't regenerate your issue.
They are just different syntactic sugar for dictionary declaration and one type will not work in all cases which are kind of a bug.
For Example,
var msSet = [Vertex<Int> : Double]()
I have asked a question related to it.
Swift Dictionary Initialization of custom type gives: '>' is not a postfix unary operator error
I'm confused about dynamic type checking in Swift.
Specifically, I have a weirdo case where I want to, essentially, write (or find) a function:
func isInstanceOf(obj: Any, type: Any.Type) -> Bool
In Objective-C, this is isKindOfClass, but that won't work because Any.Type includes Swift structs, which are not classes (much less NSObject subclasses).
I can't use Swift is here, because that requires a hardcoded type.
I can't use obj.dynamicType == type because that would ignore subclasses.
The Swift book seems to suggest that this information is lost, and not available for structs at all:
Classes have additional capabilities that structures do not:
...
Type casting enables you to check and interpret the type of a class instance at runtime.
(On the Type Casting chapter, it says "Type casting in Swift is implemented with the is and as operators", so it seems to be a broader definition of "type casting" than in other languages.)
However, it can't be true that is/as don't work with structures, since you can put Strings and Ints into an [Any], and pull them out later, and use is String or is Int to figure out what they were. The Type Casting chapter of the Swift Book does exactly this!
Is there something that's as powerful as isKindOfClass but for any Swift instances? This information still must exist at runtime, right?
Actually you can use is operator.
Use the type check operator (is) to check whether an instance is of a certain subclass type. The type check operator returns true if the instance is of that subclass type and false if it is not.
Since struct can't be subclassed, is is guaranteed to be consistent when applied to instance of struct because it will check on it static type, and for classes it will query the dynamic type in runtime.
func `is`<T>(instance: Any, of kind: T.Type) -> Bool{
return instance is T;
}
This work for both, struct and class.
As already stated, is/as should work fine with structs. Other corner cases can usually be done with generic functions, something like:
let thing: Any = "hi" as Any
func item<T: Any>(_ item: Any, isType: T.Type) -> Bool {
return (item as? T) != nil
}
print(item(thing, isType: String.self)) // prints "true"
No idea if this fits your specific use case.
More generally, keep in mind that Swift (currently) requires knowing the type of everything at compile time. If you can't define the specific type that will be passed into a function or set as a variable, neither will the compiler.