Featured Post

Applying Email Validation to a JavaFX TextField Using Binding

This example uses the same controller as in a previous post but adds a use case to support email validation.  A Commons Validator object is ...

Tuesday, February 24, 2015

Swift Optionals Are Optionals

I'm converting an Objective-C app to Swift.  Rather than rewriting the app, I'm porting by going VC-by-VC and line-by-line (where possible) to update the app. Optionals are a new concept in Swift and they're going to play a key role in my porting project.

A Java NullPointerException. A C segmentation fault.  Accessing something that isn't there is the major source of bugs that I've seen in my career.  Optionals -- or more specifically the non-Optionals -- are Swift's way of trying to fend off this type of bug.  You're strongly encouraged into controlling your program state with keywords like "let" or "var" (without the "?").   That way, when you get deep into a class or method, you will find your objects ready to be used.

But you may need Optionals.  In the porting project I'm describing, I've deferred a lot of Objective-C object creation such that I don't have all the information to set these values in an init().  Also, there is logic based on nil checks.  In those cases, I have classes that are using Optionals.

This is a small demonstration on how I handle this type of initialization.  I start with the open-ended declaration, in this case a variable "optDictionary".  Note the question mark after the declaration.

var optDictionary : [String: String]?

I following up the declaration by setting the Optional at some other place in the program.

optDictionary = [String: String]()

Optionals are Optionals

At this point, optDictionary is still an Optional.  Although I've rigorously defined the type of optDictionary, I can't use normal Dictionary methods and operators like the subscript operator ([]) or the keys function.  I have to mark the Optional with an exclamation point (!) in order to use it, a step called unwrapping.

optDictionary!["A"] = "alpha"
optDictionary!["B"] = "bravo"
optDictionary!["C"] = "charlie"

If you don't use the !, you'll get 

error: '[String : String]?' does not have a member named 'subscript'

Similarly, I use the ! to get to keys

for k in optDictionary!.keys {    

The use of Optionals can be avoided if you can initialize the data structures up front.  Here is the example rewritten without Optionals and unwrapping.

var d = [String: String]()

d["A"] = "alpha"
d["B"] = "bravo"
d["C"] = "charlie"

for k in d.keys {

Additionally, I can avoid the ! unwrapping by working with d as a non-Optional and assigning later.

optDictionary = d

All things being equal, it's best to avoid the Optionals.  However, in my porting project, I am driving some logic from nil checks.  To get the project finished, I'm carrying this logic forward.  I am considering a refactor that would address this once I verify the functionality.

Functions that Return Optionals

pathForResource() is an NSBundle call that returns an Optional.  As such, this won't work

// won't compile
var usStatesPath1 : String = NSBundle.mainBundle().pathForResource("USStates", ofType: "plist")

usStatesPath1 is not an Optional, so there is a mismatch in return values.  Similarly, this won't work either because the implicit type from the "var" line is String (not String?)

// won't compile
var usStatesPath2 = ""
usStatesPath2 = NSBundle.mainBundle().pathForResource("USStates", ofType: "plist")

This can be fixed by making an explicit declaration

// ok
var usStatesPath3 : String? = ""
usStatesPath3 = NSBundle.mainBundle().pathForResource("USStates", ofType: "plist")

By using the unwrap operator !

// ok
var usStatesPath4 = ""
usStatesPath4 = NSBundle.mainBundle().pathForResource("USStates", ofType: "plist")!

By using an implicit declaration based on the Optional

// ok
var usStatesPath5 = NSBundle.mainBundle().pathForResource("USStates", ofType: "plist")

This means that when working with core calls, you should check the documentation.  If you see something like this


func pathForResource(_ nameString?,
              ofType extensionString?) -> String?

Know that you're working with an Optional because the call can return nil and declare your variables appropriately.

No comments:

Post a Comment