Many Swift developers will reach for map when they want to transform the elements of an array:

let twos = (1...10).map { $0 * 2 }
twos // [2,4,6 ... 20]

however fewer developers use map with an Optional:

// UIImage(named:) has a signature: String -> UIImage?
let imageSize = UIImage(named: "anImage").map {$0.size}
imageSize // a 'CGSize?' or more explicitly 'Optional<CGSize>'

This is only of limited interest for Optional as Swift has simpler built-in syntax for handling such cases. The code below is equivalent to the above but I think most people will agree the code is simpler and more idiomatic:

let imageSize = UIImage(named: "anImage")?.size
imageSize // a 'CGSize?' identical to 'map' example

In both these cases you can think of map as operating on contained or wrapped values. In the first example the container is an Array; in the second example the container is an Optional. This becomes more interesting when you interact with other types such as:

For example using map on a Result value:

// transforms a Result<Int, JSONError> to a Result<String, JSONError>
let idResult = intForKey(json, key:"id").map { id in String(id) }

Using map on a Future:

// open() returns Future<TextDocument, AnyError>
document.open().map {updateView ($0)}
}

In all these cases map is operating on the element inside the container type; where the container type is one of: Array, Optional, Result, Future.

flatMap

Consider the difference between map and flatMap below:

// map:
let arrayOfArrayOfChars = ["array", "of", "arrays"].map {Array($0.characters)}
arrayOfArrayOfChars // [["a", "r", "r", "a", "y"],["o", "f"],["a", "r", "r", "a", "y", "s"]]

// flatMap:
let arrayOfChars = ["array", "of", "characters"].flatMap {Array($0.characters)}
arrayOfChars // ["a", "r", "r", "a", "y", "o", "f", "c", "h", "a", "r", "a", "c", "t", "e", "r", "s"]

when the function passed to map has a signature T -> [T] and you want the result of the mapping to be [T] not [[T]] replace map with flatMap. Again generalising Array to any container type, the same holds true for Optional:

// UIImagePNGRepresentation has the type signature: UIImage -> NSData?

let doubleOptionalData = UIImage(named: "anImage").map(UIImagePNGRepresentation)
doubleWrappedData // Optional<Optional<NSData>> or NSData??

let optionalData = UIImage(named: "anImage").flatMap(UIImagePNGRepresentation)
optionalData //  Optional<NSData> or NSData?

Here flatMap ensures we only have one level of Optional container.



A final example from a live project. I’ve written the same method twice, once in idiomatic Swift, once using map and flatMap.

Idiomatic version:

func readSampleText() -> String {
    guard let
        assetData = NSDataAsset(name: "SampleText1"),
        text = NSString(data:assetData.data, encoding: NSUTF8StringEncoding) else {
            return ""
    }
    return text as String
}

map, flatMap version:

func readSampleText() -> String {
    return NSDataAsset(name: "SampleText1").flatMap { assetData in
        NSString(data:assetData.data, encoding: NSUTF8StringEncoding)
    }.map { text in
        text as String
    } ?? ""
}

Final thoughts

For Optional values, I think the idiomatic version wins by virtue of being understood instantly by other Swift developers. However the map and flatMap version is often the best approach when using other container types such as Result and Future . I hope that over time the use of map and flatMap on values other than Array will become familiar to mainstream Swift developers. I find that when using map and flatMap, my code becomes simpler as I think in terms of data transformation rather than state.

See also