I'm trying to understand of as typecasting.
Reading Type Casting chapter on Apple Documentation, I've two syntax for downcasting (as? and as! operators) but I didn't find anything about as.
So I thought that I should never have used this kink of operator but yesterday when I was typing code with do-try-catch statement, I met this one:
catch let error as NSError {
print(error)
}
Initially, error was type conforms to the Error protocol.
Now using the as NSError casting, it has become an instance of a NSError class.
But my question is: What does as operator do?
It's not a downcasting sure.. Could it be used for "convert" object?
EDIT
I don't think it's a duplicate.
In my case, the error variable, is not a class and doesn't inherit from a super class so I have not a upcasting. It is not even a pattern matching.
I've already read both Swift Blog page and this thread on StackOverflow.
EDIT 2
From Swift Blog
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.
The text above doesn't work for me because if I tried to use the as! operator instead of as, compiler complain me.
EDIT 3
Even in Using Swift with Cocoa and Obj-C documentation they use the let-as? syntax for checking and casting to a protocol.
So, why in my case, I can't use it?
First of all, as suggested in the dasblinkenlight's comment, your code snippet is not using a type-casting-operator. Check the syntax of do-statement and you can find these:
catch-clause → catch patternopt where-clauseopt code-block
pattern → value-binding-pattern
value-binding-pattern → var pattern | let pattern
pattern → type-casting-pattern
type-casting-pattern → is-pattern | as-pattern
as-pattern → pattern as type
So, your EDIT 2 has no meaning, there are no syntax accepting as! in catch-clause.
But this code (using type-casting-operator) works, so I try to explain how to use as-casting.
enum MyError: Error {
case bad
//...
}
let error: Error = MyError.bad
let nsError = error as NSError
As shown in the linked article in the EI Captain v2.0's comment, as-casting is used for Guaranteed conversion. I have collected some use cases of such conversions.
Upcasting
class Animal {}
class Dog: Animal {}
let d = Dog()
d as Animal // upcast succeeds
As shown in the article, upcasting always succeeds, so you can use as.
Specifying literal type
let byte = 123 as UInt8
let ch = "a" as UnicodeScalar
In Swift, literals are typeless, so you can use as to specify the types of literals
In case Swift can infer the type of the literal, you can omit such as-casting:
let byte: UInt8 = 123
let ch: UnicodeScalar = "a"
Disambiguating overloaded methods
class MyClass {
func aMethod(_ arg: String) {
print(arg)
}
func aMethod(_ arg: Int) {
print("\"\(arg)\"")
}
}
let obj = MyClass()
let theFunc = obj.aMethod as (String)->Void
theFunc("abc") //->abc
Always-succeeds bridging
let str: String = "a String"
let nsStr = str as NSString
let intArr: [Int] = [1,2,3]
let nsArr = intArr as NSArray
The example let nsError = error as NSError is included in this category, and you need to read this article carefully, to understand why this is an always-succeeds bridging.
For your EDIT 3.
You may need to distinguish these two syntaxes:
let a: Any = 1
//Using if-let -- Optional binding, `(a as? Int)` is an expression using type-casting-operator which generates an Optional result
if let intA = (a as? Int) {
print("\(intA) is Int")
}
//Using if-case -- pattern matching, `(let intA as Int)` is a pattern using as-pattern
if case (let intA as Int) = a {
print("\(intA) is Int")
}
As already noted, catch leads a pattern, and you cannot use as? in pattern.
as
Use as for types that Apple has done some work to handle the conversion in the background. These are usually Foundation types that Apple bridged into Swift and want you to have a quick way to convert back and forth from their ObjC equivalents, for example:
String <-> NSString
URL <-> NSURL
Array <-> NSArray
Data <-> NSData
These casts always succeed and Xcode will warn you if you use as? or as!. In your specific case, Apple has done some meddling in the background to make the Error protocol and NSError to be castable to/from each other.
as!
Use as! when you know the object is castable to another type. as! will crash your app if the object is not castable (for example, when sender is actually a UITextField)
let button = sender as! UIButton // you are sure that the sender is always
// a UIButton or your app will crash
as?
Use as? when you not sure if the object is castable to the other type. In practice, this should be your preferred method and you should optional binding to check for success. as? produces nil if the object is not castable:
// Exit the function if the sender is not a UIButton
guard let sender = sender as? UIButton else {
return
}
// Only execute the block if the sender is UIButton
if let button = sender as? UIButton {
// ...
}
Related
What's the equivalent of Obj.C's NSMutableDictionary<Class, Class> in Swift?
I tried:
var dictionary = [AnyClass: AnyClass]()
However this throws the error: Type 'AnyClass' (aka 'AnyObject.Type') does not conform to protocol 'Hashable'
Since there can only be one class per name, we know a given class reference refers to a unique class; there is only one "String" in a given namespace. So why is this not hashable?
I also tried:
var dictionary = NSMutableDictionary<AnyClass, AnyClass>
However this also fails with: Cannot specialize non-generic type 'NSMutableDictionary'
I thought Swift was supposed to be type-safe, but here the compiler is encouraging me to just throw anything into this NSMutableDictionary without type-checking it to make sure it's an AnyClass!
Also, DO NOT lecture me about "You shouldn't be doing that in the first place," because, I am not doing it, it's already like that in some Objective C code that I am required to translate into Swift. I am simply trying to do it in the best possible way—if it means I must resort to a non-type-safe NSMutableDictionary, then so be it, however this seems ridiculous.
Surely I'm missing something here... what am I missing?
The closest you can get in Swift, I discovered, is to do this:
var classToClassMapping = Dictionary<ObjectIdentifier, AnyClass>()
extension Dictionary where Key == ObjectIdentifier, Value == AnyClass {
subscript<T>(keyType: T.Type) -> AnyClass? {
get {
let id = ObjectIdentifier(keyType)
return self[id]
}
set {
let id = ObjectIdentifier(keyType)
self[id] = newValue
}
}
}
classToClassMapping[Yay.self] = NSString.self
if let stringClass = classToClassMapping[Yay.self] as? NSString.Type {
print(stringClass.init(string: "hell yeah"))
}
// Prints "hell yeah"
// Alternative:
switch classToClassMapping[Yay.self] {
case let val as NSString.Type:
print(val.init(string: "yaiirrr boy"))
default:
print("woops")
}
// prints "yaiirrr boy"
Works perfectly for my needs! (Using Swift 5.1 here)
I’m learning Swift. How do I fix the following code to list the window names?
import CoreGraphics
let windows = CGWindowListCopyWindowInfo(CGWindowListOption.optionAll, kCGNullWindowID)
for i in 0..<CFArrayGetCount(windows) {
if let window = CFArrayGetValueAtIndex(windows, i) {
print(CFDictionaryGetValue(window, kCGWindowName))
}
}
The error:
main.swift:6:32: error: cannot convert value of type 'UnsafeRawPointer' to expected argument type 'CFDictionary?'
print(CFDictionaryGetValue(window, kCGWindowName))
^~~~~~
as! CFDictionary
It becomes easier if you avoid using the Core Foundation types and methods, and bridge the values to native Swift types as early as possible.
Here, CGWindowListCopyWindowInfo() returns an optional CFArray of CFDictionaries, and that can be bridged to the corresponding Swift type [[String : Any]]. Then you can access its values with the usual Swift methods (array enumeration and dictionary subscripting):
if let windowInfo = CGWindowListCopyWindowInfo(.optionAll, kCGNullWindowID) as? [[ String : Any]] {
for windowDict in windowInfo {
if let windowName = windowDict[kCGWindowName as String] as? String {
print(windowName)
}
}
}
You can use unsafeBitCast(_:to:) to convert the opaque raw pointer to a CFDictionary. Note that you'll also need to convert the second parameter, to a raw pointer:
CFDictionaryGetValue(unsafeBitCast(window, to: CFDictionary.self), unsafeBitCast(kCGWindowName, to: UnsafeRawPointer.self))
unsafeBitCast(_:to:) tells the compiler to treat that variable as another type, however it's not very safe (thus the unsafe prefix), recommending to read the documentation for more details, especially the following note:
Warning
Calling this function breaks the guarantees of the Swift type system; use with extreme care.
In your particular case there should not be any problems using the function, since you're working with the appropriate types, as declared in the documentation of the Foundation functions you're calling.
Complete, workable code could look something like this:
import CoreGraphics
let windows = CGWindowListCopyWindowInfo(CGWindowListOption.optionAll, kCGNullWindowID)
for i in 0..<CFArrayGetCount(windows) {
let windowDict = unsafeBitCast(CFArrayGetValueAtIndex(windows, i), to: CFDictionary.self)
let rawWindowNameKey = unsafeBitCast(kCGWindowName, to: UnsafeRawPointer.self)
let rawWindowName = CFDictionaryGetValue(windowDict, rawWindowNameKey)
let windowName = unsafeBitCast(rawWindowName, to: CFString?.self) as String?
print(windowName ?? "")
}
Update
You can bring the CoreFoundation array sooner to the Swift world by casting right from the start:
let windows = CGWindowListCopyWindowInfo(CGWindowListOption.optionAll, kCGNullWindowID) as? [[AnyHashable: Any]]
windows?.forEach { window in
print(window[kCGWindowName])
}
The code is much readable, however it might pose performance problems, as the cast to [[AnyHashable: Any]]` can be expensive for large array consisting of large dictionaries.
Here is my swift 2.X code that does'nt work any more on swift 3 :
var dictThemesNamesStyles=[String:[Int:Int]]()
self.styles=dictThemesNamesStyles
let keysArray:Array=Array(self.styles.keys)
let sortedKeysArray = keysArray.sorted(by:
{
(str1: NSObject, str2: NSObject) -> Bool in
return Int((str1 as! String))<Int((str2 as! String))
})
self.stylesLevel1Keys=sortedKeysArray
self.styleThemesPickerView.reloadAllComponents()
On line :
"return Int((str1 as! String)) < Int((str2 as! String))"
it complains with the error : "Type of expression is ambiguous without more context"
What do I have to change in this code to make it work ?
Thanks a lot.
Let's go through this line by line:
var dictThemesNamesStyles=[String:[Int:Int]]()
self.styles=dictThemesNamesStyles
Okay, we've got a dictionary of strings to dictionaries of integers.
let keysArray:Array=Array(self.styles.keys)
There are a few problems with this line:
The declaration of :Array without a generic parameter.
The type declaration is unnecessary, since the type system already knows this is an Array, since you're calling Array's initializer.
Creating this whole Array is unnecessary, since we're just passing the result to sorted, which already exists on the collection returned by keys, and which will return an Array. Creating the array is therefore a needless performance hit which we should avoid.
I would, in fact, delete this entire line, and just replace keysArray with self.styles.keys in the next line:
let sortedKeysArray = self.styles.keys.sorted(by:
Next:
{
(str1: NSObject, str2: NSObject) -> Bool in
return Int((str1 as! String))<Int((str2 as! String))
Okay, we've got a few problems here.
str1 and str2 are declared as NSObject, when they are in fact Strings.
Consequently, the as! String casts are unnecessary.
Int(String) returns an Optional, so you need to take into account the case where the result may be nil. I'd just provide a default value; probably 0, although you could also use a guard statement to throw an error if you prefer.
In general, there's a lot of verbosity here. This whole closure can actually be succinctly written as a one-liner:
let sortedKeysArray = self.styles.keys.sorted { (Int($0) ?? 0) < (Int($1) ?? 0) }
Anyway, take care of these issues, and your code will compile.
That all seems pretty complex. You can simplify this quite a bit:
self.styles = [String: [Int: Int]]()
self.stylesLevel1Keys= self.styles.keys.sorted { Int($0)! < Int($1)! }
self.styleThemesPickerView.reloadAllComponents()
I am bit confused with typecasting in Swift.
Have a small doubt.
What is the difference between as?,as! and only as.
And can we say "as" is similar to is.
Thanks in advance.
The 'as' keyword is used for casting.
'as' example:
let calcVC = destinationViewController as CalculatorViewController
This line casts the destinationViewController to a CalculatorViewController. However, this would crash if destinationViewController was not a CalculatorViewController or a subclass thereof.
To protect against a crash, you can use 'if let' with 'as?'...
'as?' example:
if let calcVC = destinationViewController as? CalculatorViewController {
// ... write code to execute if destinationViewController is in fact a CalculatorViewController
}
You can even check before you even try to do 'as' with the 'is' keyword...
'is' example:
if destinationViewController is CalculatorViewController {
//...
}
is is used to check the type of a value whereas as is used to cast a value to a different type.
The conditional form, as?, returns an optional value of the type you are trying to downcast to.
Example:
let number: Int = 1
if number as? Int {
print ("Int")
}
You are "asking" if number is of type Int.
Using the as! you're forcing, for example, your let number to be of a certain type, in case it is not, a crash you're going to take!
Example:
if number as! String { //CRASH
}
Answer in simple words ->
as! -> it is used for casting one data type to other data type forcefully(Use this only if you are sure..). We even call it force downcast means we downcast a particular type from superclass to subclass.
as -> This keywprd is used to raise an object to superclass type. So in other words we can say its upcasting.
as? -> So in this case unless you are certain that this cast is going to work then better option to add ? instead of !. Its also downcasting.
how can i convert any object type to a string?
let single_result = results[i]
var result = ""
result = single_result.valueForKey("Level")
now i get the error: could not assign a value of type any object to a value of type string.
and if i cast it:
result = single_result.valueForKey("Level") as! String
i get the error:
Could not cast value of type '__NSCFNumber' (0x103215cf0) to 'NSString' (0x1036a68e0).
How can i solve this issue?
You can't cast any random value to a string. A force cast (as!) will fail if the object can't be cast to a string.
If you know it will always contain an NSNumber then you need to add code that converts the NSNumber to a string. This code should work:
if let result_number = single_result.valueForKey("Level") as? NSNumber
{
let result_string = "\(result_number)"
}
If the object returned for the "Level" key can be different object types then you'll need to write more flexible code to deal with those other possible types.
Swift arrays and dictionaries are normally typed, which makes this kind of thing cleaner.
I'd say that #AirSpeedVelocity's answer (European or African?) is the best. Use the built-in toString function. It sounds like it works on ANY Swift type.
EDIT:
In Swift 3, the answer appears to have changed. Now, you want to use the String initializer
init(describing:)
Or, to use the code from the question:
result = single_result.valueForKey("Level")
let resultString = String(describing: result)
Note that usually you don't want valueForKey. That is a KVO method that will only work on NSObjects. Assuming single_result is a Dictionary, you probably want this syntax instead:
result = single_result["Level"]
This is the documentation for the String initializer provided here.
let s = String(describing: <AnyObject>)
Nothing else is needed. This works for a diverse range of objects.
The toString function accepts any type and will always produce a string.
If it’s a Swift type that implements the Printable protocol, or has overridden NSObject’s description property, you’ll get whatever the .description property returns. In the case of NSNumber, you’ll get a string representation of the number.
If it hasn’t, you’ll get a fairly unhelpful string of the class name plus the memory address. But most standard classes, including NSNumber, will produce something sensible.
import Foundation
class X: NSObject {
override var description: String {
return "Blah"
}
}
let x: AnyObject = X()
toString(x) // return "Blah"
"\(x)" // does the same thing but IMO is less clear
struct S: Printable {
var description: String {
return "asdf"
}
}
// doesn't matter if it's an Any or AnyObject
let s: Any = S()
toString(s) // reuturns "asdf"
let n = NSNumber(double: 123.45)
toString(n) // returns "123.45"
n.stringValue // also works, but is specific to NSNumber
(p.s. always use toString rather than testing for Printable. For one thing, String doesn’t conform to Printable...)
toString() doesn't seem to exist in Swift 3 anymore.
Looks like there's a failable initializer that will return the passed in value's description.
init?(_ description: String)
Docs here https://developer.apple.com/reference/swift/string/1540435-init