Swift: The Problem with Trailing Closure Syntax

UPDATE: See this discussion on Twitter on how to best handle the need for two closures for the success / failure blocks scenario.

Let’s say you have a class with a method that takes in a closure as an argument:

class MyFunClass {
    
    func doSomething(activity: String,
        handler: (String) -> Void)
    {
        // do something
    }
    
}

When you call this method, you can do it the old regular way (autocompleted like this by default):

let myFunClass = MyFunClass()

myFunClass.doSomething("fun", handler: { (funString) -> Void in
    // do something
})

Or you can use the Swift trailing closure syntax the new and cool way:

let myFunClass = MyFunClass()

myFunClass.doSomething("fun") { (funString) -> Void in
    // do something
}

The problem comes up when a method has two closures as arguments. For example, it is a common pattern to have a success and failure handlers when networking is involved:

class MyFunClass {
    
    func doSomething(activity: String,
        successHandler: ([String]) -> Void,
        failureHandler: (NSError) -> Void)
    {
        // do something
    }    
}

Unfortunately, when two closures are involved, Swift autocompletes to this (the second closure becomes trailing automatically):

let myFunClass = MyFunClass()

myFunClass.doSomething("fun",
    successHandler: { (result) -> Void in
    // execute
}) { (error) -> Void in
    // execute
}

Of course I can now modify this manually to this:

myFunClass.doSomething("fun",
    successHandler: { (result) -> Void in
    // execute
    }, failureHandler: { (error) -> Void in
    // execute
})

But I personally find it super annoying to have to manually modify how I call my methods with two completion handlers, and it doesn’t look too much more readable to me. The method ends up just being too long!

Instead, I’ve opted out of having two closures, and instead just use one, in which case I do like using the trailing closure syntax:

class MyFunClass {
      
    func doSomething(activity: String,
        completionHandler: (result: [String]?, error: NSError?) -> Void)
    {
        // do something
    }  
}

let myFunClass = MyFunClass()

myFunClass.doSomething("fun") { (result, error) in
    // do something
}

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