Understanding FlatMap on Optionals

abdul ahad
3 min readMay 27, 2024

--

Photo by Marius Serban on Unsplash

The flatMap function for optionals in Swift is used to transform an optional value and return a new optional. It is particularly useful when the transformation itself might return an optional, and you want to avoid nested optionals (Optional<Optional<T>>).

Here’s a step-by-step explanation of how flatMap works with optionals:

  1. If the original optional is nil, flatMap returns nil.
  2. If the original optional contains a value, flatMap applies a transformation (provided as a closure) to that value.
  3. The transformation closure returns an optional. If it returns nil, the result of flatMap is nil. If it returns a non-optional value, the result of flatMap is that value.

Example Scenario

Let’s say we have a function that tries to get data from a URL, and then another function that tries to convert that data into an image.

Here’s an example:

  1. fetchData(url:): Returns an optional Data from a URL.
  2. flatMap(UIImage.init):
  • UIImage.init(data:) is a failable initializer for UIImage. It takes Data and returns an optional UIImage?.
  • flatMap is used to transform the optional Data returned by fetchData(url:) into an optional UIImage
import UIKit

// Function to fetch data from a URL
func fetchData(url: URL) -> Data? {
// Simulate fetching data (this would usually be a network call)
return try? Data(contentsOf: url)
}

// Example URL (ensure this URL points to a valid image in a real scenario)
let url = URL(string: "https://example.com/image.png")!

// Using flatMap to chain optional transformations

let image = fetchData(url: url).flatMap(UIImage.init)

// Check the result
if let image = image {
print("Image successfully loaded and converted")
} else {
print("Failed to load or convert image")
}

How It Works

  • If fetchData(url: url) returns nil (meaning data fetching failed), flatMap will not attempt to initialize a UIImage and will directly return nil.
  • If fetchData(url: url) returns non-nil Data, flatMap applies UIImage.init(data:) to this data.
  • If UIImage.init(data:) successfully creates an image, flatMap returns this UIImage.
  • If UIImage.init(data:) fails (e.g., the data does not represent a valid image), flatMap returns nil.

This results in a single optional UIImage? that is either the successfully created image or nil if any step failed.

What if we use MAP?

as you can see we end up with nested optional so if you want to avoid nested optionals (Optional<Optional<T>>) use flatmap.

Equivalent Expanded Code

To better understand how flatMap simplifies the code, here's the equivalent logic without using flatMap:

let data = fetchData(url: url)
let image1: UIImage?

if let data = data {
image1 = UIImage(data: data)
} else {
image1 = nil
}

This does the same thing:

  • It fetches the data.
  • Checks if the data is non-nil.
  • If non-nil, attempts to create a UIImage from the data.
  • Otherwise, sets the image to nil.

Conclusion

The flatMap(UIImage.init) expression is a concise way to transform optional Data into an optional UIImage while gracefully handling the potential failures at each step:

  • Fetching the data might fail.
  • Converting the data to an image might fail.

By using flatMap, you avoid nested optionals and make the code cleaner and more readable.

--

--

abdul ahad
abdul ahad

Written by abdul ahad

A software developer dreaming to reach the top and also passionate about sports and language learning

No responses yet