{"id":26770701,"url":"https://github.com/alexjohnj/requests","last_synced_at":"2025-03-28T23:16:20.678Z","repository":{"id":62452805,"uuid":"159221484","full_name":"alexjohnj/Requests","owner":"alexjohnj","description":"Sugar for your HTTP requests","archived":false,"fork":false,"pushed_at":"2020-02-29T11:16:39.000Z","size":314,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-02T13:17:04.946Z","etag":null,"topics":["carthage","cocoapods","http","ios","networking","request","swift","swift-package-manager","urlrequest","urlsession"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alexjohnj.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-11-26T19:27:07.000Z","updated_at":"2022-09-14T17:22:47.000Z","dependencies_parsed_at":"2022-11-01T23:45:51.191Z","dependency_job_id":null,"html_url":"https://github.com/alexjohnj/Requests","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexjohnj%2FRequests","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexjohnj%2FRequests/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexjohnj%2FRequests/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexjohnj%2FRequests/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alexjohnj","download_url":"https://codeload.github.com/alexjohnj/Requests/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246112665,"owners_count":20725301,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["carthage","cocoapods","http","ios","networking","request","swift","swift-package-manager","urlrequest","urlsession"],"created_at":"2025-03-28T23:16:20.012Z","updated_at":"2025-03-28T23:16:20.648Z","avatar_url":"https://github.com/alexjohnj.png","language":"Swift","readme":"# Requests [![Build Status](https://app.bitrise.io/app/5a6709f831604876/status.svg?token=IhVpUeUB9JduoFGCyhR6Uw\u0026branch=master)](https://app.bitrise.io/app/5a6709f831604876)\n\n_Requests_ is a Swift library focused on providing sugar for building and\norganising your application's HTTP requests.\n\n_Requests_ **is not** concerned with performing network requests. You can use\n[whatever][urlsession-docs] [you][alamofire-docs] [want][afnetworking-docs] to\nperform the requests. _Requests_ simply provides some types that make building\nrequests and keeping them organised more enjoyable.\n\n[urlsession-docs]: https://developer.apple.com/documentation/foundation/urlsession\n[alamofire-docs]: https://github.com/Alamofire/Alamofire\n[afnetworking-docs]: https://github.com/AFNetworking/AFNetworking\n\n\u003e ⚠️ _Requests_ is under active development and there are some areas of the API\n\u003e that will change. Until _Requests_ reaches version 1.0, any non patch 0.x\n\u003e release can be API breaking.\n\n---\n\n# Usage Guide\n\n## Core Types\n\n_Requests_ contains a few types that form the core of the library as well as\nmany helper types. Complete reference documentation for all types can be found\n[here](https://alexjohnj.github.io/Requests).\n\nThe core types are:\n\n- The `RequestConvertible` protocol --- Conforming types declare the properties\n  of a HTTP request and can be converted into Foundation `URLRequest` instances.\n- The `Request` structure --- A concrete implementation of the\n  `RequestConvertible` protocol that provides a fluent interface for declaring\n  API requests.\n- The `RequestProviding` protocol --- Conforming types declare the base URL of\n  an API and can initialise base `Request` instances for a specific API.\n- The `ResponseDecoder` structure --- A type wrapping a function that decodes a\n  type from a HTTP response.\n- The `BodyProvider` structure --- A type wrapping a function that encodes the\n  body of a `RequestConvertible` type.\n\nIf you need to get started with _Requests_ quickly, you should investigate the\n`Request` and `RequestProviding` types.\n\n## Installation\n\n_Requests_ supports installation using [CocoaPods][cocoapods-gh],\n[Carthage][carthage-gh] or the [Swift Package Manager][swiftpm-gh]. _Requests_\nsupports macOS, iOS, tvOS and watchOS. Linux is not supported but may work.\n\n\u003e ⚠️ While _Requests_ is in the `0.x` release phase, use your package manager's\n\u003e [pessimistic operator][for-a-pessimist-im-pretty-optimistic] to pin the\n\u003e version number to a minor release.\n\n[for-a-pessimist-im-pretty-optimistic]: https://robots.thoughtbot.com/rubys-pessimistic-operator\n[cocoapods-gh]: http://cocoapods.org/\n[carthage-gh]: https://github.com/carthage/carthage/\n[swiftpm-gh]: https://github.com/apple/swift-package-manager\n\n### CocoaPods\n\nAdd the following to your `Podfile`:\n\n``` ruby\npod \"Requests\", \"~\u003e 0.3.0\"\n```\n\n### Carthage\n\nAdd the following to your `Cartfile`:\n\n``` ruby\ngithub \"alexjohnj/Requests\" ~\u003e 0.3.0\n```\n\n### Swift Package Manager\n\nAdd the following to your `Package.swift` file's dependencies:\n\n``` swift\ndependencies: [\n    .package(url: \"https://github.com/alexjohnj/Requests.git\", .upToNextMinor(from: \"0.3.0\"))\n]\n```\n\n\n\n## Create a Request Provider for an API\n\nFor each API in your application, create a type that conforms to the\n`RequestProviding` protocol. These types provide the base URL for an API:\n\n``` swift\nenum ExampleAPI: RequestProviding {\n    case development\n    case production\n\n    var baseURL: URL {\n        switch self {\n        case .development:\n            return URL(\"https://dev.example.com/api\")\n        case .production:\n            return URL(\"https://live.example.com/api\")\n        }\n    }\n}\n\nlet api = ExampleAPI.development\n```\n\n`RequestProviding` types form the entry point to building a `Request` for an\nAPI. A `RequestProviding` type has several methods that construct a base\n`Request` to an API.\n\n## GET a Resource\n\nTo build a request to retrieve a JSON encoded resource modelled as a `Decodable`\nstructure, use the `get(_:from:)` method on a request provider:\n\n``` swift\nstruct User: Codable { }\n\nlet getUserRequest = api.get(.json(encoded: User.self), from: \"/user/1/\")\n\nURLSession.shared.perform(getUserRequest) { result in\n    switch result {\n    case .success(let urlResponse, let user):\n        // Do something with the user\n        break\n\n    case .failed(let response?, let error):\n        // We got a HTTP response but also an error. Something probably went wrong decoding the JSON.\n        break\n\n    case .failed(nil, let error):\n        // We didn't get a response. There was probably a network error.\n        break\n    }\n}\n```\n\nThis method constructs a `GET` request to `https://dev.example.com/api/user/1`\nand configures it with a `ResponseDecoder` that tries to decode a `User` struct\nfrom the response's body. The returned request is generic over its response's\nbody's type (called the `Resource` to distinguish it from a `HTTPURLResponse`).\n\nThe `perform(_:)` method on `URLSession` performs a request and evaluates the\n`ResponseDecoder` with the response's body. It then passes the decoded\n`Resource` to the completion block alongside a `HTTPURLResponse` if everything\nsucceeds. Otherwise the block receives an `Error` and possibly a\n`HTTPURLResponse`.\n\n## POST Some Data\n\nSending data looks similar to retrieving a resource. To build a request that\nposts a JSON encoded `User` struct, use the `post(_:to:)` method on an API's\nrequest provider:\n\n``` swift\nlet user = User()\nlet createUserRequest = api.post(.json(encoded: user), to: \"/user/\")\nURLSession.shared.perform(createUserRequest) { result in\n    // Handle the result\n}\n```\n\nThis method creates a `POST` request configured with a `BodyProvider` that\nencodes the user struct as JSON. The `Resource` type of the request is `Void`\nmeaning the request's response doesn't have a body or the request doesn't care\nabout the body. Note that the `BodyProvider` will take care of updating the\nrequest's headers to indicate the type of content it contains.\n\n## Authenticating a Request\n\n_Requests_ has basic support for authenticating requests. If a request can be\nauthenticated using its header, use an `AuthenticationProvider` to update the\nheader with the required credentials:\n\n``` swift\nlet authToken = \"DEADBEEF-DEADBEEF-DEADBEEF\"\nlet updateUserRequest = api.patch(\"/user/1\", with: .json(encoded: user))\n    .authenticated(with: .bearerToken(authToken))\n\nURLSession.shared.perform(updateUserRequest) { _ in }\n```\n\nThis builds a `PATCH` request that will include a bearer token in the\nheader. _Requests_ includes built in support for attaching:\n\n- Bearer token headers\n- HTTP Basic Auth headers\n\nYou can add additional header based authentication schemes by writing a new\n`AuthenticationProvider`.\n\n## Customising Headers\n\nThe `Request` type has several functions for setting the headers of a\nrequest. The `Header` type models a request's header, consisting of multiple\n`Field`s. `Field`s consist of a name and a value.\n\nTo set the header of a request, use the `with(header:)` method:\n\n``` swift\nlet getBioRequest = api.get(.text, from: \"/user/1/bio\")\n    .with(header: [\n        .acceptLanguage(\"en-scouse\"),\n        .accept(.plainText)\n        ])\n```\n\nThis constructs a new `Header` from an array of `Field`s and replaces the\nrequest's header with it.\n\nTo add a header to a request or replace a single field in a request's header,\nuse one of `adding(headerField:)`, `adding(headerFields:)` or\n`setting(headerField:)`.\n\n\u003e ⚠️ A request's `BodyProvider` and `AuthenticationProvider` can both modify the\n\u003e fields of a request's header. Any changes made by them will override the\n\u003e fields you specify when building the request.\n\n## Customising Query Parameters\n\nSimilar to headers, the `Request` type provides several functions that set the\nquery parameters of a request:\n\n``` swift\nlet searchRequest = api.get(.text, from: \"/users/search\")\n    .with(query: [\n        \"query\": \"alex\",\n        \"limit\": \"30\",\n        ])\n```\n\nThis produces a request to the URL\n`https://dev.example.com/api/users/search?query=alex\u0026limit=30`. Note that\n_Requests_ uses the Foundation `URLQueryItem` to represent query items but\nprovides several extensions that makes building them neater.\n\n## Defining Custom Header Fields\n\n_Requests_ includes several predefined fields for common HTTP headers. You can\neasily add new ones by adding a `static` property on the `Field` and\n`Field.Name` types:\n\n``` swift\nextension Field.Name {\n    static let applicationKey = Field.Name(\"X-APPLICATION-KEY\")\n}\n\nextension Field {\n    static let applicationKey: (String) -\u003e Field = { Field(name: .applicationKey, value: $0) }\n}\n```\n\n## Defining a Base Request for an API\n\nSome APIs require common properties set on all API requests. For example, an API\nmight require an application key in the header of each request. You can achieve\nthis by implementing an optional method in a `RequestProviding` conforming type.\n\nThe `request(to:using:)` method is the core method of the `RequestProviding`\nprotocol. It returns a new `Request` for an API and is the starting point for\nall other request building methods on `RequestProviding`.\n\nA custom implementation of `request(to:using:)` can return a `Request` with a\ndefault set of values applied:\n\n``` swift\nstruct ExternalAPI: RequestProviding {\n    let baseURL: URL = URL(\"https://api.external.org\")\n\n    func request(to endpoint: String, using method: HTTPMethod) -\u003e Request\u003cExternalAPI, Void\u003e {\n        return Request(api: self, endpoint: endpoint, responseDecoder: .none, method: method)\n            .adding(headerField: .applicationKey(\"DEAD-BEEF\"))\n    }\n}\n```\n\nNow, any `Request` built from `ExternalAPI` will include the application key header field.\n\n## Writing a New Response Decoder\n\n_Requests_ ships with a couple of built in `ResponseDecoder`s for JSON and text\ndata. It's possible to define a new `ResponseDecoder` if needed.\n\nA `ResponseDecoder` is a structure generic over its `Response` that wraps a\nthrowing function taking a `HTTPURLResponse` and some `Data` and producing a\n`Response`:\n\n``` swift\npublic struct ResponseDecoder\u003cResponse\u003e {\n\n    public init(_ decode: @escaping (HTTPURLResponse, Data) throws -\u003e Response)\n\n    ...\n}\n```\n\nWhen adding a new response decoder, declare a static property or function in an\nextension of the `ResponseDecoder` type that returns a new\n`ResponseDecoder`. This provides unqualified access to a decoder when used with\nthe `Request` building methods and goes a long way towards making request\ndefinitions readable.\n\nAs an example, the definition of `.text(encoding:)` response decoder is a static\nfunction on the `ResponseDecoder\u003cString\u003e` type:\n\n``` swift\nextension ResponseDecoder where Response == String {\n\n    public static let text = ResponseDecoder\u003cString\u003e.text(encoding: .utf8)\n\n    public static func text(encoding: String.Encoding) -\u003e ResponseDecoder\u003cString\u003e {\n        return ResponseDecoder { _, data in\n            guard let string = String(data: data, encoding: encoding) else {\n                throw CocoaError(.fileReadInapplicableStringEncoding,\n                                 userInfo: [NSStringEncodingErrorKey: encoding.rawValue])\n            }\n\n            return string\n        }\n    }\n}\n```\n\nUsing this, the call site for the response decoder looks incredibly neat:\n\n``` swift\nlet getBookRequest = api.get(.text(encoding: .ascii), from: \"/book/1/contents\")\n\n// Or for UTF-8\nlet getOtherBookRequest = api.get(.text, from: \"/book/2/contents\")\n\n```\n\nThis approach is a bit unconventional for Swift---a protocol would generally be\nthe more Swifty solution. However, the goal here was to optimise for readability\nat the call site rather than in the implementation of the protocol. As you'll be\nconsuming request providers more often than you'll be writing them (especially\nas _Requests_ adds more built-ins), I believe this is a worthwhile trade-off.\n\n## Writing a New Authentication Provider\n\nLike the `ResponseDecoder` type, an `AuthenticationProvider` is a struct\nwrapping a function. An authentication provider wraps a function that mutates an\n`inout Header`:\n\n``` swift\npublic struct AuthenticationProvider {\n\n    public init(authenticate: @escaping (inout Header) -\u003e Void)\n\n    ...\n}\n\n```\n\nAgain, declare `AuthenticationProvider`s as static properties or functions on\nthe `AuthenticationProvider` type so that they read nicely with the `Request`\ntype's methods:\n\n``` swift\nextension AuthenticationProvider {\n\n    static let custom: (String) -\u003e AuthenticationProvider = { customToken in\n        AuthenticationProvider { header in\n            header[.authorization] = \"Custom \\(customToken)\"\n        }\n    }\n\n}\n```\n\n## Writing a New Body Provider\n\nNo surprises with this one. `BodyProvider`s work the same way as\n`AuthenticationProvider`s and `ResponseDecoder`s. A body provider is a struct\nthat wraps a throwing function that takes an `inout Header` and returns a\n`RequestBody`:\n\n``` swift\npublic struct BodyProvider {\n\n    public init(encode: @escaping (inout Header) throws -\u003e RequestBody)\n\n    ...\n}\n```\n\nIn the body of the `BodyProvider` you should encode some data, update the\n`ContentType` of the `Header` and then return the body. Note that the returned\n`RequestBody` can wrap either raw `Data` or an `InputStream`.\n\nDeclare new body providers in static functions in an extension of `BodyProvider`:\n\n``` swift\nextension BodyProvider {\n    static func text(_ text: String) -\u003e BodyProvider {\n        return BodyProvider { header in\n            guard let data = text.data(using: .utf8) else {\n                throw TextBodyEncodingError.utf8EncodingFailed\n            }\n\n            header.set(.contentType(.plainText))\n            return .data(data)\n        }\n    }\n}\n```\n\n\u003e ⚠️ Only update the header of a request after calling any throwing functions.\n\n## Advanced Usage\n\n### The `RequestConvertible` Protocol\n\nThe `RequestConvertible` protocol is really the core of _Requests_. Indeed, for\na long time it was all there was to _Requests_. Everything else was built around\nthe type to simplify its usage.\n\n`RequestConvertible` types declare all the information needed to convert a\nrequest to a Foundation `URLRequest`. An extension method on the protocol\n(`toURLRequest()`) handles the actual conversion of conforming types. If you're\nbuilding any functions that operate on requests, you should consider\nconstraining them to `RequestConvertible` conforming types instead of the\n`Request` type itself for maximum flexibility.\n\nMost of the properties of the `Request` type map directly to a requirement in\nthe `RequestConvertible` protocol. The only difference between `Request` and\n`RequestConvertible` is the absence of an associated `API` type in the\nprotocol. `RequestConvertible` lacks this type because of the different usage\nmodel organising requests with it opens up.\n\nWith the `RequestConvertible` protocol, you can organise you application's HTTP\nrequests using protocol inheritance and composition. Each request in your\napplication is a `RequestConvertible` type. Common properties for an API can be\ndeclared in a protocol that inherits from the base `RequestConvertible`\nprotocol. This eliminates the need for an associated `API` type.\n\nThis organisation system has pros and cons. Some of the pros are:\n\n- Ease of discoverability --- Each API request is its own type (generally in its\n  own file) and so can easily be searched for in a project.\n- Easy definition of ad-hoc `Resource` types --- You can satisfy the protocol's\n  `Resource` associated type requirement using a type nested inside the request\n  definition. This is handy for one-off responses and keeps the model of the\n  request and its associated resource in close proximity.\n\nSome of its cons:\n\n- Boilerplate --- This approach leads to lots of boilerplate. Each API request\n  needs a new type, a new file (normally) and then a protocol hiding the actual\n  construction and execution of the network request.\n- Protocol composition is not as composable as function composition --- If you\n  try and compose two `RequestConvertible` child protocols that both have\n  default implementations of the same property, you will lose the default\n  implementation. You will need knowledge of the default implementations of both\n  the protocols you're composing to implement the conforming type's properties\n  correctly.\n\n### Performing a Request\n\nAs has already been mentioned, _Requests_ is not concerned with performing\nnetwork requests, only constructing them. Saying that, _Requests_ does come with\na supported extension on `URLSession` to perform requests. This is there to help\npeople get up and running with _Requests_ but it is by no means meant to define\nhow _Requests_ should be used.\n\nIf you're integrating _Requests_ with another networking system, keep the\nfollowing in mind:\n\n- Constrain your functions to operate on `RequestConvertible` types, not\n  `Request`.\n- A `Void` resource type indicates the request either doesn't expect or doesn't\n  care about the response's body. Your functions should respect this and not\n  treat a `nil` response body as an error for `Void` requests.\n- `ResponseDecoders` only operate on HTTP responses. Your functions should treat\n  non `HTTPURLResponse` instances as an error.\n- Converting a `RequestConvertible` type to a `URLRequest` can fail.\n\n## License\n\n_Requests_ is released under the MIT license.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexjohnj%2Frequests","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falexjohnj%2Frequests","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexjohnj%2Frequests/lists"}