Swift/Obj-C HTTP 框架,专注于 REST 和 JSON

现已存档和分叉

PMHTTP 以后将不会在此存储库中维护。请使用、创建问题,并在位于此处的 PHMTTP 分支上制作 PR。

啪��

版本
平台
语言
许可证
迦太基兼容
可可豆荚

PMHTTP 是一个围绕 Swift 构建和设计的 HTTP 框架,同时保留了 Obj-C 兼容性。URLSession

我们认为很棒。但它是为 Obj-C 设计的,它不会处理请求的网络
方面以外的任何内容。这意味着不处理 JSON,甚至不提供上传。PMHTTP
离开网络并提供其他一切。功能包括:
URLSessionmultipart/form-dataURLSession

  • 请求可以定义独立于完成块异步执行的分析处理程序,并且可以
    在分析时取消请求,并且完成块会看到正确的结果。
  • 使用 PMJSON 的一流 JSON 支持。
  • 结构化结果和高质量错误;不再被视为网络错误。URLError.cancelled
  • 强类型结果。
  • 线程安全。
  • 智能缓存处理
  • 请求可以定义一次(包括解析处理程序)并执行多次,就像 .URLRequest
  • 可配置的在安全时自动重试失败的请求。
  • 可配置的基本 URL,允许在暂存和生产之间切换,而无需更改构造请求的代码
  • 支持基本身份验证。
  • multipart/form-data、 和 JSON 上传支持。application/x-www-form-urlencoded
  • 内置请求模拟支持,无需使用方法重排。
  • 没有任何东西使用主线程,甚至没有完成块,除非你明确要求它这样做。

PMHTTP是专门为Postmates需要的HTTP功能而设计的。这意味着
一流的 REST 支持,专注于 JSON。但是有些功能它无法处理
,我们可能会在某个时候开始执行(请参阅问题)。欢迎拉取请求。

目录

Usage

A typical GET request looks like:

// https://api.example.com/v1/search?query=%s
let task = HTTP.request(GET: "search", parameters: ["query": "cute cats"])
    .parseAsJSON(using: { (response, json) in
        return try JSON.map(json.getArray(), Cat.init(json:))
    })
    .performRequest(withCompletionQueue: .main) { task, result in
        switch result {
        case let .success(response, cats):
            // Do something with the Cats.
        case let .error(response, error):
            // Handle the error. This includes network errors, JSON parse errors,
            // and any error thrown by Cat.init(json:).
        case .canceled:
            // The task was canceled. Ignore or handle as appropriate.
        }
}
// task can be canceled and can be queried for its state
// and this can be done from any thread.
Swift

A POST request might look like:

// https://api.example.com/v1/submit_cat
let task = HTTP.request(POST: "submit_cat", parameters: ["name": "Fluffles", "color": "tabby"])
    .parseAsJSON(using: { result in
        // POST parse blocks take a single `result` argument because 204 No Content is a valid
        // response. The `result` enum vends an optional `value` property, and has a
        // `getValue()` method that throws an error if the response was 204 No Content.
        return try SubmitCatResponse(json: result.getValue())
    })
    .performRequest(withCompletionQueue: .main) { task, result in
        switch result {
        case let .success(response, value):
            // value is a SubmitCatResponse.
        case let .error(response, error):
            // Handle the error. This could be a network error, a JSON parse error, or
            // any error thrown by SubmitCatResponse.init(json:).
        case .canceled:
            // The task was canceled.
        }
}
Swift

A upload might look like:multipart/form-data

// https://api.example.com/v1/submit_cat with photo
let req = HTTP.request(POST: "submit_cat", parameters: ["name": "Fluffles", "color": "tabby"])!
// We could add the image synchronously, but it's better to be asynchronous.
// Note: There is a convenience function to do this already, this is just an example.
req.addMultipartBody { upload in
    // This block executes on a background queue.
    if let data = UIImageJPEGRepresentation(catPhoto, 0.9) {
        upload.addMultipart(data: data, withName: "photo", mimeType: "image/jpeg")
    }
}
let task = req.parseAsJSON(using: { try SubmitCatResponse(json: $0.getValue()) })
    .performRequest(withCompletionQueue: .main) { task, result in
        // ...
}
Swift

Setup

You can modify the properties of the global object at any time, but to make setup
easier, if your or object conforms to the
protocol it will be asked to configure the the first time
the global variable is accessed. This might look like:
HTTPManagerUIApplicationDelegateNSApplicationDelegateHTTPManagerConfigurableHTTPManagerHTTP

extension AppDelegate: HTTPManagerConfigurable {
    public func configure(httpManager: HTTPManager) {
        httpManager.environment = HTTPManager.Environment(string: /* ... */)
        let config = URLSessionConfiguration.default
        config.timeoutIntervalForRequest = 10
        // PMHTTP defines a default User-Agent but we can supply our own
        config.HTTPAdditionalHeaders = ["User-Agent": myUserAgent]
        httpManager.sessionConfiguration = config
        if let (username, apiKey) = getAPICredentials() {
            httpManager.defaultCredential = URLCredential(user: username, password: apiKey, persistence: .forSession)
        }
        httpManager.defaultRetryBehavior = HTTPManagerRetryBehavior.retryNetworkFailureOrServiceUnavailable(withStrategy: .retryTwiceWithDefaultDelay)
    }
}
Swift

Detailed Design

PMHTTP was designed with 6 goals in mind:

  • Be as Swift-like as possible while retaining Obj-C compatibility.
  • Speed, with an emphasis on being concurrent by default.
  • Thread safety wherever it makes sense.
  • Explicitness and type safety. For example, PMHTTP doesn't auto-detect the return type but requires
    you to declare what response format you're expecting.
  • Correctness, which includes avoiding surprising behavior.
  • Make it easy to add new functionality, such as auto-retrying and network mocking.

HTTPManager

The overall manager class for PMHTTP is . This is the class that allows you to
configure various global properties and to create new requests. Multiple managers can be created if
desired, but a single global instance is provided under the global property (for Obj-C this
is ). All properties and methods on this class are completely
thread-safe.
HTTPManagerHTTP[HTTPManager defaultManager]

Configuration of the shared instance can be done by adopting the
protocol on your app delegate. This protocol provides a method that can be used to configure the
shared object the first time the property is accessed. This design allows you
to ensure the shared instance is properly configured prior to first use even if it's used prior to
the normal entry point for your application (e.g. inside some class's method). Do note,
however, that this method will be executed on whatever thread is first accessing the
property, and so it should be safe to run from any thread.
HTTPHTTPManagerConfigurableHTTPManagerHTTP+loadHTTP

Important: The shared instance is a convenience intended for use by the application. If
you're writing a shared component (e.g. a framework) that uses PMHTTP, you need to carefully
consider whether using is appropriate or whether you should be using a separate instance of
. The use of is only appropriate if you want to automatically adopt any
configuration the application provides (including environment and default credential).
HTTPHTTPHTTPManagerHTTP

Environments

HTTPManager has a property of type . An environment is a
simple wrapper around a and represents the base URL that requests should use if the request
is not made with an absolute URL. You may wish to create your own extension that looks something
like:
environmentHTTPManager.EnvironmentURL

extension HTTPManager.Environment {
    // @nonobjc works around "a declaration cannot be both 'final' and 'dynamic'" error.
    @nonobjc static let Production = HTTPManager.Environment(baseURL: productionURL)
    @nonobjc static let Staging = HTTPManager.Environment(baseURL: stagingURL)
}
Swift

The environment is also used to determine whether a given request should adopt the default
credential configured on the . Only requests for URLs that are prefixed by the
environment will use the default credential. Requests for any other URL will have no credential by
default, though a credential can always be added to any request.
HTTPManager

Requests

Requests in PMHTTP are objects. In a pure-Swift world they'd be structs/protocols, but they're
objects in order to be compatible with Obj-C. Unlike , PMHTTP requests are inherently
mutable (so they're like ). They're also the only public component of PMHTTP
that is not thread-safe, though it is safe to access a request concurrently as long as no thread is
mutating the request (which is to say, reading values from the request does not perform any internal
mutation).
URLRequestNSMutableURLRequest

Requests are split into a hierarchy of classes:

  • HTTPManagerRequest - The root request type, which contains parameters and methods that are
    applicable to all requests.
    • HTTPManagerNetworkRequest - The parent class for all requests that do not have a parse
      handler.
      • HTTPManagerDataRequest - The class for GET requests that do not have a parse handler.
      • HTTPManagerActionRequest - The class or parent class for POST/PUT/PATCH/DELETE requests that
        do not have a parse handler.
        • HTTPManagerUploadFormRequest - The class for POST/PUT/PATCH requests without a parse
          handler that have a body of either or
          .
          application/x-www-form-urlencodedmultipart/form-data
        • HTTPManagerUploadDataRequest - The class for POST/PUT/PATCH requests without a parse
          handler that have a body consisting of an arbitrary .
          NSData
        • HTTPManagerUploadJSONRequest - The class for POST/PUT/PATCH requests without a parse
          handler that have a body consisting of a JSON value.
    • HTTPManagerParseRequest<T> - The class for any request that has a parse handler.
    • HTTPManagerObjectParseRequest - The class for requests made from Obj-C that have a parse
      handler. Similar to but the parse result is always an .
      HTTPManagerParseRequest<T>AnyObject?

This hierarchy means that every class can provide only the methods/properties that make sense for
all requests of that class type. For example, only requests allow for
adding multipart bodies.
HTTPManagerUploadFormRequest

Requests include properties for configuring virtually every aspect of the network request. A few
properties inherit default values from the object, though these default values can
always be overridden. One property of note is , which is a boolean property that
should be set if the request represents some action the user is waiting on. Setting this property to
causes the underlying network task to be executed at a high priority and causes all
background queue processing to occur using .
HTTPManageruserInitiatedtrueQOS_CLASS_USER_INITIATED

HTTPManagerUploadFormRequest provides support for creating requests, which
can be used for uploading files/images. These requests are implemented in a streaming fashion, so
e.g. memory-mapped objects won't be copied into a contiguous buffer, thus allowing you to
upload files without concerns about memory use.
multipart/form-dataNSData

HTTPManagerRequest conforms to so copies can be made of any request if necessary.
Furthermore, when attaching a parse handler to a request (and therefore converting it into an
) the original request data is copied so subsequent mutations to the
original request do not affect the parse request, and when a request is executed the request data is
copied so the request can be immediately mutated without affecting the executing network task.
NSCopyingHTTPManagerParseRequest<T>

Requests are also designed such that they can be easily created and executed using a
functional-style chain, as demonstrated by the Usage section above.

Parse requests always execute their parse handler on a background queue, with no option to run on a
given queue (or the main queue). This constraint exists both to encourage parsing in the background,
and for simplicity, as parsing on the main queue can always be accomplished by skipping the parse
handler and parsing in the completion block instead.

Request completion blocks are similarly executed on a background queue by default (for requests with
a parse handler, this will be the same queue that the parse handler executed on), although here a
specific queue can be provided where the completion block should run, such as the main queue.

Network Tasks

Executing a request returns a value of type . This class is the PMHTTP equivalent
of and is completely thread-safe. It provides properties for inspecting the
current state of the request, including for accessing the underlying , and it
provides a method for canceling the request. Unlike ,
can be used to cancel a request while the parse handler is executing, not
just canceling the networking portion. PMHTTP guarantees that if you execute
from the same queue that the completion block is targeting, prior to the
completion block itself executing, the completion block will always be given a result of
even if it had already finished parsing before was invoked. This means that if you target
the main queue for your completion block, you can be confident that a canceled task will never
behave as though it succeeded or failed.
HTTPManagerTaskURLSessionTaskURLSessionTaskcancel()URLSessionTask.cancel()HTTPManagerTask.cancel()HTTPManagerTask.cancel().canceledcancel()

Like , supports key-value observing (although, like
, the KVO messages will occur on some background queue).
URLSessionTaskHTTPManagerTaskURLSessionTask

In the absence of automatic retrying, the property value will never change during the
lifetime of the task. If automatic retrying has been configured, will change if the
request is retried, and will broadcast any relevant key-value observing messages.
networkTasknetworkTask

Network Activity Indicator

PMHTTP provides a callback you can use to implement support for the global network activity
indicator. Each request object has a property (which defaults to
) that controls whether any tasks created from the request affect the callback. The callback
itself is configured by assigning a block to . This block is run
on the main thread whenever the number of active tasks has changed. In order to display the global
network activity indicator you can configure this like so:
affectsNetworkActivityIndicatortrueHTTPManager.networkActivityHandler

HTTPManager.networkActivityHandler = { active in
    UIApplication.sharedApplication().networkActivityIndicatorVisible = active > 0
}
Swift

Automatic Retrying of Failed Requests

PMHTTP includes support for automatically retrying failed requests according to a configurable
policy. The default retry policy can be configured with , which
can be overridden on individual requests with . A few common retry
policies are provided as convenience methods on , but any custom policy is
supported as well. The convenience policies implement intelligent handling of the various
errors, such as not retrying when encountering a non-transient error (such as
), or retrying non-idempotent requests if
the error indicates the server never received the request (e.g. ). By
default, retrying is disabled.
HTTPManager.defaultRetryBehaviorHTTPManagerRequest.retryBehaviorHTTPManagerRetryBehaviorNSURLErrorDomainNSURLErrorAppTransportSecurityRequiresSecureConnectionNSURLErrorCannotConnectToHost

Cache Handling

PMHTTP implements intelligent cache handling for JSON responses. The HTTP standard allows user
agents to cache responses at their discretion when the response does not include caching headers.
However, this behavior is inappropriate for most REST API requests, and does not
document its caching strategy for such responses. To handle this case, PMHTTP inspects JSON
responses for appropriate caching headers and explicitly prevents responses from being cached
if they do not include the appropriate cache directives. By default this behavior is only applied
to requests created with , , or , although
it can be overridden on a per-request basis (see
). Notably, requests created with
do not use this cache strategy as it would interfere with caching image requests.
URLSession.parseAsJSON().parseAsJSON(using:).decodeAsJSON(_:)HTTPManagerRequest.defaultResponseCacheStoragePolicy.parse(using:)

Mocking

PMHTTP has built-in support for mocking network requests. This is done without swizzling (so it's
safe to mock requests even in App Store builds), and it's done in a fashion that still creates a
valid (so any code that inspects will function as
expected). Mocks can be registered on the as a whole, and individual requests can be
independently mocked (so you can control whether a request is mocked based on more than just the URL
in question).
URLSessionTaskHTTPManagerTask.networkTaskHTTPManager

Testing

PMHTTP itself has a comprehensive test suite, covering just about everything in the Swift API (the
Obj-C–specific API is not currently tested, see
issue #7). The tests are run against a custom
HTTP/1.1 server implemented in the test bundle that listens on the loopback interface. This allows
for testing all the functionality without any dependencies on external services and ensures the
tests are very fast. The HTTP/1.1 server currently relies on CocoaAsyncSocket, which can be
installed with . This dependency is not exposed to clients of PMHTTP as it's
only used by the test suite.
carthage bootstrap

The HTTP/1.1 server implements just about everything that I thought was useful. It has a few minor
dependencies on PMHTTP itself (most notably, it uses instead of
reimplementing the functionality), but beyond that, it could actually be pulled out and used
anywhere else that an HTTP/1.1 server is required. However, as this server was written for the
purposes of testing and not production use, it does not have any built-in mitigation of DOS attacks
beyond rejecting uploads greater than 5MiB (for example, it does not impose any limit on headers,
which are kept in memory, and it does not have any sort of timeout on connection duration). It also
does not have any tests itself, beyond the fact that it behaves as expected when used in the PMHTTP
test suite.
HTTPManagerRequest.HTTPHeaders

Requirements

Requires a minimum of iOS 8, macOS 10.10, watchOS 2.0, or tvOS 9.0.

Installation

After installation with any mechanism, you can use this by adding to your code.import PMHTTP

Carthage

To install using Carthage, add the following to your Cartfile:

github "postmates/PMHTTP" ~> 4.0

This release supports Swift 4.0. For Swift 3.x you can use

github "postmates/PMHTTP" ~> 3.0

CocoaPods

To install using CocoaPods, add the following to your Podfile:

pod "PMHTTP", "~> 4.0"

This release supports Swift 4.0. For Swift 3.x you can use:

pod "PMHTTP", "~> 3.0"

License

Licensed under either of

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the
work by you shall be dual licensed as above, without any additional terms or conditions.

Version History

v4.5.0 (2019-11-27)

  • Add Obj-C convenience functions for creating upload requests with an but no explicit (#65).NSDatacontentType
  • Fix the Obj-C and methods to take a nullable value (#67).-setValue:forHeaderField:-setValue:forDefaultHeaderField:
  • Add to combine multiple retry behaviors together (#69).HTTPManagerRetryBehavior.init(any:)
  • Add methods ) and and property (#66).HTTPManagerRequest.HTTPHeadersinit(minimumCapacity:reserveCapacity(_:)capacity

v4.4.3 (2019-11-14)

  • Support PMJSON 4.x in addition to PMJSON 3.x with CocoaPods. Carthage doesn't support that kind of version range so it's now just set to PMJSON 4.x only.

v4.4.2 (2019-08-13)

  • Fix a bug with the deprecated property where assigning to the property wouldn't work.HTTPManagerObjectParseRequest.credential

v4.4.1 (2019-04-24)

v4.4.0 (2019-04-23)

  • Fix a bug when parsing images where we passed the wrong value for the type identifier hint, resulting in a warning being logged to the console (#62).
  • Add computed properties on for convenient access to the associated values (e.g. , , etc).HTTPManagerError.response.body
  • Add computed property that returns the failing status code for the error, or for (#60).HTTPManagerError.statusCodenil.unexpectedContentType
  • Add Obj-C function that returns the failing status code for the error, or for or for non-PMHTTP errors (#60).PMHTTPErrorGetStatusCode()nilPMHTTPErrorUnexpectedContentType
  • Provide user info key for more error types (#59).PMHTTPStatusCodeErrorKey
  • Add computed property that can be used to test if a response comes from a request that was intercepted by the mock manager without a mock installed (#46).URLResponse.isUnmockedInterceptedRequest

v4.3.3 (2019-04-07)

  • Update to handle and in addition to and .PMHTTPErrorIsFailedResponsePMHTTPErrorUnexpectedNoContentPMHTTPErrorUnexpectedRedirectPMHTTPErrorFailedResponsePMHTTPErrorUnauthorized
  • Fix warnings introduced by Xcode 10.2.

v4.3.2 (2018-11-14)

  • Fix bug where requests constructed from a would not inherit environmental defaults (e.g. auth, headers, etc) (#52).URL

v4.3.1 (2018-08-01)

  • Add method overloads to query and set protocol properties on s (#43).URLProtocolHTTPManagerRequest

v4.3.0 (2018-07-26)

  • Expose as a public property (#42).HTTPManagerTask.userInitiated
  • Add another parameter to the callback. In order to retain backwards compatibility, the old initializer and property were deprecated and a new initializer and property were added with different names (#41).HTTPManager.MetricsCallback

v4.2.0 (2018-07-10)

  • Percent-encode more characters for bodies and query strings. Notably, semicolon (;) is now percent-encoded, as some servers treat it as a separator.application/x-www-form-urlencoded
  • Optimize task metrics collection such that metrics are not collected if is (#37).metricsCallbacknil
  • Extend built-in retry behaviors to support custom strategies (#35).
  • Add properties that correspond to the properties and (#40).HTTPManagerRequestURLRequestmainDocumentURLhttpShouldHandleCookies

v4.1.1 (2018-06-21)

  • Add and .HTTPHeaders.merge(_:uniquingKeysWith:)HTTPHeaders.merging(_:uniquingKeysWith:)
  • Deprecate .HTTPHeaders.append(contentsOf:)
  • Merge header fields when calling , giving priority to existing request header fields in the case
    of a conflict.
    HTTPManagerRequest.setDefaultEnvironmentalProperties()

v4.1.0 (2018-06-15)

  • Support mocking relative URLs when no environment has been set.
  • Shrink the default mock delay to 10ms.
  • Make mutable in Obj-C.HTTPManagerRequest.headerFields
  • Add which defines the default header fields to attach to requests and, like , only applies to requests within the current environment.HTTPManager.defaultHeaderFieldsdefaultAuth
  • Declare conformance to for .EquatableHTTPManagerRequest.HTTPHeaders
  • Fix fatal error when using deferred multipart bodies along with .serverRequiresContentLength
  • Add to collect task metrics () from all tasks associated with the .HTTPManager.metricsCallbackURLSessionTaskMetricsHTTPManager

v4.0.1 (2018-05-17)

  • Fix so it can be imported from Obj-C++ code.<PMHTTP/PMHTTP.h>

v4.0.0 (2018-02-23)

  • Convert to Swift 4.
  • Add a method .HTTPManagerParseRequest.map(_:)
  • Add methods and .HTTPManagerDataRequest.decodeAsJSON(_:with:options:)HTTPManagerActionRequest.decodeAsJSON(_:with:options:)

v3.0.5 (2017-09-11)

  • Extend to support handling 403 Forbidden errors as well.HTTPAuth

v3.0.4 (2017-09-05)

  • Support Swift 3.2.
  • Handle correctly in .serverRequiresContentLengthpreparedURLRequest

v3.0.3 (2017-08-18)

  • Add overloads to the request creation methods that take a . These overloads return a non-optional request.URL
  • Add new property . This disables streaming body support (for JSON and multipart/mixed) and instead encodes the body synchronously so it can provide a header to the server. There is a corresponding property as well.HTTPManagerRequest.serverRequiresContentLength"Content-Length"HTTPManager.defaultServerRequiresContentLength
  • Add a method that sets properties to the -defined defaults that otherwise are only set if the request's path matches the environment. This is primarily intended for requests constructed using absolute paths (e.g. ) that should still use the environment defaults. Right now this method only sets and .HTTPManagerRequest.setDefaultEnvironmentalProperties()HTTPManagerHTTP.request(GET: "/foo")authserverRequiresContentLength

v3.0.2 (2017-05-01)

  • Add to Obj-C methods.@discardableResult-performRequest…

v3.0.1 (2017-03-28)

  • Fix Xcode 8.3 compatibility.

v3.0.0 (2017-02-27)

  • Preserve network task priority when retrying tasks.
  • Add convenience Obj-C function to test PMHTTP errors easily.PMHTTPErrorIsFailedResponse
  • Add methods and to and ..parseAsImage(scale:).parseAsImage(scale:using:)HTTPManagerDataRequestHTTPManagerActionRequest
  • When a session is reset, cancel any tasks that were created but never resumed.
  • Ensure that the completion block is always deallocated on either the completion queue or on the thread that created the task. Previously there was a very subtle race that meant the completion block could deallocate on the 's delegate queue instead. This only matters if your completion block captures values whose cares about the current thread.URLSessiondeinit
  • Expand dictionaries, arrays, and sets passed as parameters. Dictionaries produce keys of the form and arrays and sets just use the key multiple times (e.g. ). The expansion is recursive. The order of values from expanded dictionaries and sets is implementation-defined. If you want syntax, then put the in the key itself. See the documentation comments for more details. Do note that this behavior is slightly different from what AFNetworking does."foo[bar]""foo=bar&foo=qux""array[]""[]"
  • Also expand nested s in parameters. The resulting parameter uses dictionary syntax ().URLQueryItem"foo[bar]"
  • Change the type signature of the Obj-C parse methods that take handlers to make the error parameter non-optional.
  • Provide a callback that can be used for session-level authentication challenges. This can be used to implement SSL pinning using something like TrustKit.
  • Fix a small memory leak when retrying tasks.
  • Rework how authorization works. The and properties have been replaced with and , using a brand new protocol . An implementation of Basic authentication is provided with the object. This new authentication mechanism has been designed to allow for OAuth2-style refreshes, and a helper class is provided to make it easy to implement refreshable authentication.defaultCredentialcredentialdefaultAuthauthHTTPAuthHTTPBasicAuthHTTPRefreshableAuth

v2.0.1 (2017-01-05)

  • Fix PMJSON dependency in CocoaPods podspec.

v2.0.0 (2017-01-03)

  • Support in addition to .text/jsonapplication/json
  • Add 2 convenience methods for uploading s as PNG or JPEG data.UIImage
  • Add property to .objcErrorPMHTTPResult
  • Change on to instead of .objcErrorHTTPManagerTaskResultError?NSError?
  • Fix Xcode 8.1 compatibility of unit tests.
  • Add optional parameter to and .optionsparseAsJSON()parseAsJSON(with:)
  • Add to .withMultipartBody(using:)HTTPManagerUploadFormRequest
  • Rename , , and to use the parameter name instead, which is more in line with Swift 3 Foundation naming conventions.parse(with:)parseAsJSON(options:with:)addMultipartBody(with:)using:

v1.0.4 (2016-10-20)

  • Add more Obj-C request constructors.
  • Fix encoding of characters in query strings and bodies.+application/x-www-form-urlencoded

v1.0.3 (2016-09-23)

  • Fix obj-c name of .HTTPManager.parsedDateHeader(from:)

v1.0.2 (2016-09-22)

  • Add fix-its for the Swift 3 API changes.

v1.0.1 (2016-09-12)

  • Adopt and deprecate the bridging methods.CustomNSErrorNSError
  • Add autoreleasepools to dispatch queues where appropriate.
  • Fix CocoaPods support.

v1.0.0 (2016-09-09)

  • Support Swift 3.0.

v0.9.3 (2016-09-09)

  • Fix building for tvOS.

v0.9.2 (2016-09-09)

  • Support Swift 2.3.

v0.9.1 (2016-08-17)

  • Rename Source folder to Sources.
  • CocoaPods support.

v0.9 (2016-08-05)

Initial release.

GitHub

https://github.com/postmates/PMHTTP