Effective Use of Optionals in Swift

Introduction

Optionals are a data type in the Swift programming language which may contain a value or may be nil. This is all covered in the Apple Swift documentation.

The trouble is that humans need to make decisions about what sign to use and Xcode usually suggests the ! — crash way.

This post’s concern is about a set of ground rules so multiple developers work with optionals the same way in a project, not yet another article on the subject.

As a quick summary about optionals:

  • Non optional types always have a value. For example var name = “My Name”; name = nil shows an error.
  • Optional types can either have a value or be nil. Nil indicates the absence of a valid value. Eg: var name: String? = “My Name”; name = nil works. Optionals are denoted by a question mark when invoking them.
  • Implicitly unwrapped optionals: var name: String! = nil are optionals that behave like non optionals. While they have a value they can be accessed it can be accessed like a non-optional, but the app crashes if it’s nil.
  • To process Optionals there is Optional Binding: if let n = name { print(\(name)) } …
  • Guard statements: guard let n = name else {return}
  • Nil-Coalescing Operator: print(“\(name ?? “the name is nil”)”)
  • And then, there is forced unwrapping (the exclamation mark)… let name: String? = nil, print(“\(name!)”)

This sounds like a lot of info, but to understand the rest of the post, all of the above should be familiar. Try them out in a playground if not comfortable with them.

Another aspect is, that apparently, multiple developers develop multiple strategies to use optionals. Some prefer overusing them, others prefer using magic numbers, others prefer leaving a conceptual difference between the meaning of a non-optional and an optional.

Reasoning about data in a project

Optionals represent values that may be missing (invalid) for some instances forever, or present for only a subset of instances of the same data structure.

There are foundation methods that return only optionals such as Date, URL initialisers and IBOutlets are force unwrapped by default. So using the force unwrap crash operator turns out to be acceptable in certain cases (crash when the user of the application can’t recover to a working state).

When learning to use optionals in Swift, one of the first apparent correlations is that the number of ! is proportional with the number of crashes an app exhibits. So what’s the sensible way to choose between !, ? or none of them?

I propose to think about a hypothetical data structure that represents a user, chose your favourite definition:

All Force Unwrapped All Optionals Some Optionals No Optionals Mixed
fullName! fullName? fullName fullName fullName!
firstName! firstName? firstName firstName firstName
lastName! lastName? lastName lastName lastName
age! age? age age age
ssn! ssn? ssn? ssn ssn!
uid! uid? uid? uid uid!

All Force unwrapped:

Almost guaranteed to experience loads of crashes.  It has no distinction between mandatory and optional properties. They are all mandatory optionals.

All Optionals:

Seems not to cause crashes as long as the data isn’t force unwrapped too much down the line. It has no distinction between mandatory and optional properties. All fields can be nil, it’s possible to have an instance of this data structure that occupies memory but all properties are nil.

Some Optionals:

Shows that a user always has a name and age and that it’s possible that other fields may be missing. A developer working with such a structure will know not to expect certain fields for all users and maybe different conditional handling should be used in different parts of the application.

No Optionals:

Makes no distinction about types, won’t have crashes, would always have some values stored, sometimes correct ones. With it the developer must always remember to check against magic constants if values were actually filled. And not making any mistakes ever is a tall order for any of us.

Mixed:

Looks like a random mixture. But in the case of the fullName, it’s possible that the implementation fills in fullName = firstName + lastName in the initialiser, so that part may not be as dangerous as it looks. Other than that, this may be the most punishing definition of all, because a developer doesn’t stand a chance to reason about the presence of values.

I advocate around the 3rd option, nice content information and little to no exclamation marks.

 

Deciding to make a variable optional

Looking through other developers’ projects, it’s possible to notice that everybody has his own understanding and preference concerning optionals.

This is in no small part because a decision must be made, and the facts taken into account can differ between developers.

I propose to strive for non-optionals. If there is a value with up to date contents, that doesn’t invalidate itself, I don’t see any reason to make it an optional:

  • If there is a way to make a value not optional without compromising the source of truth in the project, define it non-optional.
  • If a value is expected to be nil and that state is correct, make it optional. Malformed server data and bad user input falls in this category.
  • If a value is not expected to be nil ever like application constants, use force unwrapping.

Creating optionals flow chart

Deciding on how to access an optional variable

Using an optional is also a decision based on the project’s context:

  • If the particular method can’t continue without a value, guard let … else {return}
  • If there is a specific code path for the nil case , if let … else …
  • Or just show a default value show(value ?? “Default”)

Optionals in different layers of project architecture

This is another decision.

Clean architecture design should use conditionals about optionals just like regular control flow statements, at the closest level of abstraction to the data structure.

If there is a string manipulation to be done, it should be as close to the layer of the actual data. Top level objects accessing data several layers deep is always to be avoided.