Search code examples
swiftgcdwebserver

How to understand this GCDWebServer swift unit test code?


I have come across this code:

class WebServerTests: XCTestCase {
    let webServer: GCDWebServer = GCDWebServer()
    var webServerBase: String!

    /// Setup a basic web server that binds to a random port and that has one default handler on /hello
    private func setupWebServer() {
        webServer.addHandlerForMethod("GET", path: "/hello", requestClass: GCDWebServerRequest.self) { (request) -> GCDWebServerResponse! in
            return GCDWebServerDataResponse(HTML: "<html><body><p>Hello World</p></body></html>")
        }

I am confused by the webServer.addHandlerForMethod part. It seems to me it is already a complete function call (webServer.addHandlerForMethod("GET", path: "/hello", requestClass: GCDWebServerRequest.self)). Therefore I do not understand why it is followed by a closure ( {(request) -> ... )

EDIT: Clarify what I do not understand

According to the documentation on https://github.com/swisspol/GCDWebServer, the function signature in obj-c is:

[webServer addDefaultHandlerForMethod:@"GET"
                         requestClass:[GCDWebServerRequest class]
                    asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {

Therefore I expect its swift counterpart will be called somewhat like this:

        webServer.addHandlerForMethod("GET", path: "/hello", requestClass: GCDWebServerRequest.self, { (request) -> GCDWebServerResponse! in
            return GCDWebServerDataResponse(HTML: "<html><body><p>Hello World</p></body></html>")
        })

i.e. the handling of the incoming request is passed as the third parameter. But since the closure comes after the closing ')', it does not look like part of the function call at all.

Why the function signature is mapped from obj-c to swift this way?


Solution

  • In Swift, you can use this syntax if the last argument to a function is a closure. Here's the example from the Swift language guide section about closures (scroll down to Trailing Closures):

    func someFunctionThatTakesAClosure(closure: () -> ()) {
        // function body goes here
    }
    
    // here's how you call this function without using a trailing closure:
    
    someFunctionThatTakesAClosure({
        // closure's body goes here
    })
    
    // here's how you call this function with a trailing closure instead:
    
    someFunctionThatTakesAClosure() {
        // trailing closure's body goes here
    }
    

    And then there's also this note:

    If a closure expression is provided as the function’s only argument and you provide that expression as a trailing closure, you do not need to write a pair of parentheses () after the function’s name when you call the function.

    This means it's also legal to write this:

    someFunctionThatTakesAClosure {
        // closure body
    }
    

    … which helps provide a nice meta-programming syntax. For example:

    let lock = NSLock()
    
    func locked(closure: () -> ()) {
        lock.lock();
        closure()
        lock.unlock();
    }
    
    locked {
        NSLog("Hello, world!")
    }