Unwrapping optionals in Apple's game code project template - swift

In the small sample of Apple's code which is created when you make a new game project in Xcode, the GameScene has several functions which use an unwrapping pattern I've not come across, & I fail to see the point of it -
if let n = self.spinnyNode?.copy() as! SKShapeNode? { ...code... }
Two questions - (1) is this dangerous, & (2) why not use the more common pattern -
if let n = self.spinnyNode?.copy() as? SKShapeNode { ...code... }
I can find nothing relating to this on SO or Google...

Lets break down the original line of code:
if let n = self.spinnyNode?.copy() as! SKShapeNode? { ...code... }
self.spinnyNode is declared as an SKShapeNode?. In other words, it's an optional SKShapeNode.
SKShapeNode inherits from NSObject which provides the copy method. The copy method is declared to return Any.
self.spinnyNode?.copy() will return an Any? with a value of nil if self.spinnyNode is nil or it will return a non-nil Any? if it's not.
So the compiler thinks that self.spinnyNode?.copy() will return an Any?. But we know it will really return a SKShapeNode?. Since we know what it will really be we can safely use as!. And since we know it will return a SKShapeNode?, we use as! SKShapeNode?.
At this point we could have:
let n = self.spinnyNode?.copy() as! SKShapeNode?
where n will be a SKShapeNode?.
Since we now want to safely unwrap the result, we add the if in front of the let:
if let n = self.spinnyNode?.copy() as! SKShapeNode? { ...code... }
and inside the block we know that n is a non-nil SKShapeNode.
With all of that, let's answer your questions:
Is this dangerous? No, not in this case since we know for sure that self.spinnyNode?.copy() will always result in a SKShapeNode? we can safely use as! to cast it to SKShapeNode?.
Why not use the more common as? SKShapeNode pattern? We could but that pattern is used when you don't know for sure that the result is actually a SKShapeNode.

Related

Typecasting with as in Swift

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­ pattern­opt ­where-clause­opt ­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 {
// ...
}

swift: safely access dictionary of SCNNode arrays

I'm relatively new to Swift and have yet to master the safety aspects of optionals.
I have a dictionary of type [String: [SCNNode]]. A given molecule will have multiple components as elements in an [SCNNode]. For that molecule I retrieve this array of components and assign each element to a local SCNNode to be displayed, manipulated and animated.
let components = moleculeDictionary["aceticAcid"] // the array of components
// [baseMolecule, hydrogenCharge, oxygenCharge, ionizingH, ionizedBond, bonds]
atomsNode_1 = components![0] // baseMolecule
baseNode.addChildNode(atomsNode_1)
atomsNode_5 = components![3] // ionizingH
atomsNode_1.addChildNode(atomsNode_5)
// etc.
In an attempt to optionally bind this, the compiler seems happy with this.
if let node = components?[0] { // baseMolecule
baseNode.addChildNode(node)
}
I'm unclear on the ?. My reading on this suggests we're unwrapping in such a way that we don't care if there's a nil. But does that make this optional binding any better that the forced unwrapping above? Or is this "optional chaining"? Should I be looking instead to just do a check when I assign components? Should I even be concerened about safety here? The only "upsteam" test I've done is for the presence of the dictionary archive before assigning it to moleculeDictionary.
I will have hundreds of these assignments so I'd like to get this right. Suggestions on the best way to handle this are welcome!
Thanks, Byrne
In my opinion, you should be worried about safety in Swift. Given your code above I would rewrite it in the first pass as:
guard let components = moleculeDictionary["aceticAcid"] where components.count > 3 else { // handle the case where there is no value for this key, or not enough nodes }
// Now I know that I have the correct number of components I don't need to force unwrap:
atomsNode_1 = components[0] // baseMolecule
baseNode.addChildNode(atomsNode_1)
atomsNode_5 = components[3] // ionizingH
atomsNode_1.addChildNode(atomsNode_5)
That's the first first pass. The second pass would be to make sure that I have values for all the components would be to write a struct to contain the SCNNode values so that I either had a value or a nil for each node, you could then build up your node structure accordingly.
Edited to add
Here's an indication of the second pass - which really needs more domain knowledge than I have, but it's a start.
It seems you build up the molecules from [[SCNNode]] (an array of arrays of SCNNodes where the position of each subarray is significant. It's worth putting this into it's own structure such that:
struct Molecule {
let baseMolecule: [SCNNode]?
let hydrogenCharge: [SCNNode]?
let oxygenCharge: [SCNNode]?
let ionizingH: [SCNNode]?
let ionizedBond: [SCNNode]?
let bonds: [SCNNode]?
// Other methods can be written to build up atom nodes from within the struct which handle the cases where the component array is nil
}

Swift NSUserDefaults first time nil

Hi my app crashes the first time I run it. This is my code:
let State = save.stringForKey("StateSave")
let City = save.stringForKey("CitySave")
let Vehicle = save.stringForKey("ModelNumberSave")
let ExtensionPeriod = save.stringForKey("ExtensionPeriodChoosed")
let Location = "Location"
if ExtensionPeriod == nil {
let name = ""
var FieldChoosed: Void = save.setObject(name, forKey: "ExtensionPeriodChoosed")
save.synchronize()
}
save.synchronize()
var DetailNames = ["\(State!)","\(City!)","\(Location)","\(Vehicle!)","\(ExtensionPeriod!)"]
I get a nil on this line:
var DetailNames =
["(State!)","(City!)","(Location)","(Vehicle!)","(ExtensionPeriod!)"]
In fact ExtensionPeriod is nil. But I don't understand why... ExtensionPeriod is nil, but with the code I write, ExtensionPeriod will be like "" so it's not nil. Please help me.
stringForKey returns nil when there has not been a value saved already.
You need to give your values a default. The easiest way to do this is with the ?? operator, that replaces nil with another value:
let State = save.stringForKey("StateSave") ?? "-"
Some general advice: you need to stop using ! so much. Usually when something returns nil, it’s for a good reason – it might be nil. When you unwrap it with !, your program will crash (with not much helpful info as to why). Similarly, it’s usually a bad sign if you’re comparing values to nil.
Instead, take a look at this list of optional handling techniques for some alternatives.
Airspeed Velocity has a good solution for the proper way to accomplish what you want to do, but he did not really explain why what you did does not work, so I will address that aspect of this question.
if ExtensionPeriod == nil {
let name = ""
var FieldChoosed: Void = save.setObject(name, forKey: "ExtensionPeriodChoosed")
save.synchronize()
}
That block of code does not set ExtensionPeriod, thus ExtensionPeriod is still nil. All it does is set the value for the key "ExtensionPeriodChoosed" in the NSUserDefaults to no longer be nil. The local variable ExtensionPeriod, however, still has nil. ExtensionPeriod doesn't just magically point to the variable stored in NSUserDefaults, such that when you update NSUserDefaults, it automatically updates the variable. Instead, it copies the variable at the time that it is created.
Here is some sample code that demonstrates this:
NSUserDefaults.standardUserDefaults().setValue("string", forKey: "s")
NSUserDefaults.standardUserDefaults().synchronize()
var s = NSUserDefaults.standardUserDefaults().valueForKey("s")
NSUserDefaults.standardUserDefaults().setValue("string2", forKey: "s")
NSUserDefaults.standardUserDefaults().synchronize()
var y = NSUserDefaults.standardUserDefaults().valueForKey("s")
println(s)
println(y)
outputs:
"string"
"string2"
For your code to work, if you were to keep it the same structure (although you really shouldn't), you would need to change ExtensionPeriod to a var, and then in your if block, after you synchronize save, you would have to reassign ExtensionPeriod to save.valueForKey("ExtensionPeriodChoosed").
One way to make sure that your app's defaults are set is to use NSUserDefault's registerDefaults(_: [NSObject : AnyObject]) function. In Objective-C, I often put in the + (void)initialize class method, but overriding the init() of the application delegate should work just as well.

"if let" statement executed despite value being nil

I have an "if let" statement that is being executed, despite the "let" part being nil.
if let leftInc : Double? = self.analysis.inputs[self.btnLeftIncisor.dictionaryKey!]! {
println(leftInc)
let valueString : String = formatter.stringFromNumber(NSNumber(double: leftInc!))!
self.leftIncisorTextField?.text = valueString
self.btnLeftIncisor.associatedLabel?.text = valueString
}
// self.analysis.inputs is a Dictionary<String, Double?>
The inputs dictionary holds information entered by the user - either a number, or nil if they haven't entered anything in the matching field yet.
Under the previous version of Swift, the code was written as this:
if let leftInc : Double? = self.analysis.inputs[self.btnLeftIncisor.dictionaryKey!]?? {
and worked correctly.
I saw a similar question here, but in that instance the problem seemed to be the result of using Any?, which is not the case here.
Swift 2.2
In your if let you define another optional, that's why nil is a legitimate case. if let is intended mainly to extract (maybe) non optional value from an optional.
You might try:
if let leftInc : Double = self.analysis.inputs[self.btnLeftIncisor.dictionaryKey!].flatMap ({$0}) {
// leftInc is not an optional in this scope
...
}
Anyway I'd consider to not do it as a one liner but take advantage of guard case. Just in order to enhance readability. And avoid bang operator (!).
The if-let is for unwrapping optionals. You are allowing nil values by setting the type to an optional Double.
The if statement should be:
if let leftInc = self.analysis.inputs[self.btnLeftIncisor.dictionaryKey!] as? Double{
...
}
This will attempt to get an object out of inputs, if that fails it returns nil and skips it. If it does return something it will attempt to convert it to a Double. If that fails it skips the if statement as well.
if inputs is a dictionary like [Something:Double] then you don't need the last as? Double as indexing the dictionary will return a Double?
I recommend reading the swift book on optional chaining.
You could break it down further -
if let optionalDouble = self.analysis.inputs[self.btnLeftIncisor.dictionaryKey!], leftInc = optionalDouble {
....
}
as your dictionary has optional values - this way of writing it might make it clearer what's going on
if let k = dict["someKey"]{}, dict["someKey"] will be an object of type Any
this can bypass a nill
So do a typecast to get it correct like if let k = dict["someKey"] as! String {}

Why are multiple unwrapping optionals impossible?

I've been playing with optionals in swift. I make frequent use of the conditional unwrap pattern:
var myOptional: AnyObject?
if let unwrapped = myOptional {
// do stuff
}
However, on occasion I have two optional values that I only want to use if both of them are non-nil. As such, I tried to use the following syntax:
var myOptional: AnyObject?
var myOtherOptional: AnyObject?
if let unwrapped = myOptional && let otherUnwrapped = myOtherOptional? {
// do stuff
}
I've tried putting the two parts in brackets etc. but there doesn't seem to be a way to do this. Is there a good reason why I shouldn't be able to do it? Obviously I can just embed one statement in the other but I would prefer to put it all on one line.
Starting with Swift 1.2 you can unwrap multiple optionals and conditions.
The “if let” construct has been expanded to allow testing multiple
optionals and guarding conditions in a single if (or while) statement
using syntax similar to generic constraints:
if let a = foo(), b = bar() where a < b,
let c = baz() { } This allows you to test multiple optionals and include intervening boolean conditions, without
introducing undesirable nesting (i.e., to avoid the “pyramid of
doom”).
Because the language doesn't support it.
In the document:
The value of any condition in an if statement must have a type that conforms to the BooleanType protocol. The condition can also be an optional binding declaration, as discussed in Optional Binding.
The condition must be an "expression of BooleanType" or an "optional binding declaration".
And "optional binding declaration" is not an "expression" so you can't join with &&.
Instead, you can do that with switch:
switch (myOptional, myOtherOptional) {
case let (.Some(unwrapped), .Some(otherUnwrapped)):
// do stuff
println("\(unwrapped), \(otherUnwrapped)")
default:
break
}
The only way is to nest if statements. I think this is because apple implemented it as syntactic sugar. So the pre compiler converts
var myOptional: AnyObject?
if let unwrapped = myOptional {
// do stuff
}
into
var myOptional: AnyObject?
if myOptional != nil {
let unwrapped = myOptional
// do stuff
}
You can of course do this by yourself in a single if, but this will make your code only a little prettier. On the downside, you won't know which one caused the crash while debugging.
For more information see the documentation
var myOptional: AnyObject?
var myOtherOptional: AnyObject?
let unwrapped: AnyObject? = myOptional, otherUnwrapped: AnyObject? = myOtherOptional?
if (unwrapped != nil && otherUnwrapped != nil) {
// Do Stuff
}
This is another way you could do it. Swift is looking better day by day