In the first month and a half of using Swift, I could argue the optional value is the most difficult new concept. It isn’t hard, just something different to wrap one’s head around. That Apple’s documentation peppers information about optionals in two different e-books without consolidating the information doesn’t help. I decided to combine what I know about optionals into one post as a reference.
[edited for Swift 1.2 5/1/15]
1. You need nil so you need optionals
There are many cases where we need a state that says “this does not exist yet.” When I am deciding on breakfast for example I’m thinking about yogurt, coffee and chocolate croissants. I’m not thinking about Pizza. My choice for a pizza for lunch is not even on my mind. My choice for pizzaType
is nil. The same is true in code. There are places where things just don’t exist yet, and we need a way to show it. That is what optionals are for. Optionals change any type into a type with a flag for the nil state and a value. If the optional is nil, there is no value, if the optional is non-nil there is a value.
we declare an optional like this:
var pizza:Pizza?= Pizza()
This would make an optional from the Pizza class. Swift can also do this:
var numberOfPizzas:Int? = nil
This would set an integer optional to nil. Optionals are a lot like pointers in Objective-C. However in Objective-C you can’t put a pointer to an integer, only classes. In Swift any type, not just classes, can be an optional.
When declaring optionals, if starting with nil, you can leave off the nil assignment if you wish.
var numberOfPizzas:Int?
2. Unwrap before use
Assigning to an optional is just like assigning to anything else.
numberOfPizzas = 5
However getting data from an optional is slightly more challenging. As I mentioned earlier, optional values have two parts, one for nil and one for the value. If we retrieve a value from numberOfPizzas
like this:
var myNumberOfPizzas:Int = numberOfPizzas
The result is a compiler error:
Value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'?
Here we need to use the force unwrap operator !
to get an integer value out of the optional
var myNumberOfPizzas:Int = numberOfPizzas!
The compiler error message now disappears.
3. Always check for nil
If we run now while myNumberOfPizzas
is nil, we get a run time error
fatal error: Can't unwrap Optional.None
We cannot unwrap an optional that is nil. In order to prevent this, first check an optional for nil, then get its value using the force unwrap operator !
var myNumberOfPizzas:Int = 0 if numberOfPizzas != nil{ myNumberOfPizzas = numberOfPizzas! }
Do this for anything you make an optional. Also check a class that is optional before using the methods and properties of the class.
4. Don’t check for nil to break your code
There are times you are absolutely certain there will be a non-nil optional. If that optional is nil, something is very wrong. This is the big exception to rule #3. You want to crash the code right here, and start debugging to find out why that optional is nil. For example, when working with delegates you will have some statement that call the delegate method,
@IBAction func saveColor(sender : UIBarButtonItem) { delegate!.myVCDidFinish(self, text: colorLabel!.text!) }
If the delegate was not set, this will crash, letting you know to go set the delegate property.
5. Implicit optionals are still optional
Although rule 4 is an exception to rule 3 to check for nil, it ends up there are several times we know an optional is non-nil (rule 6). For ease of use, Swift allows you to declare a implicitly unwrapped optional. For example:
var numberOfPizzas:Int! = 1
Makes a implicitly unwrapped optional with a value of 1. Implicitly unwrapped optionals aren’t force unwrapped. Use them the same way as non-optionals, and don’t force unwrap when accessed.
myNumberOfPizzas = numberOfPizzas
In code they will look like non-optionals. It’s easy to forget that they are optionals. They are, and as optionals if they ever go nil, you will get the run time error
fatal error: Can't unwrap Optional.None
from something that doesn’t look like an optional. Just a word of caution to watch for this, especially in light of rule 6
6. Optionals are not optional for API’s
Objective C classes, as pointers, expect a nil value. As almost everything in the API’s and frameworks is an Objective-C class, almost everything you will work with is an optional. The compiler will remind you of this very often when you try to assign something to a non-optional value that is an optional value. As you check help screens and documentation you will see definitions of API like this:
func prepareForSegue(_ segue: UIStoryboardSegue!,sender sender: AnyObject!)
The definition tells you segue
is an implicitly unwrapped UIStoryboardSegue
and sender is a implicitly unwrapped AnyObject
. For the most part, optionals in the API are implicitly unwrapped, so rule 5 above applies often when working with frameworks and API’s. Read these definitions carefully. For example, while in Swift alone you can switch around NSString
and String
rather easily, many places that an NSString
is a property in Objective-C requires String!
in Swift. For example in UILabel
, the text property’s definition is:
var text: String!
The compiler will give you errors if you forget this in some situations. In some it will not…until it is too late.
7. Cast optionals down from AnyObject
What Objective-C returns as a type id
, Swift returns as AnyObject!
. While AnyObject!
may be nice, if you are trying to get some properties or methods from your returned object, AnyObject
doesn’t work so well. Often, there is an expected subclass for the return value, such as UIButton
. For example
var myButton = UIButton.buttonWithType(UIButtonType.System)
Gives the compiler warning:
Variable 'myButton' inferred to have type 'AnyObject!', which may be unexpected
If you try to use a method of UIButton
, Xcode may not find any methods. Downcasting to UIButton
using as!
or as?
var myButton = UIButton.buttonWithType(UIButtonType.System) as! UIButton
Silences the warning, creating a non-optional UIButton
. To keep consistent with Objective-c code, this is more accurate:
var myButton:UIButton! = UIButton.buttonWithType(UIButtonType.System) as? UIButton
This code produces a implicitly unwrapped optional UIButton
. The as?
makes myButton
an optional, and the :UIButton!
makes it implicitly unwrapped.
8. Chain methods not properties
Another way to unwrap an optional is to use optional chaining. For example, if UIButton
above was:
var myButton: UIButton? = UIButton.buttonWithType(UIButtonType.System) as? UIButton
It would be a basic-flavor optional. I could try this:
myButton.setTitle("Pizza", forState: UIControlState.Normal)
I would get the following error message:
'UIButton?' does not have a member named ‘setTitle'
I could force-unwrap it like this:
myButton!.setTitle("Pizza", forState: UIControlState.Normal)
If it is nil, I would get a run-time error. Another way to unwrap and check if it is nil is optional chaining like this:
myButton?.setTitle("Pizza", forState: UIControlState.Normal)
Instead of a ! we use ? The expression, if nil, does nothing. If non-nil, it calls the method. This works great for methods, but don’t use it for assigning properties of an optional class. For example, this
myButton?.tintColor = UIColor.redColor()
gives you the error:
Cannot assign to the result of this expression
You must force unwrap the value to assign it, with the risk of a run time error unless you check it. This would be best:
if myButton != nil{ myButton!.tintColor = UIColor.redColor() }
9. Outlets are weak, weak is optional
[Edited for Xcode 6 Beta 4]
Some might argue that rule #4 applies to the last example. If myButton
was strong, that would be true. However If using outlets, anything that is an outlet is weak. Prior to Xcode 6 Beta 4 If you have in your code
@IBOutlet var myButton:UIButton
and Xcode 6 beta 4 and later if you have:
@IBOutlet var myButton:UIButton!
Xcode replaces it behind the scenes with
@IBOutlet weak var myButton:UIButton! = nil
All outlets are implicitly weak optionals. Weak references are those that can be destroyed by ARC to prevent reference cycles. Weak references need to have a state of nil for when they do not have a reference so ARC can dispose of them. Thus weak references must be optionals. The weak keyword only works on optionals and classes. If you try this:
weak var myNumber = 1
The compiler will complain with a error.
'weak' cannot be applied to non-class type 'Int'
If we use a class,
weak var pizzatoo:Pizza = Pizza()
We get a different error
'weak' variable should have optional type 'Pizza?'
To use weak
, have a class and an optional:
weak var pizzatoo:Pizza? = Pizza()
10. Use optional binding
In the control statements if
and while
the conditional statement can be replaced with an assignment of a constant. This is known as optional binding
. For example:
if let aButton = myButton{ aButton.tintColor = UIColor.redColor() let type = aButton.buttonType let title = aButton.currentTitle ... }
The constant aButton
, if myButton
is non-nil, executes the code with aButton
used as an unwrapped myButton
. This is particularly useful if you has several things you needed unwrapped all at once. With some weak references, it is the only way you can get the values. Here’s an example from my recent post on Split View Controllers. The only way to access the outlets in the detail view is through optional binding:
func configureView() { // Update the user interface for the detail item. if let detail = self.detailItem as? Pizza { //if we have an object in the detail item pizza = detail if let label = self.pizzaTypeLabel { label.text = detail.pizzaType } let pizzaSizeString = NSString(format:"%6.1fin Pizza",detail.pizzaDiameter) let priceString = NSString(format:"%6.2f sq in at $%6.2f is $%6.2f",detail.pizzaArea(),detail.unitPrice(),detail.pizzaPrice()) if let label = self.pizzaSizeLabel{ label.text = pizzaSizeString } if let label = self.pizzaPriceLabel{ label.text = priceString } } }
In this example, you have to bind IBOutlets
of UILabels
and their controller to get anything in or out of them. They are too weak to exist without the optional binding.
In later versions of Swift Apple introduced guard
, whihc works similar to if
but has a bit more flexibilty in unwrapping options. The button code above, for example would look like this
guard let aButton = myButton else { return } aButton.tintColor = UIColor.redColor() let type = aButton.buttonType let title = aButton.currentTitle ...
The guard
unwraps and assigns like any other constant, but has a handler for nil
as the else
clause block. Here I return
if myButton
is nil
and do nothing. This works the same as if
but prevents the nasty nesting we have in the configureView
function above.
Optionals are powerful, and in time most Swift Developers will think that Objective-C pointers are limited cousins to the flexibility of optionals. As developers get used to Swift, these will become second nature. Until then, hopefully these ten points will help guide the confused in how to use optionals with the least amount of frustration.
Leave a Reply