Decree – A Declarative URL Request Framework Published on Aug. 08, 2019

I'm very happy to officially announce my new Swift framework called Decree. I've made several frameworks publicly available in the past, but I've never been more proud of one.

Decree allows you to make HTTP requests in a clear and type-safe way by declaring web services and endpoints. It supports all Apple platforms as well as linux. It is the culmination of a technique I've developed over many projects. Some of those projects even include both a frontend and backend implemented in Swift that get to share a single source of endpoint declarations.

I hope that I can convince you to give Decree a try, or at least a look!

First, I want to highlight some of the features I'm most proud of:

Supports JSON, URL Query, Form URL Encoded, Form Data Encoded, and XML input. Supports JSON and XML output. Highly configurable with reasonable defaults. Virtually 100% code coverage. Thorough inline documentation on public interfaces. Thorough online wiki. Option for verbose logging for easy debugging. Thorough error reporting designed to be user friendly with an option to get detailed diagnostic information. Powerful mocking system for easy automated testing.

It also has the beginnings of a separate repository for popular service declarations called DecreeServices. This is kept separate to keep the main repository light-weight.

If that doesn't convince you to check it out on Github right now, let's take a quick look at a few examples.

A request without any input or output looks very simple.

// Asynchronous request mostly used for client apps CheckStatus().makeRequest() { result in switch result { case .success: print("Success :)") case .failure(let error): print("Error :( \(error)") } } // Synchronous requests mostly used on backends try CheckStatus().makeSynchronousRequest()

A login request, accepting a username and password and returning a token, can still look quite simple.

let credentials = Credentials(username: "username", password: "secret") // Asynchronous request mostly used for client apps Login().makeRequest(with: credentials) { result in switch result { case .success(let output): print("Token: \(output.token)") case .failure(let error): print("Error :( \(error)") } } // Synchronous requests mostly used on backends let token = try Login().makeSynchronousRequest().token

These call sites are extremely understandable. You don't have all the garbage around creating URLSessions, data tasks, formatting the input, parsing the output, etc.

To make these two requests possible, you only need to declare the web service itself and the endpoints.

struct ExampleService: WebService { // There is no service wide standard response or error formats typealias BasicResponse = NoBasicResponse typealias ErrorResponse = NoErrorResponse // Requests should use this service instance by default static var shared = ExampleService() // Required for mocking to be possible var sessionOverride: Session? // All requests will be sent to their endpoint at "https://example.com" let baseURL = URL(string: "https://example.com")! }

The endpoints are then declared as:

struct CheckStatus: EmptyEndpoint { typealias Service = ExampleService let path = "status" } struct Credentials: Encodable { let username: String let password: String } struct Login: InOutEndpoint { typealias Service = ExampleService static let method = Method.post let path = "login" typealias Input = Credentials struct Output: Decodable { let token: String } }

Most of the code has been moved to these declarations instead of the call site. Also, this code is quite readable and lays out the possibilities concisely. Finally, much more customization is available if you need it.

For much more information check out the code itself or the wiki.