Swift type casting / downcasting - swift

Why does the Sprite Kit Game template project created by Xcode use as!:
if let sceneNode = scene.rootNode as! GameScene? {...}
Wouldn't the following be equally good?
if let sceneNode = scene.rootNode as? GameScene {...}
Note, this isn't the standard "what is the difference between as? and as!" question. And Downcasting optionals in Swift: as? Type, or as! Type? is very close, but isn't quite the same thing, either. The question is that the two above patterns seems functionally similar (they both downcast and unwrap), but it's unclear why the author used if let ... as! GameScene? { ... } instead of the more common and more natural if let ... as? GameScene { ... }.

These two patterns are very close, but are not technically the same:
The if let ... as! GameScene? {...} does the forced cast to GameScene? and the if let then safely unwraps the resulting optional.
The if let ... as? GameScene { ... } will gracefully downcast and unwrap the result.
Functionally these two are almost equivalent. The question is why the template would use the former syntax. One could argue that the as? pattern is little ambiguous because glancing at the code you cannot tell whether you're merely testing the success of a downcast or whether you're dealing with an optional, too. The template's code makes this more explicit.
All of that having been said, I would use if let ... as? GameScene { ... } pattern. It is customary.

I'd say it's a mistake. In any case, the pattern if let x = y as! Type? is unnecessary and you should not imitate it. The pattern if let x = y as? Type is equivalent and usual.

Related

Class casting dynamically in swift

I am trying to dyanmically cast to a class in Swift. Is this possible? Here is the code I am trying to use:
let stringClass: AnyClass = NSString.self
let anyObject: AnyObject = "foo"
let string = anyObject as! stringClass
The code fails to compile at the cast. Is this possible and if so, why is the right syntax?
Real use case
Here is the real issue. I am attempting to refactor this code:
switch (value) {
case "valueOne":
viewController = storyboard.instantiateViewController(withIdentifier: "foo") as! FirstViewController
case "valueTwo":
viewController = storyboard.instantiateViewController(withIdentifier: "bar") as! SecondViewController
default:
return nil
}
into:
let controllersDictionary: [String: (String, UIViewController.Type)] = [
"valueOne" : ("bar", FirstViewController.self),
"valueTwo" : ("foo", SecondViewController.self)
]
let tuple = controllersDictionary[value]!
let identifier = tuple.0
let cast = tuple.1
let viewController = storyboard.instantiateViewController(withIdentifier: identifier) as! cast
I'm not sure exactly what you're trying to achieve, but here's a working version of your example:
func cast<T>(value: Any, to type: T) -> T? {
return castedValue as? T
}
let inputValue: Any = "this is a test"
let inputType = String.self()
let casted = cast(value: inputValue, to: inputType)
print(casted)
I'm not seeing what the cast at this point is for. You can write:
let controllersDictionary: [String: String] = [
"valueOne" : "bar",
"valueTwo" : "foo"
]
let identifier = controllersDictionary[value]!
let viewController = storyboard.instantiateViewController(withIdentifier: identifier)
The cast does nothing for you in the code that you have shown. viewController is typed as UIViewController, but it is the correct underlying view controller subclass thanks to polymorphism; whatever the class is in the storyboard, that's the class of this instance.
The only time you need to cast down is when you have to message an instance with a message belonging only to the subclass, and you have not shown any such need at this point in your code.
While there are/will be ways to make this kind of thing work, the Swifty solution (IMO) is to have your desired classes adhere to a protocol that defines the shared behavior you're trying to use, or simply use a super class they have in common
This allows the dynamism requried (in most cases at least) while still allowing the compile-time checks that prevent run time errors.
For your example,
protocol Stringable {
func toString() -> String
}
extension String: Stringable {
func toString() -> String {
return self
}
}
let thing = "foo"
let anything: Any = thing
let test: String? = (anything as? Stringable)?.toString()
Note that this requires "Any" rather than "AnyObject" since you need to cast to a protocol
Since you mentioned ViewControllers, I thought this might help:
static func createViewController<T: UIViewController>(storyboard: String, scene: String) -> T? {
return UIStoryboard(name: storyboard, bundle: nil).instantiateViewControllerWithIdentifier(scene) as? T
}
The x as! Y.Type syntax only works when Y is explicitly stated.
So, the compiler wants x as! NSString. The compiler crash is a bug, I suggest that you file a report.
And just think about it for a second, how would that even help you? stringClass is of type AnyClass, and you're force casting an AnyObject which already conforms to AnyClass. You should cast when you have a static type in mind, because casting doesn't really make any sense otherwise.
If you want to check, however, whether your object's class is a subclass of a particular type, you'd use:
x is Y.Type
Its possible so long as you can provide "a hint" to the compiler about the type of... T. So in the example below one must use : String?.
func cast<T>(_ value: Any) -> T? {
return value as? T
}
let inputValue: Any = "this is a test"
let casted: String? = cast(inputValue)
print(casted) // Optional("this is a test")
print(type(of: casted)) // Optional<String>
Why Swift doesn't just allow us to let casted = cast<String>(inputValue) I'll never know.
One annoying scenerio is when your func has no return value. Then its not always straightford to provide the necessary "hint". Lets look at this example...
func asyncCast<T>(_ value: Any, completion: (T?) -> Void) {
completion(value as? T)
}
The following client code DOES NOT COMPILE. It gives a "Generic parameter 'T' could not be inferred" error.
let inputValue: Any = "this is a test"
asyncCast(inputValue) { casted in
print(casted)
print(type(of: casted))
}
But you can solve this by providing a "hint" to compiler as follows:
asyncCast(inputValue) { (casted: String?) in
print(casted) // Optional("this is a test")
print(type(of: casted)) // Optional<String>
}

Is force cast really bad and should always avoid it?

I started to use swiftLint and noticed one of the best practices for Swift is to avoid force cast. However I used it a lot when handling tableView, collectionView for cells :
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(cellID, forIndexPath: indexPath) as! MyOffersViewCell
If this is not the best practice, what's the right way to handle this? I guess I can use if let with as?, but does that mean for else condition I will need to return an empty cell? Is that acceptable?
if let cell = collectionView.dequeueReusableCellWithReuseIdentifier(cellID, forIndexPath: indexPath) as? MyOffersViewCell {
// code
} else {
// code
}
This question is probably opinion based, so take my answer with a grain of salt, but I wouldn't say that force downcast is always bad; you just need to consider the semantics and how that applies in a given situation.
as! SomeClass is a contract, it basically says "I guarantee that this thing is an instance of SomeClass". If it turns out that it isn't SomeClass then an exception will be thrown because you violated the contract.
You need to consider the context in which you are using this contract and what appropriate action you could take if you didn't use the force downcast.
In the example you give, if dequeueReusableCellWithIdentifier doesn't give you a MyOffersViewCell then you have probably misconfigured something to do with the cell reuse identifier and an exception will help you find that issue.
If you used a conditional downcast then you are going to get nil and have to handle that somehow - Log a message? Throw an exception? It certainly represents an unrecoverable error and something that you want to find during development; you wouldn't expect to have to handle this after release. Your code isn't going to suddenly start returning different types of cells. If you just let the code crash on the force downcast it will point straight to the line where the issue occurred.
Now, consider a case where you are accessing some JSON retrieved from a web service. There could be a change in the web service that is beyond your control so handling this more gracefully might be nice. Your app may not be able to function but at least you can show an alert rather than simply crashing:
BAD - Crashes if JSON isn't an array
let someArray=myJSON as! NSArray
...
Better - Handle invalid JSON with an alert
guard let someArray=myJSON as? NSArray else {
// Display a UIAlertController telling the user to check for an updated app..
return
}
Update
After using Swiftlint for a while, I am now a total convert to the Zero Force-Unwrapping Cult (in line with #Kevin's comment below).
There really isn't any situation where you need to force-unwrap an optional that you can't use if let..., guard let... else, or switch... case let... instead.
So, nowadays I would do this:
for media in mediaArray {
if let song = media as? Song {
// use Song class's methods and properties on song...
} else if let movie = media as? Movie {
// use Movie class's methods and properties on movie...
}
}
...or, if you prefer the elegance and safety of an exhaustive switch statement over a bug-prone chain of if/elses, then:
switch media {
case let song as Song:
// use Song class's methods and properties on song...
case let movie as Movie:
// use Movie class's methods and properties on movie...
default:
// Deal with any other type as you see fit...
}
...or better, use flatMap() to turn mediaArray into two (possibly empty) typed arrays of types [Song] and [Movie] respectively. But that is outside the scope of the question (force-unwrap)...
Additionally, I won't force unwrap even when dequeuing table view cells. If the dequeued cell cannot be cast to the appropriate UITableViewCell subclass, that means there is something wrong with my storyboards, so it's not some runtime condition I can recover from (rather, a develop-time error that must be detected and fixed) so I bail with fatalError().
Original Answer (for the record)
In addition to Paulw11's answer, this pattern is completely valid, safe and useful sometimes:
if myObject is String {
let myString = myObject as! String
}
Consider the example given by Apple: an array of Media instances, that can contain either Song or Movie objects (both subclasses of Media):
let mediaArray = [Media]()
// (populate...)
for media in mediaArray {
if media is Song {
let song = media as! Song
// use Song class's methods and properties on song...
}
else if media is Movie {
let movie = media as! Movie
// use Movie class's methods and properties on movie...
}
Others have written about a more general case, but I want to give my solution to this exact case:
guard let cell = tableView.dequeueReusableCell(
withIdentifier: PropertyTableViewCell.reuseIdentifier,
for: indexPath) as? PropertyTableViewCell
else {
fatalError("DequeueReusableCell failed while casting")
}
Basically, wrap it around a guard statement and cast it optionally with as?.
"Force Cast" has its place, when you know that what you're casting to is of that type for example.
Say we know that myView has a subview that is a UILabel with the tag 1, we can go ahead and force down cast from UIView to UILabel safety:
myLabel = myView.viewWithTag(1) as! UILabel
Alternatively, the safer option is to use a guard.
guard let myLabel = myView.viewWithTag(1) as? UILabel else {
... //ABORT MISSION
}
The latter is safer as it obviously handles any bad cases but the former, is easier. So really it comes down to personal preference, considering whether its something that might be changed in the future or if you're not certain whether what you are unwrapping will be what you want to cast it to then in that situation a guard would always be the right choice.
To summarise: If you know exactly what it will be then you can force cast otherwise if theres the slightest chance it might be something else use a guard
As described in some casting discussions, forcing the cast for tableView.dequeueReusableCell is ok and can/should be done.
As answered on the Swiftlint Github site you can use a simple way to turn it off for the table cell forced cast.
Link to Swiftlink issue 145
// swiftlint:disable force_cast
let cell = tableView.dequeueReusableCell(withIdentifier: "cellOnOff", for: indexPath) as! SettingsCellOnOff
// swiftlint:enable force_cast
When you are working with your types and are sure that they have an expected type and always have values, it should force cast. If your apps crash you can easily find out you have a mistake on which part of UI, Dequeuing Cell, ...
But when you are going to cast types that you don't know that is it always the same type?
Or is that always have value?
You should avoid force unwrap
Like JSON that comes from a server that you aren't sure what type is that or one of that keys have value or not
Sorry for my bad English I’m trying to improve myself
Good luck🤞🏻
In cases where you are really sure the object should be of the specified type it would be OK to down cast. However, I use the following global function in those cases to get a more meaningful result in the logs which is in my eyes a better approach:
public func castSafely<T>(_ object: Any, expectedType: T.Type) -> T {
guard let typedObject = object as? T else {
fatalError("Expected object: \(object) to be of type: \(expectedType)")
}
return typedObject
}
Example usage:
class AnalysisViewController: UIViewController {
var analysisView: AnalysisView {
return castSafely(self.view, expectedType: AnalysisView.self)
}
override func loadView() {
view = AnalysisView()
}
}

Type casting operator

In Swift guide that as published on ibooks, as! operator was not mentioned. But in online reference and in some example code, they (i mean Apple in both cases) used as! operator.
Is there a difference between as and as! operators? If there are, can you explain please?
edit: Im so tired that i wrongly typed "is", instead of "as". That is now corrected...
as? will do an optional downcast - meaning if it fails it will return nil
so "Blah" as? Int will return Int? and will be a nil value if it fails or an Int if it does not.
as! forces the downcast attempt and will throw an exception if the cast fails. Generally you will want to favour the as? downcast
//ex optional as?
let nine = "9"
if let attemptedNumber = nine as? Int {
println("It converted to an Int")
}
//ex as!
let notNumber = "foo"
let badAttempt = notNumber as! Int // crash!
( You may find that you that an update is sitting there for the swift guide. It is mentioned for sure in the online version https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TypeCasting.html )
operator is the forcefully unwrapped optional form of the as? operator. As with any force unwrapping though, these risk runtime errors that will crash your app should the unwrapping not succeed.
Further, We should use as to upcast if you wish to not write the type on the left side, but it is probably best practice to write it with normal typing as shown above for upcasting.
Example:
You use the as keyword to cast data types. UIWindow rootViewController is of type UIViewController. You downcast it to UISplitViewController.
Another better example can be taken as follows.
var shouldBeButton: UIView = UIButton()
var myButton: UIButton = shouldBeButton as UIButton
The as? operator returns an optional, and then we use optional binding to assign it to a temporary constant, and then use that in the if condition, like we are doing in the below example.
let myControlArray = [UILabel(), UIButton(), UIDatePicker()]
for item in myControlArray
{
if let myLabel = item as? UILabel
{
var storeText = myLabel.text
}
else if let someDatePicker = item as? UIDatePicker
{
var storeDate = someDatePicker.date
}
}

What's the difference between "as?", "as!", and "as"?

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

Swift variable declaration meaning

What's the difference between these two declarations? Which one is better? Why?
... error = some NSError ...
1.
var newUserInfo: [NSObject: NSObject] = [:]
if let tempUserInfo = error.userInfo as? [NSObject: NSObject] {
newUserInfo = tempUserInfo
}
2.
var newUserInfo: [NSObject: NSObject]
if let tempUserInfo = error.userInfo as? [NSObject: NSObject] {
newUserInfo = tempUserInfo
} else {
newUserInfo = [:]
}
As of Swift 1.2, you can now use let with deferred assignment so you can use your if/else version:
let newUserInfo: [NSObject: NSObject]
if let tempUserInfo = error.userInfo as? [NSObject: NSObject] {
newUserInfo = tempUserInfo
} else {
newUserInfo = [:]
}
However, option 1 will not work, since there is a path where newUserInfo may not be set.
(note, as of 1.2b1, this doesn't work with global variables, only member and local variables, in case you try this out in a playground)
Alternatively, you could use the nil-coalescing operator to do it in one go, like this:
let newUserInfo = (error.userInfo as? [NSObject:NSObject]) ?? [:]
edit: Swift 1.2 added deferred assignment of let, enabling option 2 to be used with let now, but also changed the precedence of as? vs ??, requiring parens.
Pre-1.2 answer in case you have similar code you need to migrate:
Neither are particularly appealing if you ask me. In both cases, you have to have to declare newUserInfo with var, because you're not declaring and assigning it in one go.
I'd suggest:
let newUserInfo = error.userInfo as? [NSObject:NSObject] ?? [:]
In 1. newUserInfo is assigned twice if the if branch is executed. 2. is better in terms of performance
In 1. it's clearly visible that newUserInfo is initialized as an empty array. 2. makes the code less readable because you have to browse the code to know if it has a default value
If newUserInfo can be set in several places (such as if it can be initialized it in several if statements), you should duplicate the else branch in 2., so 1. looks better
So: in solution no. 1 code is more readable, but solution no. 2 is slightly more performant.
Besides using #AirspeedVelocity solution (which is better than yours, no offence :)), I'd rather prefer to use an optional, setting newUserInfo to nil to indicate absence of value - after all, that's what optionals are for, isn't it? But of course that depends on your specific needs.
My preferred pattern for this is:
struct TryUnrap<T> {
typealias Tryee = () -> T?
typealias Catchee = () -> T
private var tryee: Tryee
init(tryee: Tryee) {
self.tryee = tryee
}
func catch(catchee: Catchee) -> T {
if let result = tryee() {
return result
}
return catchee()
}
}
let error: NSError? = NSError()
let newUserInfo = TryUnrap {
error?.userInfo as? [NSObject : NSObject]
}.catch {
[:]
}
println("newUserInfo = \(newUserInfo)")
I prefer the above because I find it reads better for me. Also see answer 4 of Error-Handling in Swift-Language for general error handling using the same pattern.