diff --git a/Cartfile b/Cartfile index 62674f6..c05eee3 100644 --- a/Cartfile +++ b/Cartfile @@ -1,3 +1,5 @@ -github "mxcl/PromiseKit" ~> 6.3 +#github "mxcl/PromiseKit" ~> 6.3 +github "dougzilla32/PromiseKit" "CoreCancel" github "mxcl/OMGHTTPURLRQ" ~> 3.2 -github "PromiseKit/Foundation" ~> 3.1 +#github "PromiseKit/Foundation" ~> 3.1 +github "dougzilla32/Foundation" "CoreCancel" diff --git a/Cartfile.resolved b/Cartfile.resolved index ebbdb14..afccdca 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,4 +1,4 @@ github "AliSoftware/OHHTTPStubs" "6.1.0" -github "PromiseKit/Foundation" "3.1.0" +github "dougzilla32/Foundation" "4b6d86210cf2f3a2010d37c44d7d63ccb20547b1" +github "dougzilla32/PromiseKit" "288f7fbabc0b33c558bf908a3a0770693223d4e0" github "mxcl/OMGHTTPURLRQ" "3.2.5" -github "mxcl/PromiseKit" "6.3.3" diff --git a/Sources/NSURLSession+OMG+Promise.swift b/Sources/NSURLSession+OMG+Promise.swift index eca8512..0e4b921 100644 --- a/Sources/NSURLSession+OMG+Promise.swift +++ b/Sources/NSURLSession+OMG+Promise.swift @@ -168,3 +168,141 @@ extension URLSession { } } } + +//////////////////////////////////////////////////////////// Cancellable wrappers + +extension URLSession { + /** + Makes a cancellable **GET** request to the provided URL. + + let p = URLSession.shared.GET("http://example.com", query: ["foo": "bar"]) + p.then { data -> Void in + //… + } + p.asImage().then { image -> Void in + //… + } + p.asDictionary().then { json -> Void in + //… + } + + - Parameter url: The URL to request. + - Parameter query: The parameters to be encoded as the query string for the GET request. + - Returns: A cancellable promise that represents the GET request. + */ + public func cancellableGET(_ url: String, query: [String: Any]? = nil) -> CancellablePromise<(data: Data, response: URLResponse)> { + return cancellable(GET(url, query: query)) + } + + /** + Makes a cancellable POST request to the provided URL passing form URL encoded + parameters. + + Form URL-encoding is the standard way to POST on the Internet, so + probably this is what you want. If it doesn’t work, try the `+POST:JSON` + variant. + + let url = "http://jsonplaceholder.typicode.com/posts" + let params = ["title": "foo", "body": "bar", "userId": 1] + URLSession.shared.POST(url, formData: params).asDictionary().then { json -> Void in + //… + } + + - Parameter url: The URL to request. + - Parameter formData: The parameters to be form URL-encoded and passed as the POST body. + - Returns: A cancellable promise that represents the POST request. + */ + public func cancellablePOST(_ url: String, formData: [String: Any]? = nil) -> CancellablePromise<(data: Data, response: URLResponse)> { + return cancellable(POST(url, formData: formData)) + } + + /** + Makes a cancellable POST request to the provided URL passing multipart form-data. + + let formData = OMGMultipartFormData() + let imgData = Data(contentsOfFile: "image.png") + formData.addFile(imgdata, parameterName: "file1", filename: "myimage1.png", contentType: "image/png") + + URLSession.shared.POST(url, multipartFormData: formData).then { data in + //… + } + + - Parameter url: The URL to request. + - Parameter multipartFormData: The parameters to be multipart form-data encoded and passed as the POST body. + - Returns: A cancellable promise that represents the POST request. + - SeeAlso: [https://github.com/mxcl/OMGHTTPURLRQ](OMGHTTPURLRQ) + */ + public func cancellablePOST(_ url: String, multipartFormData: OMGMultipartFormData) -> CancellablePromise<(data: Data, response: URLResponse)> { + return cancellable(POST(url, multipartFormData: multipartFormData)) + } + + /** + Makes a cancellable POST request to the provided URL passing JSON encoded + parameters. + + Most web servers nowadays support POST with either JSON or form + URL-encoding. If in doubt try form URL-encoded parameters first. + + let url = "http://jsonplaceholder.typicode.com/posts" + let params = ["title": "foo", "body": "bar", "userId": 1] + URLSession.shared.POST(url, json: params).asDictionary().then { json -> Void in + //… + } + + - Parameter url: The URL to request. + - Parameter json: The parameters to be JSON-encoded and passed as the POST body. + - Returns: A cancellable promise that represents the POST request. + */ + public func cancellablePOST(_ url: String, json: [String: Any]? = nil) -> CancellablePromise<(data: Data, response: URLResponse)> { + return cancellable(POST(url, json: json)) + } + + /** + Makes a cancellable PUT request to the provided URL passing JSON encoded parameters. + + let url = "http://jsonplaceholder.typicode.com/posts" + let params = ["title": "foo", "body": "bar", "userId": 1] + URLSession.shared.PUT(url, json: params).asDictionary().then { json -> Void in + //… + } + + - Parameter url: The URL to request. + - Parameter json: The parameters to be JSON-encoded and passed as the PUT body. + - Returns: A cancellable promise that represents the PUT request. + */ + public func cancellablePUT(_ url: String, json: [String: Any]? = nil) -> CancellablePromise<(data: Data, response: URLResponse)> { + return cancellable(PUT(url, json: json)) + } + + /** + Makes a cancellable DELETE request to the provided URL passing form URL-encoded + parameters. + + let url = "http://jsonplaceholder.typicode.com/posts/1" + URLSession.shared.DELETE(url).then.asDictionary() { json -> Void in + //… + } + + - Parameter url: The URL to request. + - Returns: A cancellable promise that represents the PUT request. + */ + public func cancellableDELETE(_ url: String) -> CancellablePromise<(data: Data, response: URLResponse)> { + return cancellable(DELETE(url)) + } + + /** + Makes a cancellable PATCH request to the provided URL passing the provided JSON parameters. + + let url = "http://jsonplaceholder.typicode.com/posts/1" + let params = ["foo": "bar"] + NSURLConnection.PATCH(url, json: params).asDictionary().then { json -> Void in + //… + } + - Parameter url: The URL to request. + - Parameter json: The JSON parameters to encode as the PATCH body. + - Returns: A cancellable promise that represents the PUT request. + */ + public func cancellablePATCH(_ url: String, json: [String: Any]? = nil) -> CancellablePromise<(data: Data, response: URLResponse)> { + return cancellable(PATCH(url, json: json)) + } +} diff --git a/Tests/TestNSURLSession.swift b/Tests/TestNSURLSession.swift index 8e3d77e..f9a60ac 100644 --- a/Tests/TestNSURLSession.swift +++ b/Tests/TestNSURLSession.swift @@ -72,3 +72,81 @@ class NSURLSessionTests: XCTestCase { OHHTTPStubs.removeAllStubs() } } + +//////////////////////////////////////////////////////////// Cancellation + +extension NSURLSessionTests { + func testCancel1() { + let json: NSDictionary = ["key1": "value1", "key2": ["value2A", "value2B"]] + + OHHTTPStubs.stubRequests(passingTest: { $0.url!.host == "example.com" }) { _ in + return OHHTTPStubsResponse(jsonObject: json, statusCode: 200, headers: nil) + } + + let ex = expectation(description: "") + URLSession.shared.cancellableGET("http://example.com").compactMap { + XCTFail() + try JSONSerialization.jsonObject(with: $0.data) + }.done { + XCTAssertEqual(json, $0 as? NSDictionary) + XCTFail() + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + }.cancel() + waitForExpectations(timeout: 1) + } + + func testCancel2() { + + // test that Promise chains thens + // this test because I don’t trust the Swift compiler + + let dummy = ("fred" as NSString).data(using: String.Encoding.utf8.rawValue)! + + OHHTTPStubs.stubRequests(passingTest: { $0.url!.host == "example.com" }) { _ in + return OHHTTPStubsResponse(data: dummy, statusCode: 200, headers: [:]) + } + + let ex = expectation(description: "") + + cancellable(after(seconds: 0.1)).then { () -> CancellablePromise<(data: Data, response: URLResponse)> in + let p = URLSession.shared.cancellableGET("http://example.com") + p.cancel() + return p + }.done { + XCTAssertEqual($0.data, dummy) + XCTFail() + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + } + + waitForExpectations(timeout: 1) + } + + func testCancelSyntax() { + let json: NSDictionary = ["key1": "value1", "key2": ["value2A", "value2B"]] + + OHHTTPStubs.stubRequests(passingTest: { + $0.url!.host == "example.com" + }, withStubResponse: { _ in + OHHTTPStubsResponse(jsonObject: json, statusCode: 200, headers: nil) + }) + + let p = URLSession.shared.cancellableGET("http://example.com", query: [ + "1": 1, + "2": 2 + ]) + + let ex = expectation(description: "") + p.compactMap { + p.cancel() + try JSONSerialization.jsonObject(with: $0.data) + }.done { + XCTAssertEqual(json, $0 as? NSDictionary) + XCTFail() + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + } + waitForExpectations(timeout: 1) + } +}