Search code examples
swiftfuturevapor

Swift Vapor: catchMap not being awaited


I have the following section of code in a controller, which queries MailGun to send an email, and then should ideally wait for a response before returning in the controller. As configured right now, this should fail as I have intentionally broken my MailGun configuration. But currently the controller is returning a success status, because the .catchMap functionality is not being properly awaited, and I'm not sure how to correctly structure my code so that it is.

return emailTemplate.render(emailData, on: req).map { message -> Future<Response> in
    let deliveryService = try req.make(EmailDeliveryService.self)
    return try deliveryService.send(message, on: req)
}.catchMap { error in
    /// this is not being awaited, and no abort is thrown before the request returns
    throw Abort(.internalServerError, reason: "Error sending email.")
}.transform(to: savedObj)

The function that should be properly awaited, deliverService.send, has the method signature:

func send(_ message: EmailMessage, on container: Container) throws -> Future<Response>

How can I appropriately structure this code to correctly catch an error that is returned by the result of the deliveryService.send method?


Solution

  • If your render() method signature is something like this:

    func render(…) -> Future<String>
    

    then, I think you need to use flatMap instead of map in:

    return emailTemplate.render(emailData, on: req).map // <—
    

    Right now, the catchMap and transform method are receiving a Future<Future<Response>> because map only transform the encapsulated data of the given future which go like this:

    Future<String> -map(String -> Future<Response)-> Future<Future<Response>>
    

    Using flatMap, it will flatten the double Future, which is the purpose of this method which lead to:

    Future<String> -flatMap(String -> Future<Response)-> Future<Response>
    

    Then, the catchMap will be able to access the error.