Skip to content
Framework for making Declarative HTTP Requests
Swift Other
  1. Swift 99.6%
  2. Other 0.4%
Branch: master
Clone or download
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
Assets [CO] Add header asset Jul 23, 2019
Decree.xcodeproj [BF] Readd Project Version to allow including this package in publish… Aug 18, 2019
Sources/Decree [CI] More accurately mock the threading of mocked requests Aug 18, 2019
Tests [CI] Improve test reliability Aug 18, 2019
.gitignore [CI] Rename to Decree Jul 22, 2019
.gitmodules [NF] Amazon AWS S3 support Jul 22, 2019
.swift-version [BF] Fix linux build Jul 22, 2019
Decree.podspec [RE] v4.1.2 Aug 8, 2019
LICENSE Create LICENSE Jul 21, 2019
Package.swift [UP] Update to original XMLCode repository Aug 4, 2019
README.md
azure-pipelines.yml [NF] Make DecreeErrorDescribable public Aug 6, 2019

README.md

Decree - Declarative HTTP Requests

Swift platforms Swift Package Manager compatible CocoaPods Compatible MIT Build Status

Twitter @drewag Blog drewag.me

Make HTTP requests in a clear and type safe way by declaring web services and endpoints on iOS, macOS, and Linux

When it comes to making URL requests with Swift, you largely have two options: use the URLSession APIs in Foundation or use some heavy handed framework.

This framework is designed to be light-weight while remaining customizable and focusing on declaring the interface to an API in a declarative manner. Once declared, making requests to the various endpoints is very straight-forward and type safe. It works on iOS, macOS, and Linux.

Andrew developed this strategy through the implementation of many different apps and backend services written in Swift. He's used this paradigm for communicating between his own front and back-ends (both implemented in Swift) as well as to services such as Spotify, FreshDesk, Stripe, and more.

We offer a separate repository DecreeServices with service declarations for popular services

Table of Contents

Features

Four types of Endpoints

These protocols declare if an endpoint has input and/or output.

  • EmptyEndpoint (no input or output)
  • InEndpoint (only input)
  • OutEndpoint (only output)
  • InOutEndpoint (input and output)

Five Input formats

These formats are used to encode the endpoint's input using the Swift Encodable protocol.

  • JSON
  • URL Query
  • Form URL Encoded
  • Form Data Encoded
  • XML

Two Output formats

These formats are used to initialize the endpoint's output using the Swift Decodable protocol.

  • JSON
  • XML

Three types of Authorization

Allows setting authorization to be used for all endpoints in a web service. Each endpoint can then specify an authorization requirement.

  • Basic
  • Bearer
  • Custom - Custom HTTP header key and value

Configurable

You can optionally perform advanced configuration to the processing of a request and response.

  • Customize URLRequest (e.g. custom headers)
  • Customize JSON encoders and decoders
  • Custom response validation
  • Custom error response format
  • Custom standard response format

Virtually 100% Code Coverage

The vast majority of our code is covered by unit tests to ensure reliability.

Request and Response Logging

Thorough Error Reporting

The errors thrown and returned by Decree are designed to be user friendly while also exposing detailed diagnostic information.

Mocking

Allows mocking endpoint responses for easy automated testing.

Third-Party Services

We created a separate framework that defines services and endpoints for several third-party services. Check it out at DecreeServices.

Examples

Here are a few examples of how this framework is used.

Simple Get

Here we define a CheckStatus endpoint that is a GET (the default) with no input or output that exists at the path “/status”. Scroll down to see the definition of ExampleService.

struct CheckStatus: EmptyEndpoint {
    typealias Service = ExampleService

    let path = "status"
}

We can then use that definition to make asynchronous requests.

CheckStatus().makeRequest() { result in
    switch result {
    case .success:
        print("Success :)")
    case .failure(let error):
        print("Error :( \(error)")
    }
}

We can also make synchronous requests that simply throw an error if an error occurs.

try CheckStatus().makeSynchronousRequest()

Input and Output

We can also define endpoints that have input and/or output. Here, we define a Login endpoint that is a POST to “/login” with username and password parameters encoded as JSON. If successful, the endpoint is expected to return a token.

struct Login: InOutEndpoint {
    typealias Service = ExampleService
    static let method = Method.post

    let path = "login"

    struct Input: Encodable {
        let username: String
        let password: String
    }

    struct Output: Decodable {
        let token: String
    }
}

Then we can make an asynchronous request.

Login().makeRequest(with: .init(username: "username", password: "secret")) { result in
    switch result {
    case .success(let output):
        print("Token: \(output.token)")
    case .failure(let error):
        print("Error :( \(error)")
    }
}

Or we can make a synchronous requests that returns the output if successful and throws otherwise.

let token = try Login().makeSynchronousRequest(with: .init(username: "username", password: "secret")).token

The Service Definition

The only extra code necessary to make the above examples work, is to define the ExampleService:

struct ExampleService: WebService {
    // There is no service wide standard response format
    typealias BasicResponse = NoBasicResponse

    // Errors will be in the format {"message": "<reason>"}
    struct ErrorResponse: AnyErrorResponse {
        let message: String
    }

    // Requests should use this service instance by default
    static var shared = ExampleService()

    // All requests will be sent to their endpoint at "https://example.com"
    let baseURL = URL(string: "https://example.com")!
}

Here we define a WebService called ExampleService with the a few properties.

That's all you need. You can then define as many endpoints as you like and use them in a clear and type safe way.

Real World Examples

To see real world examples, check out how we declared services in DecreeServices.

Hopeful Features

Features we are hoping to implement are added as issues with the enhancement tag. If you have any feature requests, please don't hesitate to create a new issue.

Contributing

It is very much encouraged for you to report any issues and/or make pull requests for new functionality.

You can’t perform that action at this time.