swift: safely access dictionary of SCNNode arrays - swift

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
}

Related

Fixed length array and a forced unwrapping of the last and the first elements

I have an array with 3 elements and want to take the first one and the last one elements.
let array = ["a", "b", "c"]
let first: String = array.first!
let last: String = array.last!
SwiftLint mark a force unwrap as a warning. Can I avoid a forced unwrapping when asking about the first and the last elements for a well known (defined) arrays?
I don't want to use a default values like in an example below
let first :String = array.first ?? ""
Edit:
Why am I asking about it? Because, I would like to avoid an warnings from the SwiftLint when using a forced unwrapping when asking for a first and a last element of an array which was defined by a literal and has enough elements to be sure that there is the first and the last element.
Edit 2:
I have found a name for what I was looking for. It's called Static-Sized Arrays. Static-Sized Arrays discussion stoped in 2017 and there is no chance to use it.
Try with index:
let first = array[0]
let last = array[array.count - 1]
Why am I asking about it? Because, I would like to avoid an warnings
from the SwiftLint when using a forced unwrapping when asking for a
first and a last element of an array which was defined by a literal
and has enough elements to be sure that there is the first and the
last element.
You can't really avoid to unwrap optional value, so if you only need it for two cases extensions can help here.
extension Collection {
func first() -> Element {
guard let first = self.first else {
fatalError() // or maybe return any kind of default value?
}
return first
}
}
let array = [1, 2]
array.first() // 1
And if it need to be only in one swift file you can place this code in that file and mark extensions with private keyword.
Can I avoid a forced unwrapping when asking about the first and the last elements for a well known (defined) arrays?
No you don't have to worry about it for a fixed array , actually the optional attachment for the properties first and last is designated to avoid crashes for an empty arrays

Swift ?? on a type

I know what the ?? is when used as an operator but what does it mean when it is on a Type?
I have a struct in my project called MyStruct and the autocompletion of a particular var tells me it is of type MyStruct??
Not really sure what that means or how to unwrap it safely.
As others have said, it's a double Optional, completely unrelated to the nil coalescence operator (??).
To unwrap it, you just unwrap a regular optional, twice:
let doubleOptional: MyStruct?? = MyStruct()
guard let singleOptional = doubleOptional else {
//first unwrapping failed
}
guard let wrappedValue = singleOptional else {
//second unwrapping failed
}
//use wrappedValue
These are quite uncommon, but there are times when they're useful.
For example, consider you have a data structure that stores an average of an array. The array might be empty, thus the average should be allowed to be nil, to indicate there is no average. Suppose calculating this average is very expensive and we want to store it in a caching layer. This caching layer could have a double optional representing that average. If the value is Optional.None (i.e. nil), we know the cache doesn't have a value yet, thus it needs to be computed. If the value is Optional.Some(Optional.None), we know the cache has a value, but that there is no valid average (i.e. the array was empty. Lastly, the value could be Optional.Some(Optional.Some(/*...*/)), which represents a valid cache value of a valid average.

swift function to iterate possibly reversed array

I'd like to create a function that will iterate over an array (or collection or sequence). Then I will call that function with an array, and the reversed version of the array (but efficiently: without creating a new array to hold the reverse).
If I do this:
func doIteration(points: [CGPoint]) {
for p in points {
doSomethingWithPoint(p)
}
// I also need random access to points
doSomethingElseWithPoint(points[points.count-2]) // ignore obvious index error
}
And if I have this:
let points : [CGPoint] = whatever
I can do this just fine:
doIteration(points)
But then if I do this:
doIteration(points.reverse())
I get 'Cannot convert value of type 'ReverseRandomAccessCollection<[CGPoint]> to expected argument type [_]'
Now, I DON'T want to do this:
let reversedPoints : [CGPoint] = points.reverse()
doIteration(reversedPoints)
even though it will work, because that will (correct me if I'm wrong) create a new array, initializing it from the ReverseRandomAccessCollection returned by reverse().
So I guess I'd like to write my doIteration function to take some sort of sequence type, so I can pass in the result of reverse() directly, but ReverseRandomAccessCollection doesn't conform to anything at all. I think I'm missing something - what's the accepted pattern here?
If you change your parameter's type to a generic, you should get the functionality you need:
func doIteration
<C: CollectionType where C.Index: RandomAccessIndexType, C.Generator.Element == CGPoint>
(points: C) {
for p in points {
doSomethingWithPoint(p)
}
doSomethingElseWithPoint(points[points.endIndex - 2])
}
More importantly, this won't cause a copy of the array to be made. If you look at the type generated by the reverse() method:
let points: [CGPoint] = []
let reversed = points.reverse() // ReverseRandomAccessCollection<Array<__C.CGPoint>>
doIteration(reversed)
You'll see that it just creates a struct that references the original array, in reverse. (although it does have value-type semantics) And the original function can accept this new collection, because of the correct generic constraints.
You can do this
let reversedPoints : [CGPoint] = points.reverse()
doIteration(reversedPoints)
or this
doIteration(points.reverse() as [CGPoint])
but I don't think there is any real difference by the point of view of a the footprint.
Scenario 1
let reversedPoints : [CGPoint] = points.reverse()
doIteration(reversedPoints)
Infact in this case a new Array containing references to the CGPoint(s) present in the original array is created. This thanks to the Copy-on-write mechanism that Swift used to manage structures.
So the memory allocated is the following:
points.count * sizeOf(pointer)
Scenario 2
On the other hand you can write something like this
doIteration(points.reverse() as [CGPoint])
But are you really saving memory? Let's see.
A temporary variable is created, that variable is available inside the scope of the function doIteration and requires exactly a pointer for each element contained in points so again we have:
points.count * sizeOf(pointer)
So I think you can safely choose one of the 2 solutions.
Considerations
We should remember that Swift manages structures in a very smart way.
When I write
var word = "Hello"
var anotherWord = word
On the first line Swift create a Struct and fill it with the value "Hello".
On the second line Swift detect that there is no real reason to create a copy of the original String so writes inside the anotherWord a reference to the original value.
Only when word or anotherWord is modified Swift really create a copy of the original value.

NSDictionary: error Unexpectedly found nil while unwrapping an Optional value

I was playing with an idea in the Playground. This idea NSDictionaries used as a way to "keep" data.
I started creating a variable called layer type [String:[String:String]].
So, an error occurred. I'm an hour trying to solve, and could not find the error reason happen. I am new to Swift.
var layers: [String:[String:String]]!
layers["key"] = ["layer":"layer"]
layers["key2"] = ["asd":"12312"]
print(layers)
Could someone help me? Or tell me how can I get the end result of my NSDictionaries?
You've declared the type of the variable layers but you haven't allocated storage for it yet.
Try this instead:
var layers = [String:[String:String]]()
If you insist on layers being an implicitly unwrapped optional, then you could initialize it this way:
var layers: [String:[String:String]]! = [:]
This would allow you to assign nil to it later, but that would be dangerous because it would crash if you try to use it when it is nil. That is the reason for your crash.
If you want it to be optional, you should declare it with ? so that it can be safely unwrapped:
var layers: [String:[String:String]]?
// Sometime later
layers = [:]
// use optional chaining to assign values, this safely does
// nothing if layers is nil
layers?["key"] = ["layer":"layer"]
layers?["key2"] = ["asd":"12312"]
// use optional binding to unwrap layers
if let unwrapped_layers = layers {
print(unwrapped_layers)
}
Try this in a Playground, and then try commenting out the layers = [:] part and you will see that it doesn't crash because all accesses to layers are done in a safe manner that properly handle the case when layers is nil.

Declaring a random amount of objects in Swift

I'm basically trying to do what this guy is trying to do, but with Swift:
Declaring a random amount of objects in c++
I'm pretty new to programming and very new to Swift so I'm a little fuzzy on how to do some things. Basically, I just want to declare a random amount of enemies that my player has to weave through and to register when he's hit one of them. I've been looking every where for an answer and either this is a really stupid question, so stupid no one has ever needed to put it on the internet, or Sift is too new for it to have been a problem for someone else. I'm guessing it's a stupid question, but regardless I'm out of ideas on how to figure this out.
Thanks.
Swift arrays are dynamic, at least the mutable type that are declared with var array.
So you can just add objects to your array until you have enough objects.
e.g.:
var objects = [String]()
//let numberOfObjects = arc4random_uniform(10) // between 0 and 9
let numberOfObjects = arc4random_uniform(5) + 5 // between 5 and 9
for i in 0..<numberOfObjects {
let object = String()
objects.append(object)
}
println("we have \(countElements(objects)) objects")
Maybe I'm too dense to understand the question, but it seems to me that simply declaring an NSMutableArray would do what you want:
let myArray:NSMutableArray = NSMutableArray()
You would have all the flexibility to add or remove objects as you please.
In any given loop, you could remove all objects, then use a random number generator to add back a random number of objects if that's what you're trying to do.
My additional comment on the other answer to this is that I would explicitly type cast everything in the statement that generates the random number:
var randomNumber:Int = Int(arc4random_uniform(UInt32(5)))
Here's an example using a simple Enemy class:
class Enemy : NSObject {
override init() {
super.init()
}
}
let range = (5, 20)
let numEnemies = Int(arc4random_uniform(UInt32(range.1 - range.0) + 1) + UInt32(range.0))
var enemyArray = [Enemy]()
for i in 0..<numEnemies {
enemyArray.append(Enemy())
}
You want a random number of objects in an array. Assume:
class MyObject {}
and that you have an upper bound on the number of objects:
let theObjectLimit = 10
then you can create a random number of new, distinct instances of MyObject with simply:
let theObjects = (1...arc4random_uniform(theObjectLimit)).map {
_ -> MyObject in return MyObject ()
}
When learning a language like Swift, that has first class functions (aka closures), you'll want to use functions like map and reduce wherever possible - especially instead of iteration.