Swift Optionals? Don’t Forget to Unwrap!

There is a compiler error that throws me off every time I see it.  It takes the form,

‘ClassName?’ does not have a member named ‘memberName'

This can happen when you’ve declared a variable as an optional, but forget to unwrap that optional when you attempt to call a method on it.

For example, given this class definition:

 1class Bird {
 2	var family: String
 3	var color: String
 4 
 5	init(family: String, color: String) {
 6		self.family = family
 7		self.color = color
 8	}
 9 
10	func isSwift() -> Bool {
11		return self.family == "Apodidae" ? true : false
12	}
13}

If, say in a ViewController, I declare a variable that I intend to reference an optional Bird instance like this:

1var birdInstance: Bird?

And then I later initialize this variable with a Bird instance, perhaps in viewDidLoad() :

1birdInstance = Bird(family: "Apodidae", color: "Black")

If I attempt to invoke the isSwift method on the birdInstance later  on, I’ll get a compiler error:

1println(birdInstance.isSwift())

error: ‘Bird?’ does not have a member named ‘isSwift'

This may seem pretty basic — after all, I declared the birdInstance as an optional and I know optionals need special treatment.  How could I make this mistake??  Perhaps this is just a consequence of my current stage in life, trying to code in 15-30 minute spurts with my 1 1/2 year old running around, haha.

We all deal with this though:  it’s fairly easy to write some code and come back to it later and not remember how you declared your variable in an earlier coding session.  Then when you’re presented a message saying that your class doesn’t have a member named “___”, you immediately go to the class definition and see the function there, plain as day.  It’s easy to spend 3-5 minutes scratching your head thinking, “What in the world??!” … And then you realize – it’s that optional declaration that you forgot to handle.

To fix this, of course, you can do any number of things, depending on your situation.

Force unwrap the optional and invoke the method:
1birdInstance!.isSwift() // Force unwrapped -- **CAUTION** make sure that birdInstance gets instantiated before you do this, or you'll get a runtime error
Employ optional chaining and invoke the method:
1birdInstance?.isSwift()
Declare the variable as implicitly unwrapped optional, then invoke the method later without extra exclamation or question marks:
1var birdInstance: Bird! // Implicitly unwrapped -- **CAUTION** make sure that birdInstance gets instantiated before you use it, or you'll get a runtime error
2birdInstance = Bird(family: "Apodidae", color: "Black")
3
4// Some time later, invoke isSwift
5
6println(birdInstance.isSwift())

A blog post by Peter Witham over at CompileSwift was the article that caused me to think, “OH!  I haven’t done anything with my optional…that’s the problem”.  Credit to you, sir, for your post!

comments powered by Disqus