Type casting operator - swift

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

Related

Swift type casting / downcasting

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.

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

Cast value & get property from it in one line

I'd like to write following lines into one line statement:
var myBool = false
if let myButton = myView.subView.button as? MyButton {
myBool = !myButton.isValid
}
Is it possible to do it this way, that I have everything in short statement which will also return false if myButton is not the type of MyButton?
If you really want it on one line, you can do something like this:
let myBool = !((myView.subView.button as? MyButton)?.isValid ?? true)
This is the most concise way I've been able to come up with that has the expected behavior you outlined. However, it's far from easy to read.
The clear reason to want a single line approach however is perhaps so that myBool can be declared as a constant with let, right?
The alternative here could be a method:
func isValidButton(testView: UIView) -> Bool {
guard let button = testView as? MyButton else {
return false
}
return button.isValid
}
So this is multiple lines, but it's easier for a human to read what is going on here. And in the calling place, it's still one line and allows for the let declaration of your boolean variable:
let myBool = !isValidButton(myView.subView.button)
And keep in mind, this doesn't even have to be a method on the class. If you need it in just one spot, it can be a closure with local scope.
func viewDidLoad() {
super.viewDidLoad()
let isValidButton = { (testView: UIView) -> Bool in
guard let button = testView as? MyButton else {
return false
}
return button.isValid
}
let myBool = !isValidButton(myView.subView.button)
// do some things
}
As a note here, I've only assuming that your MyButton inherits from UIView and that UIView is perhaps what myView.subView.button is declared as returning. Realistically, your isValidButton() closure should take an argument of whatever type myView.subView.button returns (maybe it's UIButton) and presumably, that type is either a parent of MyButton or it is a protocol which MyButton conforms to.
Another option for a 1-liner is to use the nil coalescing operator along with map to apply the negation:
let myBool = ((myView.subView.button as? MyButton)?.isValid).map{!$0} ?? false

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.

How do I validate IBOutlet/var for nil?

I've written a simple application using Swift and Xcode 6.1.1. The program, which is a simple calculator, works well but I haven't been able to validate non-nil input for the three text fields. As a result, if the user leaves the field blank and then hits "calculate," the application crashes.
The app takes three inputs, initially as strings. I wrote an if statement to check for nil but it doesn't work - it will just pass to else regardless. Here's the code block that's relevant to my question:
...
#IBOutlet var calcButton: UIBarButtonItem!
#IBOutlet var yearOneField: UITextField!
#IBOutlet var yearTwoField: UITextField!
#IBOutlet var yearThreeField: UITextField!
#IBOutlet var displayWindow: UILabel!
#IBAction func calcButtonTapped(sender: AnyObject) {
if (yearOneField == nil) {
displayWindow.text = ("Non-zero entries are not permitted. Please enter new values.")
} else {
let yearOne = yearOneField.text.toInt()
let yearTwo = yearTwoField.text.toInt()
let yearThree = yearThreeField.text.toInt()
...
I was thinking I could evaluate the IBOutlet for nil but that didn't work. I'm new to Swift and Xcode so I hope this is a n00b question to the more experienced developers out there. Thank you.
The only way the #IBOutlets could be nil is if you forgot to wire them up in Interface Builder. Usually you don't need to check that because the crash will tell you to fix that problem.
The toInt() function returns an Optional Int (aka Int?) that must be unwrapped before being used. toInt() will return nil if the value in the text field does not represent a valid Int. "2.1", "seven", and "" would all return nil if converted with toInt(). I recommend you use optional binding (if let) syntax to check the conversion for nil and unwrap the result if it is not nil:
if let yearOne = yearOneField.text.toInt() {
if let yearTwo = yearTwoField.text.toInt() {
if let yearThree = yearThreeField.text.toInt() {
// yearOne, yearTwo, and yearThree are all valid Ints
// so do the calculations
}
}
}
Alternatively, if you know you want to use a default value (like 0) when the field can't be converted to an Int, you can unwrap the result using the nil coalescing operator ?? like so:
let yearOne = yearOneField.text.toInt() ?? 0
let yearTwo = yearTwoField.text.toInt() ?? 0
let yearThree = yearThreeField.text.toInt() ?? 0
The text fields themselves will never be nil. They are created and assigned during initialization, and you're never removing them.
I think you want to check if their text properties contain any text, which you can do like this:
Updated for Swift 2:
if let text = yearOneField.text where !text.isEmpty {
// perform the conversions
} else {
// the text field is empty
}
You can avoid nesting using guard:
guard let text = yearOneField.text where !text.isEmpty else {
// the text field is empty
return
}
// perform the conversions
I prefer the guard syntax because it's clearer about what the ideal result is.
You can just check as you do with normal optionals.
guard let unwrapped = myLabel else {return}
Or like this
if myLabel == nil {
//do stuff
}
Or like this:
if let unwrappedLabel = myLabel {
}