Chapter 16: Can't create AcronymPostHandler handler

Hello all. Is anyone else attempting to plough through the V3.0.0 copy of the book beyond the leaf chapters? I have managed to bend some of the examples into something that works in Vapor 4 but am completely stumped by a compiler error in chapter 16.

The createAcronymPostHandler function on page 254 looks like this in my editor:

func createAcronymPostHandler(_ req: Request) throws -> EventLoopFuture<Response> {
    let data = try req.content.decode(CreateAcronymData.self)
    let acronym = Acronym(
        short: data.short,
        long: data.long,
        userID: data.userID
    )
    return acronym.save(on: req.db).map { acronym in
        guard let id = acronym.id else { throw Abort(.internalServerError) }
        return req.redirect(to: "/acronyms/\(id)")
    }
}

But the code fails to compile on the acronym.save() line with the following error:

Invalid conversion from throwing function of type '(Void) throws -> Response' to non-throwing function type '(Void) -> (Response)'

If I put this dummy line in I can get Xcode to tell me what acronym.save() is returning:

Screenshot 2020-11-17 at 16.35.31

Aargh! I expected an EventLoopFuture<Acronym>

If I break the rules still further I get a bit more information:

Screenshot 2020-11-17 at 16.49.10

This time the error is short and sweet: Value of tuple type 'Void' has no member 'id'

So the compiler is definitely not intending to pass anything through to the closure. How can I map this result to an Acronym and get it into a form I can work with?

Apologies if (Tim) you’re fielding loads of these queries. Hopefully this is a simple incantation which I haven’t stumbled across yet. If the answer is “wait for the updated chapters” then that will have to do!

Thanks for reading.

save(on:) now returns a ELF<Void> (because it’s all reference types and it avoids adding extra work you might not need). Additionally, map doesn’t allowing throwing now, but you can use flatMapThrowing (which is a throwing variant of map and not flatMap, I know, confusing…). So the code will end up looking like:

return acronym.save(on: req.db).flatMapThrowing {
  guard let id = acronym.id else {
    throw Abort(.internalServerError)
  }
  return req.redirect(to: "/acronyms/\(id)")
}

Thanks Tim! Now you mention it, I remember flatMapThrowing getting a section in Ch. 4 but this is the first time in the narrative that it’s been required so I totally forgot that part :roll_eyes:

Loving the book so far!

1 Like