{infiniteZest}
// Articles. Tutorials. Utilities.
Home  |   Search  |   Login  
Categories Skip Navigation Links
New / All
AJAX
Apple
ASP.NET
.NET
Git
Google / Android
Python / IronPython
Miscellaneous
SQL Server
Swift - Optional Chaining
Summary
This article talks about optional chaining in swift where multiple optionals can be chained together and fail gracefully by returning nil when one of them is nil.
 
Table of Contents

Chaining Properties

Forcibly Unwrapping

Safe Unwrapping

Optional Chaining

 
Article Series
Previous Article:
Test Your Knowledge: Swift - Nil Coalescing Operator
This article is part of the Series:
Swift - Optionals
Next Article:
Test Your Knowledge: Swift - Optional Chaining

Chaining Properties

You will run into this scenario quite often: in order to get the value of a property, you will start with a high level object first and get one of its properties; then get a property of that property object; then go down deep again to get the property of this second level property; and so on. Some times you go could down multiple levels deep.

If you were to check each optional property whether it’s a nil before proceeding down, we would be using multiple levels of if-else optional binding clauses to safely get what we want. This can be become really clumsy and we will see how optional chaining resolves this issue.

Let’s first see a situation where properties can be chained multiple levels deep:

// city structure contains nickname that is Optional String
struct city {
  var name: String
  var nickname: String?
}

// nickname for nyc is not provided; and it’s ok because
// nickname is an optional String
let sf = city(name: “San Francisco”, nickname: “Fog City”)
let nyc = city(name: “New York”, nickname: nil)

// cities is a dictionary: [String: City]
// key is city code, value is city object
let cities = [“SFO”: sf, “NYC”: nyc];

In the above code (going from bottom to top), we have a dictionary that has city code and city object as key-value pairs. Since the city struct has nickname as optional String, we are not providing nickname for NYC.

Forcibly Unwrapping

Let’s say we want to know how many characters are there in the nickname of of a city. String type has a collection called characters (of type CharacterView) which has a count property. This count property gives us the number of characters.

In order to get this character count, we can start from the dictionary to get the appropriate city object, then get the nickname string, and then get characters collection on it and then finally use the count property to get the count of the characters in the nickname string.

We can chain all these properties to get the desired value. In this list, two of them return optional values — dictionary returns optional value (in this case city?) and nickname returns optional string. For these two we need to use ! to unwrap them.

let sfcount = cities[“SFO”]!.nickname!.characters.count
// sfcount will be 8 - for “Fog City”

In the above code:

cities[“SFO”] returns an variable of type city? - and we used ! to unwrap it to get city variable.

Then the type of nickname is String? — we unwrapped that to get non-optional String variable.

Then characters and count don’t return optionals, so no unwrapping is necessary.

In the above case code works fine, because no nil values have been encountered. Let’s look at the following case:

let nycount = cities[“NYC”]!.nickname!.characters.count
// Crash - fatal error: unexpectedly found nil while unwrapping an Optional value

If we use the same flow on NYC, we will get a crash. Because we didn’t provide a nickname for NYC as it was optional. And when force unwrapped (by using !), it found nil and crashed.

Let’s do another scenario, where we ask for a city that’s not there in the dictionary.

let chicount = cities[“CHI”]!.nickname!.characters.count
// Crash - nil while unwrapping

This also crashes, because the dictionary didn’t find the key CHI. So, it returns nil. And we are trying to unwrap a nil and that causes crash.

Safe Unwrapping

Let’s see how we can unwrap safely by using the Optional Binding method discussed before:

// First see if the dictionary has the given city
if let city = cities[“SFO”] {

  // Then see if the city has a nickname
    if let nickname = city.nickname {

      // Then get the characters and count
      // No need to unwrap them because they are non-optionals
      let sfcount = nickname.characters.count
  }
}

As you can see above you would have to use one if-let statement for each of the optionals in the chain. Here it is only two levels deep, but when there are more optionals to go through then getting to the final value safely will have many levels of optional binding.

Optional Chaining

In order to solve the above problem, we can use optional chaining. This is done with the ? operator next to the optional variable (e.g. nickname?). In defining an optional variable we use the ? next to the type (e.g. var nickname: String?). Here we use it next to the variable itself.

So, to safely get the count in the above scenario:

let sfcount = cities[“SFO”]?.nickname?.characters.count

In forced unwrapping we use !, and in optional chaining we use ?. So, what does this mean?

With ? (optional chaining), when Swift encounters a nil, it will return a nil value for the entire expression. It will not crash. In order to accommodate this, the variable sfcount will be an optional of the type that is being returned at the end of the optional chaining.

let sfcount = cities[“SFO”]?.nickname?.characters.count
// Type of sfcount: let sfcount: Distance?
// Value: non-nil in this case: 8 (for Fog City)


let nycount = cities[“NYC”]?.nickname?.characters.count
// Type of nycount: let nycount: Distance?
// Value: nil in this case - no nickname added to dictionary

As you can see above, this value returned from optional chaining is also optional (to accommodate a potential nil value being returned). So, if you want to further use that value safely, you would need to use a pattern like Optional Binding.

But you will use this only one time, as opposed to each time for the optional in the chain:

if let sfcount = cities[“SFO”]?.nickname?.characters.count {
  let display = “Nickname has \(sfcount) characters.”
  print(display)
  // Prints: Nickname has 8 characters.
}

In this case sfcount can only be used inside the if block. Since we are using the if-let pattern, sfcount will no longer be an optional.

Optional chaining works in a similar way if there are methods or subscripts in the middle which return an optional value. You will need to use ? for the whole expression to safely return nil or non-nil without crashing.

Take a Quick Quiz on this Article

1. var display = customer.address!.line2! - address and line2 are optionals. What might be the problem here?



: Forced Unwrapping
Question 1 of 5
Article Series
Previous Article:
Test Your Knowledge: Swift - Nil Coalescing Operator
This article is part of the Series:
Swift - Optionals
Next Article:
Test Your Knowledge: Swift - Optional Chaining
Bookmark and Share This

More Articles With Similar Tags
icon-swift-test.jpg
Test on optional chaining to safely return a nil or non-nil value from a series of optionals chained together.
icon-swift-article.jpg
This article talks about unwrapping the optionals in Swift implicitly and the circumstances where you would use this functionality.
icon-swift-article.jpg
This article discusses the safer ways of unwrapping an optional in Swift that includes Optional Binding.
icon-swift-article.jpg
This article talks about the Nil Coalescing Operator (??) in Swift.
icon-swift-article.jpg
Summary of three operators (?, !, ??) used in optionals in Swift.
About  Contact  Privacy Policy  Site Map