Macros for FlyingFox allowing handlers to annotate functions with routes.
FlyingFoxMacros can be installed by using Swift Package Manager.
Note: FlyingFox requires Swift 5.9 on Xcode 15+. It runs on iOS 13+, tvOS 13+, watchOS 8+, macOS 10.15+ and Linux. Windows 10 support is experimental.
To install using Swift Package Manager, add this to the dependencies:
section in your Package.swift file:
.package(url: "https://github.com/swhitty/FlyingFox.git", .upToNextMajor(from: "0.16.0")),
.package(url: "https://github.com/swhitty/FlyingFoxMacros.git", .upToNextMajor(from: "0.1.0"))
Methods can be annotated with HTTPRoute
to automatically syntesise a HTTPHandler
.
import FlyingFox
import FlyingFoxMacros
@HTTPHandler
struct MyHandler {
@HTTPRoute("/ping")
func ping() { }
@HTTPRoute("/pong")
func getPong(_ request: HTTPRequest) -> HTTPResponse {
HTTPResponse(statusCode: .accepted)
}
@JSONRoute("POST /account")
func createAccount(body: AccountRequest) -> AccountResponse {
AccountResponse(id: UUID(), balance: body.balance)
}
}
let server = HTTPServer(port: 80, handler: MyHandler())
try await server.start()
The annotations are implemented via SE-0389 Attached Macros.
The macro synthesises conformance to HTTPHandler
delegating handling to the first matching route. Expanding the example above to the following:
func handleRequest(_ request: HTTPRequest) async throws -> HTTPResponse {
if await HTTPRoute("/ping") ~= request {
ping()
return HTTPResponse(statusCode: .ok, headers: [:])
}
if await HTTPRoute("/pong") ~= request {
return getPong(request)
}
if await HTTPRoute("POST /account") ~= request {
let body = try await JSONDecoder().decode(AccountRequest.self, from: request.bodyData)
let ret = createAccount(body: body)
return try HTTPResponse(
statusCode: .ok,
headers: [.contentType: "application/json"],
body: JSONEncoder().encode(ret)
)
}
throw HTTPUnhandledError()
}
@HTTPRoute
annotations can specify specific properties of the returned HTTPResponse
:
@HTTPRoute("/refresh", statusCode: .teapot, headers: [.eTag: "t3a"])
func refresh()
@JSONRoute
annotations can be added to functions that accept Codable
types. JSONDecoder
decodes the body that is passed to the method, the returned object is encoded to the response body using JSONEncoder
:
@JSONRoute("POST /account")
func createAccount(body: AccountRequest) -> AccountResponse
The original HTTPRequest
can be optionally passed to the method:
@JSONRoute("POST /account")
func createAccount(request: HTTPRequest, body: AccountRequest) -> AccountResponse
JSONEncoder
/ JSONDecoder
instances can be passed for specific JSON coding strategies:
@JSONRoute("GET /account", encoder: JSONEncoder())
func getAccount() -> AccountResponse