Swift: When the Functional Approach is NOT Right

Yesterday, I spotted what I considered to be a big Swift code smell in the codebase.

This immediately screamed out to me as a perfect opportunity to refactor into a functional solution! Wohooo! I get to do functional programming!!! Yes, I was that excited about it.

So the obvious solution was to use map here:

I was pretty proud of myself. It’s one line AND functional. But then this failed one requirement that I needed for animating the images – the array had to be an array of UIImages [UIImage], but this returned an array of OPTIONAL UIImages [UIImage?].

So I googled around, and the accepted functional solution for getting the array of non-optional UIImages was this:

This works, but I was just not happy about this. It’s long, clunky, and I hate having to force unwrap the optional here even though it is safe. The main issue, however, is that it just doesn’t seem as simple or efficient as the original for-loop. This can be seen clearly in the playground:

Maybe I’m doing this wrong, but I ended up keeping the for-loop. Lesson learned: sometimes the for-loop is the right solution in Swift. Of course I might be missing something here, so happy to hear your thoughts in the comments!


As several have pointed out in the comments below and on Twitter, using Swift 2.0’s flatMap is the functional solution here:

I’ll be honest here though, and say that every time I read something about flatMap, I still don’t fully understand what it does or how it works. It’s just not coming naturally to me like map or filter does. I have no idea why it works here, so I’ll have to research this more.

This brings me to the conclusion that again, I’m not sure this is the best way for code readability. If someone else who is less knowledgeable in Swift and functional programming looks at my code, will they understand what is happening here? Meanwhile, I know for sure everyone will understand the for-loop solution at first glance.

Enjoy the article? Join over 20,000+ Swift developers and enthusiasts who get my weekly updates.

  • Jason R Tibbetts

    Is it possible to avoid the filter() and second map(), and instead just force-unwrap each UIimage as it’s constructed?

    • Piotr Tobolski

      No, some images may be missing.

      • Jason R Tibbetts

        Depends on the scenario. If these images are loaded from your project, a nil image would be a fail-fast way to find out that you haven’t set the project up correctly. But if you’re loading the images remotely, then yes, they may be nil.

  • Alessandro Passaro

    In Swift 2.0 I think you can just use flatMap:

    func flatMap(@noescape transform: (Self.Generator.Element) -> T?) -> [T]

    Return an Array containing the non-nil results of mapping transform over self.

    • Jessy Catterwaul

      I think that’s 1.2 or earlier.

      • Alessandro Passaro

        Can’t check right now, but I think this particular overload (with the Optional return type) was added in 2.0

  • Piotr Tobolski

    You can also use reduce:

    let minionImagesReduced = [Int](1…7).reduce([UIImage]()) {
    if let minionImage = UIImage(named: “minionIcon-($1)”) {
    return $0 + [minionImage]
    return $0

    Or flatMap:

    let minionImagesFlatMapped = [Int](1…7).flatMap { idx -> [UIImage] in
    if let minionImage = UIImage(named: “minionIcon-(idx)”) {
    return [minionImage]
    return []

    Both are functional and have the same loop count but neither looks better than original solution.

  • Jonathan Simmons

    Flatmap is definitely the way to go here.

    By the way, I would say that taking a”functional approach” is more about how you decompose the problem into ‘pure’ (no side-effect) functions, rather than whether you can use existing ‘library’ functions like ‘map’, ‘reduce’ etc. By which I mean – often to solve a problem in a ‘functional’ way,you might need to write your own functions to do it. But have a look to see if there’s a standard one available first 😉

  • Chris M. Böddecker

    Use the “third kind of flatMap”:

    let minionImagesFlatMapped = [Int](1…7).flatMap { UIImage(named: “minionIcon-($0)”) }


  • Bret Dahlgren

    I don’t count numbers of times in playground. The playground can have really poor performance for functional programming. But it’s not optimized at all. I agree with everyone else that flapMap is preferable.

  • Oleg

    Swift is starting to suspiciously look like Scala. Where you have to flatmap over Future(Future(Option(Future(Object)))) . But hey type safety and right.

  • kgelner

    Although an interesting point of debate as to how to build the array, shouldn’t the code be loading the images based on how many it finds by name in the bundle, rather than having a hard-coded number to load?

  • F U

    Great, iOS programming is devolving into Perl. Look what I can do on one line!

    Two years later, someone else looks at your awesomely terse code and has to spend a day trying to figure out what it does. Readability doesn’t get enough credit.

  • Perluete

    flatMap to the rescue

  • Jung Min Ahn

    I use Flatmap, maybe it is solution.

    let result = [Int](1…7).flatMap {

    flatMap(UIImage(named: “minionIcon-($0)”), { [UIImage(named:”minionIcon-($0)”)] }) ?? []


  • Mark Patterson

    I like the look of the flatMap solution, it seems to be a natural fit, except that you are required to generate an array when the original loop worked on a range. Wouldn’t a flatMap extension to ranges be both clearer and more efficient?

    • Mark Patterson

      In fact, struct Range implements the protocol SequenceType which implements flatMap, so instead of wasting valuable natural resources allocating memory for an array, I think this would work:

      Range(start: 1, end: 8).flatMap { UIImage(named: “minionIcon-($0)”) }
      (8 because the end of a range is the first value not included)

      But of course, that could be made more succinct like this:
      (1…7).flatMap { UIImage(named: “minionIcon-($0)”) }

      • Thanks! map only worked on the array, glad I can get rid of [Int](1…7) – didn’t like that at all!

        • Just realized map also works on ranges in Swift 2.0. Was originally working in Swift 1.2 for this. Yay protocol extensions!

      • Updated my blog post to take out all the [Int](1…7). Thanks again!

  • I’ve had similar issues getting to know flatMap and found that its hard to discern simply from playing around with it the overall purpose. Sometimes it behaves like map, sometimes it doesn’t.

    So far I’ve come to the following understandings:

    1. it can be used to reduce an array of optionals to non-optionals, e.g. [String?] to [String], as you’ve done here

    2. it can reduce nested arrays into a single array, e.g. [[1,2,3],[4,5,6]] to [1,2,3,4,5,6]

    3. it works at a level one deeper than map (as seen in point 2), which means that if you pass it an array that is not multidimensional then it works but you shouldn’t do it

    4. flatMap when applied to a Swift optional flatMap has special knowledge about the enum and sees more than just the enum (as admitted by Apple in the docs). So if I were to use the Swift std Optional enum like so:


    I’d get back an array containing [“String”]

    But if I were to roll my own optional enum:

    enum MyOptional {
    case Some(T), None

    and do the same thing


    I’d get back exactly the same optional array as I started with. Whereas if I wanted to replicate the flatMap behaviour of the std Optional enum I’d have to do something like this:

    [MyOptional.Some(“String”),MyOptional.None].reduce([String](), combine: {
    switch $1 {
    case .Some (let s):
    return $0 + [s]
    case .None:
    return $0

    … and not use flatMap at all, because flatMap like map always needs one for one returned. And this is where I eventually get to the point I’m making, which is that this kind of special behaviour makes flatMap tricky to grasp.

    5. Finally, I understand that I need to read more about flatMap.

    • Thanks for writing this. It really helps! Glad I’m not the only one confused about flatMap. In my mind, I think of use case #2 when using it, so it’s hard for me to grasp all the other points you’ve mentioned, especially the ones with Optionals.

      • What I think it boils down to is that optionals are a special case. flatMap will reduce an array of optionals to an array of regular instances, and it’s important to know this, but that use can be thought of separately to its day job which is as in point 2.

      • Lars-Jørgen Kristiansen

        Instead of thinking nested arrays, think arrays of contained values. It flattens the array by pulling the values out of the container and then maps over the array.

        let arr1: [[Int]] = [[], [2]]
        let arr2: [Optional] = [.none, .some(2)]

        These to are pretty much the same, in the first the container is an array and in the second it is an optional.

        • Dave Yost

          The second line doesn’t compile.

          • Lars-Jørgen Kristiansen

            No, try:
            let arr2: [Int?] = [.none, .some(2)]

            It was just a quick example.. You would usually just write:

            let arr2 = [nil, 2]

  • andypf

    Hello Natasha,
    Im not sure if you ever get these
    emails but I figured its worth a shot. I hope I am not taking up too
    much of your time but I just had a quick question for you. I have been
    following your page for cs106a assignments and have just finished up the
    class. I loved it and had a really great time. I noticed that you were
    doing it back in 2011 and now you have become an ios engineer.

  • Syuleyman Mratsov

    How is the first one a code smell? Also I believe that the first segment will traverse the elements more than seven times due to append, not sure though.