Type-safe networking with Swift Concurrency

Overview

AsyncRequest

AsyncRequest is a type-safe framework for building a suite of requests to communicate with an API, built on top of Swift Concurrency.

Install

Installation is done through Swift Package Manager. Paste the URL of this repo into Xcode or add this line to your Package.swift:

.package(url: "https://github.com/lightyear/AsyncRequest", from: "0.1.0")

Usage

There are two primary types provided by this package: AsyncRequest and APIBase.

AsyncRequest is a protocol that describes the essentials of an API request. It defines the HTTP method, path to the endpoint and the type of data you expect to receive. An example that fetches users from JSONPlaceholder looks like this:

class UsersRequest: APIBase, AsyncRequest {
    override init() {
        super.init()
        path = "https://jsonplaceholder.typicode.com/users"
    }
    
    func start() async throws -> Data {
        try await super.sendRequest().data
    }
}

try await UsersRequest().start()

APIBase is a base class. It contains a URLSession instance, builds the URLRequest and starts the data task. It is intended to be subclassed and contain the logic common to all requests for a given API. Again for JSONPlaceholder, a subclass might look like:

class JSONPlaceholderAPI: APIBase {
    override init() {
        super.init()
        baseURL = URL(string: "https://jsonplaceholder.typicode.com")
    }
    
    override func buildURLRequest() throws -> URLRequest {
        var urlRequest = try super.buildURLRequest()
        urlRequest?.setValue("application/json", forHTTPHeaderField: "Accept")
        return urlRequest
    }
    
    override func startRequest() async throws -> DataResponse {
        try await super.startRequest()
            .validateStatusCode(in: 200..<300)
            .hasContentType("application/json")
    }
}

This subclass ensures that the Accept header is set for every request and validates both the HTTP status code and content type of the response. Take note that only the leaf classes conform to Request. This is important, because Swift does not look further down an inheritence hierarchy to find the proper implementation of a property or function when checking for protocol conformance.

Decoding JSON data

Getting a DataResponse struct back from a request isn't as useful as structured data. The UsersRequest can be modified slightly to do this automatically:

struct User: Codable {
    var id: Int
    var name: String
    var username: String
    var email: String
    // etc...
}

class UsersRequest: JSONPlaceholderAPI, AsyncRequest {
    override init() {
        super.init()
        path = "/users"
    }

    func start() async throws -> [User] {
        try await super.sendRequest()
            .decode([User].self, with: JSONDecoder())
    }
}

The return type of start() changed to reflect the decoded type and the decode helper is used to parse the Data into an an Array<User>.

Helpers

There are several useful helpers available to validate that the response data matches what you expect.

validateStatusCode(in:) throws an error if the response status code isn't the provided sequence. You can pass any Sequence of Int (so, Range<Int>, Set<Int>, Array<Int> all work).

hasContentType(_:) throws an error if the response content type doesn't match the passed type. This helper will match with or without a trailing charset. For example, hasContentType("text/plain") accepts a content type of either "text/plain" (exact match) or "text/plain; charset=utf-8".

Testing

You can test your Request conformances using any library that hooks into Apple's URL loading system, such as OHHTTPStubs.

You might also like...
A Modern Concurrency and Synchronization for Swift.
A Modern Concurrency and Synchronization for Swift.

##Features Simple AtomicT class for numbers and strings. Uncomplicated dispatch keyword for firing off background routines. Awesome ChanT for conc

Functional Concurrency Primitives

Concurrent Concurrent is a collection of functional concurrency primitives inspired by Concurrent ML and Concurrent Haskell. Traditional approaches to

AsyncOperators brings some features of RxSwift/Combine to Structured Concurrency

AsyncOperators brings some features of RxSwift/Combine to Structured Concurrency, such as combineLatest and distinctUntilChanged.

A declarative state management and dependency injection library for SwiftUI x Concurrency
A declarative state management and dependency injection library for SwiftUI x Concurrency

A declarative state management and dependency injection library for SwiftUI x Concurrency

straightforward networking and error handling with async-await and URLSession

AsyncAwaitNetworkingPlayground How To Run Just clone the project, open it and run. Some notes about AsyncAwaitNetworkingPlayground It's a straightforw

Lightweight async/await networking library with interceptor support - usable from iOS 13+.
Lightweight async/await networking library with interceptor support - usable from iOS 13+.

Lightweight async/await networking library with interceptor support. 🚀 Getting started AsyncNetwork's session acts as a wrapper to URLSession by addi

Remote shell using libssh2 with Objective-C, thread safe implementation.

NSRemoteShell Remote shell using libssh2 with Objective-C. Thread safe implementation. Available as Swift Package. git libssh2 prebuilt binaries are r

Type-Erased Existential Generic AsyncSequence Values in Swift

AnyAsyncSequence AnyAsyncSequence allows you to expose AsyncSequence interfaces in your APIs without exposing the underlying sequence type, while cont

A general purpose embedded hierarchical lock manager used to build highly concurrent applications of all types. Same type of locker used in many of the large and small DBMSs in existence today.

StickyLocking StickyLocking is a general purpose embedded lock manager which allows for locking any resource hierarchy. Installable Lock modes allow f

Owner
Light Year Software, LLC
Light Year Software, LLC
Type-safe thread-local storage in Swift

Threadly is a Swift µframework that allows for type-safe thread-local storage. What is Thread-Local Storage? Thread-local storage (TLS) lets you defin

Nikolai Vazquez 71 Aug 6, 2022
A complete set of primitives for concurrency and reactive programming on Swift

A complete set of primitives for concurrency and reactive programming on Swift 1.4.0 is the latest and greatest, but only for Swift 4.2 and 5.0 use 1.

AsyncNinja 156 Aug 31, 2022
Venice - Coroutines, structured concurrency and CSP for Swift on macOS and Linux.

Venice provides structured concurrency and CSP for Swift. Features Coroutines Coroutine cancelation Coroutine groups Channels Receive-only chan

Zewo 1.5k Dec 22, 2022
A Swift DSL that allows concise and effective concurrency manipulation

NOTE Brisk is being mothballed due to general incompatibilities with modern version of Swift. I recommend checking out ReactiveSwift, which solves man

Jason Fieldman 25 May 24, 2019
Swift concurrency collection support

AsyncCollections Functions for running async processes on Swift Collections ForEach Run an async function on every element of a Sequence. await array.

Adam Fowler 11 Jul 11, 2022
An introduction to using Swift's new concurrency features in SwiftUI

SwiftUI Concurrency Essentials An introduction to using Swift's new concurrency features in SwiftUI Discuss with me · Report Bug · Request Feature Art

Peter Friese 80 Dec 14, 2022
The projects and materials that accompany the Modern Concurrency in Swift book

Modern Concurrency in Swift: Materials This repo contains all the downloadable materials and projects associated with the Modern Concurrency in Swift

raywenderlich 137 Dec 16, 2022
Tools for using Swift Concurrency on macOS 10.15 Catalina, iOS 13, tvOS 13, and watchOS 6.

ConcurrencyCompatibility Tools for using Swift Concurrency on macOS 10.15 Catalina, iOS 13, tvOS 13, and watchOS 6. Xcode 13.2 adds backwards deployme

Zachary Waldowski 9 Jan 3, 2023
Slack message generator and API client, written in Swift with Result Builders and Concurrency

Slack Message Client This package provides a Swift object model for a Slack Block Kit message, as well as a Result Builder convenience interface for e

Mike Lewis 2 Jul 30, 2022
Several synchronization primitives and task synchronization mechanisms introduced to aid in modern swift concurrency.

AsyncObjects Several synchronization primitives and task synchronization mechanisms introduced to aid in modern swift concurrency. Overview While Swif

SwiftyLab 20 Jan 3, 2023