Swift/Obj-C HTTP framework with a focus on REST and JSON

Overview

Now Archived and Forked

PMHTTP will not be maintained in this repository going forward. Please use, create issues on, and make PRs to the fork of PHMTTP located here.

PMHTTP

Version Platforms Languages License Carthage compatible CocoaPods

PMHTTP is an HTTP framework built around URLSession and designed for Swift while retaining Obj-C compatibility.

We think URLSession is great. But it was designed for Obj-C and it doesn't handle anything beyond the networking aspect of a request. This means no handling of JSON, and it doesn't even provide multipart/form-data uploads. PMHTTP leaves the networking to URLSession and provides everything else. Features include:

  • Requests can define parse handlers that execute asynchronously separately from the completion block, and requests can be canceled while parsing and the completion block sees the correct result.
  • First-class JSON support using PMJSON.
  • Structured results and high-quality errors; no more treating URLError.cancelled as a network error.
  • Strongly-typed results.
  • Thread safety.
  • Intelligent cache handling.
  • Requests can be defined once (including a parse handler) and executed many times, just like URLRequest.
  • Configurable automatic retrying of failed requests when safe.
  • A configurable base URL, allowing for switching between staging and production with no change to the code constructing the requests.
  • Support for Basic authentication.
  • multipart/form-data, application/x-www-form-urlencoded, and JSON upload support.
  • Built-in request mocking support without using method swizzling.
  • Nothing uses the main thread, not even completion blocks, unless you explicitly ask it to.

PMHTTP was designed specifically for the HTTP functionality that Postmates needs. This means first-class REST support with a focus on JSON. But there's some functionality it doesn't handle which we may get around to doing at some point (see issues). Pull requests are welcome.

Table of Contents

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.

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.
        }
}

A multipart/form-data upload might look like:

// 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
        // ...
}

Setup

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

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)
    }
}

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 HTTPManager. 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 HTTP (for Obj-C this is [HTTPManager defaultManager]). All properties and methods on this class are completely thread-safe.

Configuration of the shared HTTP instance can be done by adopting the HTTPManagerConfigurable protocol on your app delegate. This protocol provides a method that can be used to configure the shared HTTPManager object the first time the HTTP 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 +load method). Do note, however, that this method will be executed on whatever thread is first accessing the HTTP property, and so it should be safe to run from any thread.

Important: The shared HTTP 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 HTTP is appropriate or whether you should be using a separate instance of HTTPManager. The use of HTTP is only appropriate if you want to automatically adopt any configuration the application provides (including environment and default credential).

Environments

HTTPManager has a property environment of type HTTPManager.Environment. An environment is a simple wrapper around a URL 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:

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)
}

The environment is also used to determine whether a given request should adopt the default credential configured on the HTTPManager. 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.

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 URLRequest, PMHTTP requests are inherently mutable (so they're like NSMutableURLRequest). 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).

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 application/x-www-form-urlencoded or multipart/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 HTTPManagerParseRequest<T> but the parse result is always an 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 HTTPManagerUploadFormRequest requests allow for adding multipart bodies.

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

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

HTTPManagerRequest conforms to NSCopying 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 HTTPManagerParseRequest<T>) 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.

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 HTTPManagerTask. This class is the PMHTTP equivalent of URLSessionTask and is completely thread-safe. It provides properties for inspecting the current state of the request, including for accessing the underlying URLSessionTask, and it provides a cancel() method for canceling the request. Unlike URLSessionTask.cancel(), HTTPManagerTask.cancel() 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 HTTPManagerTask.cancel() 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 .canceled even if it had already finished parsing before cancel() 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.

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

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

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 affectsNetworkActivityIndicator (which defaults to true) that controls whether any tasks created from the request affect the callback. The callback itself is configured by assigning a block to HTTPManager.networkActivityHandler. 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:

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

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 HTTPManager.defaultRetryBehavior, which can be overridden on individual requests with HTTPManagerRequest.retryBehavior. A few common retry policies are provided as convenience methods on HTTPManagerRetryBehavior, but any custom policy is supported as well. The convenience policies implement intelligent handling of the various NSURLErrorDomain errors, such as not retrying when encountering a non-transient error (such as NSURLErrorAppTransportSecurityRequiresSecureConnection), or retrying non-idempotent requests if the error indicates the server never received the request (e.g. NSURLErrorCannotConnectToHost). By default, retrying is disabled.

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 URLSession 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 .parseAsJSON(), .parseAsJSON(using:), or .decodeAsJSON(_:), although it can be overridden on a per-request basis (see HTTPManagerRequest.defaultResponseCacheStoragePolicy). Notably, requests created with .parse(using:) do not use this cache strategy as it would interfere with caching image requests.

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 URLSessionTask (so any code that inspects HTTPManagerTask.networkTask will function as expected). Mocks can be registered on the HTTPManager 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).

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 carthage bootstrap. This dependency is not exposed to clients of PMHTTP as it's only used by the test suite.

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 HTTPManagerRequest.HTTPHeaders 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.

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 import PMHTTP to your code.

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 NSData but no explicit contentType (#65).
  • Fix the Obj-C -setValue:forHeaderField: and -setValue:forDefaultHeaderField: methods to take a nullable value (#67).
  • Add HTTPManagerRetryBehavior.init(any:) to combine multiple retry behaviors together (#69).
  • Add HTTPManagerRequest.HTTPHeaders methods init(minimumCapacity:) and reserveCapacity(_:) and property capacity (#66).

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 HTTPManagerObjectParseRequest.credential property where assigning to the property wouldn't work.

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 HTTPManagerError for convenient access to the associated values (e.g. .response, .body, etc).
  • Add computed property HTTPManagerError.statusCode that returns the failing status code for the error, or nil for .unexpectedContentType (#60).
  • Add Obj-C function PMHTTPErrorGetStatusCode() that returns the failing status code for the error, or nil for PMHTTPErrorUnexpectedContentType or for non-PMHTTP errors (#60).
  • Provide PMHTTPStatusCodeErrorKey user info key for more error types (#59).
  • Add computed property URLResponse.isUnmockedInterceptedRequest 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).

v4.3.3 (2019-04-07)

  • Update PMHTTPErrorIsFailedResponse to handle PMHTTPErrorUnexpectedNoContent and PMHTTPErrorUnexpectedRedirect in addition to PMHTTPErrorFailedResponse and PMHTTPErrorUnauthorized.
  • Fix warnings introduced by Xcode 10.2.

v4.3.2 (2018-11-14)

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

v4.3.1 (2018-08-01)

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

v4.3.0 (2018-07-26)

  • Expose HTTPManagerTask.userInitiated as a public property (#42).
  • Add another parameter to the HTTPManager.MetricsCallback 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).

v4.2.0 (2018-07-10)

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

v4.1.1 (2018-06-21)

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

v4.1.0 (2018-06-15)

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

v4.0.1 (2018-05-17)

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

v4.0.0 (2018-02-23)

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

v3.0.5 (2017-09-11)

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

v3.0.4 (2017-09-05)

  • Support Swift 3.2.
  • Handle serverRequiresContentLength correctly in preparedURLRequest.

v3.0.3 (2017-08-18)

  • Add overloads to the request creation methods that take a URL. These overloads return a non-optional request.
  • Add new property HTTPManagerRequest.serverRequiresContentLength. This disables streaming body support (for JSON and multipart/mixed) and instead encodes the body synchronously so it can provide a "Content-Length" header to the server. There is a corresponding HTTPManager.defaultServerRequiresContentLength property as well.
  • Add a method HTTPManagerRequest.setDefaultEnvironmentalProperties() that sets properties to the HTTPManager-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. HTTP.request(GET: "/foo")) that should still use the environment defaults. Right now this method only sets auth and serverRequiresContentLength.

v3.0.2 (2017-05-01)

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

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 PMHTTPErrorIsFailedResponse to test PMHTTP errors easily.
  • Add methods .parseAsImage(scale:) and .parseAsImage(scale:using:) to HTTPManagerDataRequest and HTTPManagerActionRequest.
  • 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 URLSession's delegate queue instead. This only matters if your completion block captures values whose deinit cares about the current thread.
  • Expand dictionaries, arrays, and sets passed as parameters. Dictionaries produce keys of the form "foo[bar]" and arrays and sets just use the key multiple times (e.g. "foo=bar&foo=qux"). The expansion is recursive. The order of values from expanded dictionaries and sets is implementation-defined. If you want "array[]" 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.
  • Also expand nested URLQueryItems in parameters. The resulting parameter uses dictionary syntax ("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 defaultCredential and credential properties have been replaced with defaultAuth and auth, using a brand new protocol HTTPAuth. An implementation of Basic authentication is provided with the HTTPBasicAuth object. This new authentication mechanism has been designed to allow for OAuth2-style refreshes, and a helper class HTTPRefreshableAuth is provided to make it easy to implement refreshable authentication.

v2.0.1 (2017-01-05)

  • Fix PMJSON dependency in CocoaPods podspec.

v2.0.0 (2017-01-03)

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

v1.0.4 (2016-10-20)

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

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 CustomNSError and deprecate the NSError bridging methods.
  • 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.

Comments
  • [PROPOSAL] OAuth2 support to PMHTTP

    [PROPOSAL] OAuth2 support to PMHTTP

    Goals

    • A lightweight integration designed for PMHTTP to interact with the consumer's first party auth server.

    Given that lots of OAuth2 providers deviate slightly from the standard, it should be easy to write extensions to support others based on my work in the future. Getting a token via app switching or presenting a SKViewController will not be initially supported.

    • Automatic token refreshing under the hood.

    If the token is nonexistent or expired, PMHTTP will automatically fetch a new token before continuing with the request. An appropriate error will occur if the underlying network failure is due to an error fetching the new token.

    Proposed Solution

    Add an HTTPAuth object that a HTTPBasicAuth and HTTPOAuth2StandardAuth can inherit from. Using this object and defaultCrediental are mutally exclusive, resulting in an error.

    HTTPAuth functionality and integration

    • When an URLSessionTask is created it will be passed to a method on HTTPAuth to inject the necessary headers. That logic is determined by the subclasses.
    • HTTPManagerErrors 401 error will be extended to also include an optional HTTPAuth object used in the request.
    • HTTPManagerTask will be extended to have a HTTPAuth property.
    • HTTPManager will be extended to have a defaultHTTPAuth property.
    • When a request hits a 401, the auth object will be asked to reconciliate. HTTPOAuth2StandardAuth will refresh the token and then remake the original URLRequest with a new task. Given the original task may no longer be valid, the networkTask property on HTTPManagerTask will have updated documentation stating the underlying task may also be changed due to this reason.
    opened by ekimia 24
  • Extending `RefreshableAuth` to be invoked with other auth responses

    Extending `RefreshableAuth` to be invoked with other auth responses

    Specifically a 403.

    Use case:

    1. A client credentials token is granted.
    2. An endpoint with a higher authorization level is hit.
    3. a 403 is returned.
    4. The request is passed to RefreshableAuth to see if it can be dealt with.

    Ideally, it would be nice to register auth objects for specific response codes to optionally have another Auth object to handle that particular case. Thoughts? I'll be tinkering around with PMHTTP to get a working case done today.

    opened by ekimia 8
  • Add podspec for cocoapods support in PMHTTP

    Add podspec for cocoapods support in PMHTTP

    This addresses issue #9. Tested using pod lib lint, all platforms succeed.

    Once merged the podspec will obviously need to be pushed to the cocoapods trunk and all that.

    Hope this helps! Let me know if there's anything off that needs changing.


    This change is Reviewable

    opened by klundberg 5
  • Unknown Hint Identifier for Image MIME Types

    Unknown Hint Identifier for Image MIME Types

    On iOS 10 (building with Xcode 10.2), calling the parseAsImage(scale:) method to process HTTP requests for remote images causes the following error message to be logged to the console:

    extractOptions:147: *** unknown hint identifier 'kCGImageSourceTypeIdentifierHint:image/jpeg' -- ignoring...
    

    I can confirm that a similar error message occurs when attempting to load PNG images as well.

    Looking at the implementation of this method, I traced the issue to the following line in the private UIImage initializer called from that method:

    options = [kCGImageSourceTypeIdentifierHint: mimeType as CFString]
    

    According to Apple's documentation, CGImageSourceCreateWithData expects kCGImageSourceTypeIdentifierHint to be a UTI, rather than a MIME type. For example, according to Apple's Image I/O Programming Guide, the correct UTI for a JPEG image would be public.jpeg.

    One potential fix would be to use UTTypeCreatePreferredIdentifierForTag to get the UTI corresponding to the MIME type like so:

    import CoreServices
    
    let mimeType = "image/jpeg"
    let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType as! CFString, kUTTypeImage)
    
    uti?.takeRetainedValue() // public.jpeg
    

    My one concern with this approach is that I'm unfamiliar with the performance characteristics of UTTypeCreatePreferredIdentifierForTag, and don't know if calling this method incurs a static initialization cost to build up the list of supported UTIs (and if so, how often). It'd be unfortunate if the computation to generate the type hint took longer than it would otherwise.

    If you also share this concern, an alternative approach would be to hardcode the lookup for only the supported MIME types from a list derived from either this table in the docs or by calling CGImageSourceCopyTypeIdentifiers(_:). The downside to this approach is that it is liable to become stale over time, and at the time of writing, the list is actually quite large (primarily because of disparate RAW camera image types):

    ["public.jpeg", "public.png", "com.compuserve.gif", "com.canon.tif-raw-image", "com.adobe.raw-image", "com.dxo.raw-image", "com.canon.cr2-raw-image", "com.canon.cr3-raw-image", "com.leafamerica.raw-image", "com.hasselblad.fff-raw-image", "com.hasselblad.3fr-raw-image", "com.nikon.raw-image", "com.nikon.nrw-raw-image", "com.pentax.raw-image", "com.samsung.raw-image", "com.sony.raw-image", "com.sony.sr2-raw-image", "com.sony.arw-raw-image", "com.epson.raw-image", "com.kodak.raw-image", "public.tiff", "public.jpeg-2000", "com.apple.atx", "org.khronos.astc", "org.khronos.ktx", "public.avci", "public.heic", "public.heif", "com.canon.crw-raw-image", "com.fuji.raw-image", "com.panasonic.raw-image", "com.panasonic.rw2-raw-image", "com.leica.raw-image", "com.leica.rwl-raw-image", "com.konicaminolta.raw-image", "com.olympus.sr-raw-image", "com.olympus.or-raw-image", "com.olympus.raw-image", "com.phaseone.raw-image", "com.microsoft.ico", "com.microsoft.bmp", "com.apple.icns", "com.adobe.photoshop-image", "com.microsoft.cur", "com.truevision.tga-image", "com.ilm.openexr-image", "com.sgi.sgi-image", "public.radiance", "public.pbm", "public.mpo-image", "public.pvr", "com.microsoft.dds"]
    

    Finally, the null solution would be to simply remove the type hint altogether.

    opened by mattt 4
  • Fix HTTP Body Generation for JSON Requests

    Fix HTTP Body Generation for JSON Requests

    I ran into a tricky issue which I believe is a bug in PMHTTP, though I'm not entirely I'm using the API correctly.

    When generating and performing a network request as following, the provided JSON is not transmitted as part of the HTTP body, instead the HTTP body is empty:

    let request = HTTP.request(
        POST: "test",
        json: [
            "email" : user.email
        ]
    )
    
    request?.performRequest { task, result in
    ...
    }
    

    This can be fixed by extending the code that generates the upload body in HTTPManager to also check for a .json body type.

    This code also seems to exist in the preparedURLRequest property (https://github.com/postmates/PMHTTP/blob/19e9c57c5618e9f7f5076f545418b6f5eebb7a1b/Sources/HTTPManagerRequest.swift#L483-L503), but that code isn't called form anywhere except from the test suite.

    I would assume a long term fix would be to incorporate this code into the actual HTTP body generation.

    The test suite itself did not expose this bug, since it was calling preparedURLRequest, which had the side effect of generating the HTTP body correctly. As soon as that line was removed from the tests, they failed expectedly.


    This change is Reviewable

    opened by Ben-G 4
  • Make a sample of how to use with ObjectMapper

    Make a sample of how to use with ObjectMapper

    I want to use this library but I'm struggling with using this lib with ObjectMapper

    https://github.com/tristanhimmelman/ObjectMapper

    So can you write an example of how to use it?

    opened by kiroskirin 3
  • Does not compile with Xcode 8.3

    Does not compile with Xcode 8.3

    Xcode 8.3 appears to have completely broken my custom modulemap file, preventing the project from compiling. I haven't found any workaround yet. Filed as rdar://problem/31310946.

    bug 
    opened by lilyball 3
  • Reconfigure Header Fields

    Reconfigure Header Fields

    I setup httpManager like this

    extension AppDelegate: HTTPManagerConfigurable {
        func configure(httpManager: HTTPManager) {
            httpManager.environment = APIConfig.staging
                ? HTTPManager.Environment.Staging
                : HTTPManager.Environment.Production
    
            let config = URLSessionConfiguration.default
            config.timeoutIntervalForRequest = 10
            httpManager.sessionConfiguration = config
    
            if let token = AuthManager.JWTToken {
                httpManager.defaultHeaderFields.addValue("Bearer \(token)", forHeaderField: "Authorization")
            }
    
            httpManager.defaultRetryBehavior = HTTPManagerRetryBehavior
                .retryNetworkFailureOrServiceUnavailable(withStrategy: .retryTwiceWithDefaultDelay)
        }
    }
    

    Then I have a case when user log out and log in again. The header fields token is not refreshed. I have to close the app then open it again to make it work.

    How do I handle this case and Is there a way to reset header fields?

    opened by kiroskirin 2
  • Security review notes for followup

    Security review notes for followup

    I did a quick security-focused review and these are my notes. PTAL and assess if there's anything that makes sense to follow up on. (This is similar to my review of PMJSON).


    • I’m not Swift savvy so I may have missed something, but the following documents what I looked into / pondered and some outstanding questions.
    • Does it follow server-side redirects? Is this configurable?
      • Looks like it, here:
    • CR/LF injection in headers? (Or in URL paths too for that matter)
      • Could this be an issue in HTTPAuth.swift? I see data here that seems to land in headers.
      • Seems to be handled appropriately by the quotedString function in HTTPBodyStream.swift at least.
    • What happens if cert validation fails? (Does it fail secure by default?)
    • Most web security considerations seem not relevant in this context. (E.g.: CORS) Though I wonder if it makes sense to explicitly support HSTS or anything else, in any way?
    • Some cookie-related code in HTTPManagerRequest.swift
    • Re caching -- it is conceivable to mess this up, you can imagine if a cached HTTP resource could somehow be retrieved on a subsequent HTTPS request to the same origin / path.
    • Search for “unsafe”
      • 17 results
      • Some of these are in test code, don’t really care about those.
      • Didn’t pin down specific memory corruption issues, but that’s the sort of thing I’d be worried about for any of these.
    • SipHash implementation reports itself as cryptographically insecure. Should confirm that it’s not being used in a way that this would be required.
      • Perhaps interesting in the context of a hash DoS attack, though this is a client lib so really no big deal even if this actually would be possible.
      • Seems to be used by implementation of CaseInsensitiveASCIIString
        • Does appear to be used on untrusted data (URLs, maybe header data?) but not obvious to me if the hashing comes into play
    • What’s used for the boundary value in HTTPBodyStream.swift? If content in a multipart POST comes from different sources and some piece of content can spoof a valid boundary then that could be problematic.
      • Ahh, looks like it’s a UUID:
      • Not sure how random UUIDs generated in this way really are.
        • They seem to come from RFC 4122 version 4 which seems to suggest they aren’t necessarily based on a secure source of randomness
      • You could consider using a better source of randomness, assuming a UUID isn’t sufficiently random.

    opened by randomdross 2
  • % JSON and URL Encoded forms now have the body set

    % JSON and URL Encoded forms now have the body set

    Some servers have problems if the Content-Length isn’t set. I don’t see a reason for the body to using streaming when it is a simple encoded form or JSON blob. If the Content-Length isn't set, the Transfer-Encoding is set as Chunked which causes some problems.


    This change is Reviewable

    opened by sgruby 2
  • httpBody is nil when it shouldn't be when configuring a mocked response

    httpBody is nil when it shouldn't be when configuring a mocked response

    Repro

    • Add a mock, similar to
    HTTP.mockManager.addMock(for: "mockedPath") { request, headers, completion in
    ...
    }
    
    • Place a breakpoint in the block.
    • Create a request with parameters.
    • When the breakpoint hits, enter po request.httpBody
    • You'll see a nil response when it should contain data.
    opened by ekimia 2
  • HTTPManagerRetryBehavior needs better Obj-C support

    HTTPManagerRetryBehavior needs better Obj-C support

    We should expose the equivalent of .retryNetworkFailure(withCustomStrategy:) and retryNetworkFailureOrServiceUnavailable(withCustomStrategy:) to Obj-C.

    enhancement 
    opened by lilyball 0
  • Mock manager should have an .interceptAllUnhandledURLs() method

    Mock manager should have an .interceptAllUnhandledURLs() method

    Right now the mock manager has 2 properties to control whether it intercepts unmocked requests, one for requests within the environment and one for requests outside (the idea being that we can intercept all requests to the API server but allow requests to external services, or vice versa).

    A very common setup is to intercept all requests, so we should make it easier by having a method .interceptAllUnhandledURLs() that sets both properties.

    Alternatively we could refactor this into something like

    var interceptedUnhandledURLs: UnhandledURLIntercept = []
    struct UnhandledURLIntercept: OptionSet {
        var rawValue: Int
        static let environmental = UnhandledURLIntercept(rawValue: 1 << 0)
        static let external = UnhandledURLIntercept(rawValue: 1 << 1)
        static let all: UnhandledURLIntercept = [.environmental, .external]
    }
    var interceptUnhandledEnvironmentURLs: Bool {
        get { return interceptedUnhandledURLs.contains(.environmental) }
        set { newValue ? interceptedUnhandledURLs.insert(.environmental) : interceptedUnhandledURLs.remove(.environmental) }
    }
    var interceptUnhandledExternalURLs: Bool {
        …
    }
    

    With redesigned environments (see #48) it's also not clear if we should support intercepting unhandled URLs per-environment, or just keep a global "all environments" toggle.

    enhancement 
    opened by lilyball 0
  • Parse block should run on .default QoS, be configurable

    Parse block should run on .default QoS, be configurable

    Right now request parse blocks run on .utility (unless request.userInitiated == true, then it runs on the .userInitiated queue). We should probably change that to .default instead, and have some way to configure it. Configuration should be limited to just QoS rather than specifying explicit queues (notably, we take the position that parse blocks should never run on the main queue, and if you really need that you should parse in the completion block; this decision was made to encourage users to not require parsing on the main thread, as we try and do as little work as possible on the main thread).

    I'm not sure offhand how to bump the QoS automatically to .userInitiated with a configurable queue. We could overload all the parse methods to have separate versions that take a QoS or not, and do the automatic bumping only if the QoS isn't specified, but this would be annoying duplication and also be a bit of an ergonomic issue for anyone writing their own custom convenience parse methods. We could also just make the QoS optional, and do the automatic prioritizing if it's nil. Or we could just default it to .default and them bump it to .userInitiated if request.userInitiated == true and the QoS is specified as anything other than .userInteractive. I'm tempted to go this route, because if request.userInitiated == true then it seems odd to parse on anything lower, and this way a convenience parse wrapper can just specify whatever QoS they want without worrying if their caller will set request.userInitiated = true.

    enhancement 
    opened by lilyball 0
  • Redesign environments

    Redesign environments

    Right now we set a single global environment on the HTTPManager, and then tie a number of defaults to it. This is convenient for simple setups, but doesn't work well if e.g. the client has a number of subdomains for different API sets that all want to share the same auth.

    There are a number of ways we could go with this, but here's a brief summary of my current thoughts:

    • Multiple environments on an HTTPManager. For the base URL to use for requests constructed with relative URLs, we could either go with the first environment, or have an explicit defaultEnvironment. The first one is simpler, but means I can't configure a single environment without making it the base. The latter is more flexible, but would allow me to e.g. set a default environment that's not in the environments list. We could also just divorce the base URL handling from environments entirely and have it be a separate property, though I'm not particularly happy about this approach either.
    • The defaults that apply to the environment should be moved onto the environment object itself. This way I can configure the defaults separately per-environment. This means a separate per-environment lock, but that's probably fine.
    • Instead of HTTPManagerRequest.setDefaultEnvironmentalProperties() we'd have something like HTTPManagerRequest.applyDefaults(from:) (or maybe HTTPManagerEnvironment.applyDefaults(to:)).

    Something I'm not sure about is how to handle multiple domains that should have the same properties, because keeping the properties in sync between environments when mutating them is annoying. We could give HTTPManagerEnvironment a whole array of domains instead of a single one. Another option is to have an option to "apply to subdomains" so I can just set up an environment for the root, though this approach is problematic if I want the default base URL to be a subdomain.

    enhancement 
    opened by lilyball 0
  • Add a mechanism to allow direct manipulation of the final URLRequest

    Add a mechanism to allow direct manipulation of the final URLRequest

    We should probably have a way to attach a block to a request that can be used to customize the final URLRequest immediately prior to handing it to URLSession. This would primarily be used for allowing clients to use new URLSession features before PMHTTP has implemented them.

    This should be configurable per-request, but also have a global default. A few alternatives for the global default, not sure yet which way is best:

    1. A block that's used as the default for all requests, but is overridden per-request.
    2. A block that is used as the default for all requests, in addition to the per-request block. The global block would run first, then the per-request block.
    3. A block that is used as the default for environmental requests, but is overridden per-request.

    I'm tempted to go with option 2, so that way the global block can also be used to implement other features, such as tracking every single time we send a network request. If we pass the HTTPManager and HTTPManagerRequest objects to the block as well then the block can manually implement the other behaviors (e.g. by bailing if there's a per-request block, or bailing if the URL doesn't belong to the environment).

    We could also go ahead and implement all 3 behaviors and have it be configurable, though I'd rather not do this.

    If the request is retried, this block would also run on each retry.

    enhancement 
    opened by lilyball 0
Releases(v4.5.0)
  • v4.5.0(Nov 28, 2019)

    • Add Obj-C convenience functions for creating upload requests with an NSData but no explicit contentType (#65).
    • Fix the Obj-C -setValue:forHeaderField: and -setValue:forDefaultHeaderField: methods to take a nullable value (#67).
    • Add HTTPManagerRetryBehavior.init(any:) to combine multiple retry behaviors together (#69).
    • Add HTTPManagerRequest.HTTPHeaders methods init(minimumCapacity:) and reserveCapacity(_:) and property capacity (#66).
    Source code(tar.gz)
    Source code(zip)
  • v4.4.3(Nov 15, 2019)

    • 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.
    Source code(tar.gz)
    Source code(zip)
  • v4.4.2(Nov 15, 2019)

  • v4.4.1(Apr 24, 2019)

  • v4.4.0(Apr 24, 2019)

    • 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 HTTPManagerError for convenient access to the associated values (e.g. .response, .body, etc).
    • Add computed property HTTPManagerError.statusCode that returns the failing status code for the error, or nil for .unexpectedContentType (#60).
    • Add Obj-C function PMHTTPErrorGetStatusCode() that returns the failing status code for the error, or nil for PMHTTPErrorUnexpectedContentType or for non-PMHTTP errors (#60).
    • Provide PMHTTPStatusCodeErrorKey user info key for more error types (#59).
    • Add computed property URLResponse.isUnmockedInterceptedRequest 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).
    Source code(tar.gz)
    Source code(zip)
  • v4.3.3(Apr 7, 2019)

    • Updated PMHTTPErrorIsFailedResponse to handle PMHTTPErrorUnexpectedNoContent and PMHTTPErrorUnexpectedRedirect in addition to PMHTTPErrorFailedResponse and PMHTTPErrorUnauthorized.
    • Fix warnings introduced by Xcode 10.2.
    Source code(tar.gz)
    Source code(zip)
  • v4.3.2(Nov 15, 2018)

  • v4.3.1(Aug 2, 2018)

  • v4.3.0(Jul 27, 2018)

    • Expose HTTPManagerTask.userInitiated as a public property (#42).
    • Add another parameter to the HTTPManager.MetricsCallback 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).
    Source code(tar.gz)
    Source code(zip)
  • v4.2.0(Jul 11, 2018)

    • Percent-encode more characters for application/x-www-form-urlencoded bodies and query strings. Notably, semicolon (;) is now percent-encoded, as some servers treat it as a separator.
    • Optimize task metrics collection such that metrics are not collected if metricsCallback is nil (#37).
    • Extend built-in retry behaviors to support custom strategies (#35).
    • Add HTTPManagerRequest properties that correspond to the URLRequest properties mainDocumentURL and httpShouldHandleCookies (#40).
    Source code(tar.gz)
    Source code(zip)
  • v4.1.1(Jun 22, 2018)

    • Add HTTPHeaders.merge(_:uniquingKeysWith:) and HTTPHeaders.merging(_:uniquingKeysWith:).
    • Deprecate HTTPHeaders.append(contentsOf:).
    • Merge header fields when calling HTTPManagerRequest.setDefaultEnvironmentalProperties(), giving priority to existing request header fields in the case of a conflict.
    Source code(tar.gz)
    Source code(zip)
  • v4.1.0(Jun 16, 2018)

    • Support mocking relative URLs when no environment has been set.
    • Shrink the default mock delay to 10ms.
    • Make HTTPManagerRequest.headerFields mutable in Obj-C.
    • Add HTTPManager.defaultHeaderFields which defines the default header fields to attach to requests and, like defaultAuth, only applies to requests within the current environment.
    • Declare conformance to Equatable for HTTPManagerRequest.HTTPHeaders.
    • Fix fatal error when using deferred multipart bodies along with serverRequiresContentLength.
    • Add HTTPManager.metricsCallback to collect task metrics (URLSessionTaskMetrics) from all tasks associated with the HTTPManager.
    Source code(tar.gz)
    Source code(zip)
  • v4.0.1(May 18, 2018)

  • v4.0.0(Feb 24, 2018)

    • Convert to Swift 4.
    • Add a method HTTPManagerParseRequest.map(_:).
    • Add methods HTTPManagerDataRequest.decodeAsJSON(_:with:options:) and HTTPManagerActionRequest.decodeAsJSON(_:with:options:).
    Source code(tar.gz)
    Source code(zip)
  • v3.0.5(Sep 11, 2017)

  • v3.0.4(Sep 5, 2017)

  • v3.0.3(Aug 18, 2017)

    • Add overloads to the request creation methods that take a URL. These overloads return a non-optional request.
    • Add new property HTTPManagerRequest.serverRequiresContentLength. This disables streaming body support (for JSON and multipart/mixed) and instead encodes the body synchronously so it can provide a "Content-Length" header to the server. There is a corresponding HTTPManager.defaultServerRequiresContentLength property as well.
    • Add a method HTTPManagerRequest.setDefaultEnvironmentalProperties() that sets properties to the HTTPManager-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. HTTP.request(GET: "/foo")) that should still use the environment defaults. Right now this method only sets auth and serverRequiresContentLength.
    Source code(tar.gz)
    Source code(zip)
  • v3.0.2(May 1, 2017)

  • v3.0.1(Mar 29, 2017)

  • v3.0.0(Feb 27, 2017)

    • Preserve network task priority when retrying tasks.
    • Add convenience Obj-C function PMHTTPErrorIsFailedResponse to test PMHTTP errors easily.
    • Add methods .parseAsImage(scale:) and .parseAsImage(scale:using:) to HTTPManagerDataRequest and HTTPManagerActionRequest.
    • 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 URLSession's delegate queue instead. This only matters if your completion block captures values whose deinit cares about the current thread.
    • Expand dictionaries, arrays, and sets passed as parameters. Dictionaries produce keys of the form "foo[bar]" and arrays and sets just use the key multiple times (e.g. "foo=bar&foo=qux"). The expansion is recursive. The order of values from expanded dictionaries and sets is implementation-defined. If you want "array[]" 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.
    • Also expand nested URLQueryItems in parameters. The resulting parameter uses dictionary syntax ("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 defaultCredential and credential properties have been replaced with defaultAuth and auth, using a brand new protocol HTTPAuth. An implementation of Basic authentication is provided with the HTTPBasicAuth object. This new authentication mechanism has been designed to allow for OAuth2-style refreshes, and a helper class HTTPRefreshableAuth is provided to make it easy to implement refreshable authentication.
    Source code(tar.gz)
    Source code(zip)
  • v2.0.1(Jan 5, 2017)

  • v2.0.0(Jan 3, 2017)

    • Support text/json in addition to application/json.
    • Add 2 convenience methods for uploading UIImages as PNG or JPEG data.
    • Add objcError property to PMHTTPResult.
    • Change objcError on HTTPManagerTaskResult to Error? instead of NSError?.
    • Fix Xcode 8.1 compatibility of unit tests.
    • Add optional options parameter to parseAsJSON() and parseAsJSON(with:).
    • Add withMultipartBody(using:) to HTTPManagerUploadFormRequest.
    • Rename parse(with:), parseAsJSON(options:with:), and addMultipartBody(with:) to use the parameter name using: instead, which is more in line with Swift 3 Foundation naming conventions.
    Source code(tar.gz)
    Source code(zip)
  • v1.0.4(Oct 20, 2016)

  • v1.0.3(Sep 24, 2016)

  • v1.0.2(Sep 22, 2016)

  • v1.0.1(Sep 16, 2016)

    • Adopt CustomNSError and deprecate the NSError bridging methods.
    • Add autoreleasepools to dispatch queues where appropriate.
    • Speed up compile times a bit.
    • Fix CocoaPods support.
    Source code(tar.gz)
    Source code(zip)
  • v1.0.0(Sep 9, 2016)

  • v0.9.3(Sep 9, 2016)

  • v0.9.2(Sep 9, 2016)

  • v0.9.1(Aug 17, 2016)

Owner
Postmates Inc.
Postmates Inc.
JSEN (JSON Swift Enum Notation) is a lightweight enum representation of a JSON, written in Swift.

JSEN /ˈdʒeɪsən/ JAY-sən JSEN (JSON Swift Enum Notation) is a lightweight enum representation of a JSON, written in Swift. A JSON, as defined in the EC

Roger Oba 8 Nov 22, 2022
Swift-json - High-performance json parsing in swift

json 0.1.4 swift-json is a pure-Swift JSON parsing library designed for high-per

kelvin 43 Dec 15, 2022
JSON-Practice - JSON Practice With Swift

JSON Practice Vista creada con: Programmatic + AutoLayout Breve explicación de l

Vanesa Giselle Korbenfeld 0 Oct 29, 2021
Ss-json - High-performance json parsing in swift

json 0.1.1 swift-json is a pure-Swift JSON parsing library designed for high-per

kelvin 43 Dec 15, 2022
JSONNeverDie - Auto reflection tool from JSON to Model, user friendly JSON encoder / decoder, aims to never die

JSONNeverDie is an auto reflection tool from JSON to Model, a user friendly JSON encoder / decoder, aims to never die. Also JSONNeverDie is a very important part of Pitaya.

John Lui 454 Oct 30, 2022
A fast, convenient and nonintrusive conversion framework between JSON and model. Your model class doesn't need to extend any base class. You don't need to modify any model file.

MJExtension A fast, convenient and nonintrusive conversion framework between JSON and model. 转换速度快、使用简单方便的字典转模型框架 ?? ✍??Release Notes: more details Co

M了个J 8.5k Jan 3, 2023
Elevate is a JSON parsing framework that leverages Swift to make parsing simple, reliable and composable

Elevate is a JSON parsing framework that leverages Swift to make parsing simple, reliable and composable. Elevate should no longer be used for

Nike Inc. 611 Oct 23, 2022
HandyJSON is a framework written in Swift which to make converting model objects to and from JSON easy on iOS.

HandyJSON To deal with crash on iOS 14 beta4 please try version 5.0.3-beta HandyJSON is a framework written in Swift which to make converting model ob

Alibaba 4.1k Dec 29, 2022
ObjectMapper is a framework written in Swift that makes it easy for you to convert your model objects to and from JSON.

ObjectMapper is a framework written in Swift that makes it easy for you to convert your model objects (classes and structs) to and from J

Tristan Himmelman 9k Jan 2, 2023
This framework implements a strict JSON parser and generator in Objective-C.

SBJson 5 Chunk-based JSON parsing and generation in Objective-C. Overview SBJson's number one feature is stream/chunk-based operation. Feed the parser

null 3.8k Jan 5, 2023
Magical Data Modeling Framework for JSON - allows rapid creation of smart data models. You can use it in your iOS, macOS, watchOS and tvOS apps.

JSONModel - Magical Data Modeling Framework for JSON JSONModel allows rapid creation of smart data models. You can use it in your iOS, macOS, watchOS

JSONModel 6.9k Dec 8, 2022
Magical Data Modeling Framework for JSON - allows rapid creation of smart data models. You can use it in your iOS, macOS, watchOS and tvOS apps.

JSONModel - Magical Data Modeling Framework for JSON JSONModel allows rapid creation of smart data models. You can use it in your iOS, macOS, watchOS

JSONModel 6.8k Nov 19, 2021
Freddy - A reusable framework for parsing JSON in Swift.

Why Freddy? Parsing JSON elegantly and safely can be hard, but Freddy is here to help. Freddy is a reusable framework for parsing JSON in Swift. It ha

Big Nerd Ranch 1.1k Jan 1, 2023
An iOS framework for creating JSON-based models. Written in Swift.

An iOS framework for creating JSON-based models. Written in Swift (because it totally rules!) Requirements iOS 8.0+ Xcode 7.3 Swift 2.2 Installation E

Oven Bits 448 Nov 8, 2022
Reflection based (Dictionary, CKRecord, NSManagedObject, Realm, JSON and XML) object mapping with extensions for Alamofire and Moya with RxSwift or ReactiveSwift

EVReflection General information At this moment the master branch is tested with Swift 4.2 and 5.0 beta If you want to continue using EVReflection in

Edwin Vermeer 964 Dec 14, 2022
YamlSwift - Load YAML and JSON documents using Swift

YamlSwift parses a string of YAML document(s) (or a JSON document) and returns a Yaml enum value representing that string.

Behrang Norouzinia 384 Nov 11, 2022
Jay - Pure-Swift JSON parser & formatter. Fully streamable input and output. Linux & OS X ready.

Pure-Swift JSON parser & formatter. Fully streamable input and output. Linux & OS X ready. Replacement for NSJSONSerialization.

Danielle 132 Dec 5, 2021
Hassle-free JSON encoding and decoding in Swift

#JSONCodable Hassle-free JSON encoding and decoding in Swift Installation Simply add the following to your Cartfile and run carthage update: github "m

Matthew Cheok 605 Jun 29, 2022
Codable code is a Swift Package that allows you to convert JSON Strings into Swift structs

Codable code is a Swift Package that allows you to convert JSON Strings into Swift structs.

Julio Cesar Guzman Villanueva 2 Oct 6, 2022