From b0da296aff9fe7f07d491e8b4e90d577b8867206 Mon Sep 17 00:00:00 2001 From: Yosuke Ishikawa Date: Sun, 25 Dec 2016 23:47:10 +0900 Subject: [PATCH 01/11] Add APIKit 4 migration guide --- Documentation/APIKit4MigrationGuide.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 Documentation/APIKit4MigrationGuide.md diff --git a/Documentation/APIKit4MigrationGuide.md b/Documentation/APIKit4MigrationGuide.md new file mode 100644 index 00000000..1572cbf4 --- /dev/null +++ b/Documentation/APIKit4MigrationGuide.md @@ -0,0 +1 @@ +# APIKit 4 Migration Guide From 263ba3f6009a35f9e19a0c6594c3ca184f603b88 Mon Sep 17 00:00:00 2001 From: matsuda Date: Mon, 9 Jan 2017 02:13:17 +0900 Subject: [PATCH 02/11] Add QueryParameters protocol to provide interface for URL query. --- APIKit.xcodeproj/project.pbxproj | 29 +++++++++++++++++++ .../QueryParameters/QueryParameters.swift | 7 +++++ .../URLEncodedQueryParameters.swift | 20 +++++++++++++ Sources/APIKit/Request.swift | 12 ++++---- .../URLEncodedQueryParametersTests.swift | 19 ++++++++++++ 5 files changed, 81 insertions(+), 6 deletions(-) create mode 100644 Sources/APIKit/QueryParameters/QueryParameters.swift create mode 100644 Sources/APIKit/QueryParameters/URLEncodedQueryParameters.swift create mode 100644 Tests/APIKitTests/QueryParameters/URLEncodedQueryParametersTests.swift diff --git a/APIKit.xcodeproj/project.pbxproj b/APIKit.xcodeproj/project.pbxproj index 1dc7c5ba..143f12c4 100644 --- a/APIKit.xcodeproj/project.pbxproj +++ b/APIKit.xcodeproj/project.pbxproj @@ -7,6 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + 0ED17E891E229EC700EC1114 /* QueryParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED17E881E229EC700EC1114 /* QueryParameters.swift */; }; + 0ED17E8B1E22A22900EC1114 /* URLEncodedQueryParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED17E8A1E22A22900EC1114 /* URLEncodedQueryParameters.swift */; }; + 0ED17E8E1E22AC8300EC1114 /* URLEncodedQueryParametersTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED17E8D1E22AC8300EC1114 /* URLEncodedQueryParametersTests.swift */; }; 141F12201C1C9ABE0026D415 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD5115241B1FFBA900514240 /* Result.framework */; }; 141F12311C1C9AC70026D415 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD5115241B1FFBA900514240 /* Result.framework */; }; 141F12361C1C9AC70026D415 /* Result.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = CD5115241B1FFBA900514240 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -80,6 +83,9 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 0ED17E881E229EC700EC1114 /* QueryParameters.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QueryParameters.swift; sourceTree = ""; }; + 0ED17E8A1E22A22900EC1114 /* URLEncodedQueryParameters.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLEncodedQueryParameters.swift; sourceTree = ""; }; + 0ED17E8D1E22AC8300EC1114 /* URLEncodedQueryParametersTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLEncodedQueryParametersTests.swift; sourceTree = ""; }; 141F120F1C1C96820026D415 /* Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Base.xcconfig; path = Configurations/Base.xcconfig; sourceTree = ""; }; 141F12101C1C96820026D415 /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Configurations/Debug.xcconfig; sourceTree = ""; }; 141F12111C1C96820026D415 /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Configurations/Release.xcconfig; sourceTree = ""; }; @@ -156,6 +162,24 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 0ED17E871E229EAC00EC1114 /* QueryParameters */ = { + isa = PBXGroup; + children = ( + 0ED17E881E229EC700EC1114 /* QueryParameters.swift */, + 0ED17E8A1E22A22900EC1114 /* URLEncodedQueryParameters.swift */, + ); + name = QueryParameters; + path = APIKit/QueryParameters; + sourceTree = ""; + }; + 0ED17E8C1E22AC5D00EC1114 /* QueryParameters */ = { + isa = PBXGroup; + children = ( + 0ED17E8D1E22AC8300EC1114 /* URLEncodedQueryParametersTests.swift */, + ); + path = QueryParameters; + sourceTree = ""; + }; 141F120E1C1C96690026D415 /* Configurations */ = { isa = PBXGroup; children = ( @@ -224,6 +248,7 @@ 7F698E451D9D680C00F1561D /* RequestTests.swift */, 7F698E491D9D680C00F1561D /* SessionCallbackQueueTests.swift */, 7F698E4A1D9D680C00F1561D /* SessionTests.swift */, + 0ED17E8C1E22AC5D00EC1114 /* QueryParameters */, 7F698E3B1D9D680C00F1561D /* BodyParametersType */, 7F698E401D9D680C00F1561D /* DataParserType */, 7F698E461D9D680C00F1561D /* SessionAdapterType */, @@ -294,6 +319,7 @@ 7F7048CB1D9D89BE003C99F6 /* Session.swift */, 7F7048CC1D9D89BE003C99F6 /* Unavailable.swift */, 7F85FB8B1C9D317300CEE132 /* SessionAdapter */, + 0ED17E871E229EAC00EC1114 /* QueryParameters */, 7F18BD0D1C972C38003A31DF /* BodyParameters */, 7FA19A441C9CC9A2005D25AE /* DataParser */, 7F18BD161C9730ED003A31DF /* Serializations */, @@ -457,12 +483,14 @@ files = ( 7F7048D31D9D89BE003C99F6 /* Unavailable.swift in Sources */, 7F7048D11D9D89BE003C99F6 /* Request.swift in Sources */, + 0ED17E891E229EC700EC1114 /* QueryParameters.swift in Sources */, 7F7048E81D9D8A08003C99F6 /* DataParser.swift in Sources */, 7F7048CE1D9D89BE003C99F6 /* CallbackQueue.swift in Sources */, 7F7048DE1D9D89FB003C99F6 /* AbstractInputStream.m in Sources */, 7F7048E31D9D89FB003C99F6 /* MultipartFormDataBodyParameters.swift in Sources */, 7F7048F01D9D8A12003C99F6 /* ResponseError.swift in Sources */, 7F7048EA1D9D8A08003C99F6 /* JSONDataParser.swift in Sources */, + 0ED17E8B1E22A22900EC1114 /* URLEncodedQueryParameters.swift in Sources */, 7F7048D21D9D89BE003C99F6 /* Session.swift in Sources */, 7F7048E01D9D89FB003C99F6 /* Data+InputStream.swift in Sources */, 7F7048DF1D9D89FB003C99F6 /* BodyParameters.swift in Sources */, @@ -491,6 +519,7 @@ 7F698E501D9D680C00F1561D /* FormURLEncodedBodyParametersTests.swift in Sources */, 7F698E581D9D680C00F1561D /* RequestTests.swift in Sources */, ECA8314A1DE4DEBE004EB1B5 /* ProtobufDataParserTests.swift in Sources */, + 0ED17E8E1E22AC8300EC1114 /* URLEncodedQueryParametersTests.swift in Sources */, 7F698E5E1D9D680C00F1561D /* TestRequest.swift in Sources */, 7F698E601D9D680C00F1561D /* TestSessionTask.swift in Sources */, 7FA1690D1D9D8C80006C982B /* HTTPStub.swift in Sources */, diff --git a/Sources/APIKit/QueryParameters/QueryParameters.swift b/Sources/APIKit/QueryParameters/QueryParameters.swift new file mode 100644 index 00000000..6e9f16a5 --- /dev/null +++ b/Sources/APIKit/QueryParameters/QueryParameters.swift @@ -0,0 +1,7 @@ +import Foundation + +/// `QueryParameters` provides interface to generate HTTP URL query strings. +public protocol QueryParameters { + /// Generate URL query strings. + func encode() -> String? +} diff --git a/Sources/APIKit/QueryParameters/URLEncodedQueryParameters.swift b/Sources/APIKit/QueryParameters/URLEncodedQueryParameters.swift new file mode 100644 index 00000000..e4c0f476 --- /dev/null +++ b/Sources/APIKit/QueryParameters/URLEncodedQueryParameters.swift @@ -0,0 +1,20 @@ +import Foundation + +/// `URLEncodedQueryParameters` serializes form object for HTTP URL query. +public struct URLEncodedQueryParameters: QueryParameters { + /// The parameters to be url encoded. + public let parameters: Any + + /// Returns `URLEncodedQueryParameters` that is initialized with parameters. + public init(parameters: Any) { + self.parameters = parameters + } + + /// Generate url encoded `String`. + public func encode() -> String? { + guard let parameters = parameters as? [String: Any], !parameters.isEmpty else { + return nil + } + return URLEncodedSerialization.string(from: parameters) + } +} diff --git a/Sources/APIKit/Request.swift b/Sources/APIKit/Request.swift index b14b29ab..18bcb80e 100644 --- a/Sources/APIKit/Request.swift +++ b/Sources/APIKit/Request.swift @@ -29,7 +29,7 @@ public protocol Request { /// The actual parameters for the URL query. The values of this property will be escaped using `URLEncodedSerialization`. /// If this property is not implemented and `method.prefersQueryParameter` is `true`, the value of this property /// will be computed from `parameters`. - var queryParameters: [String: Any]? { get } + var queryParameters: QueryParameters? { get } /// The actual parameters for the HTTP body. If this property is not implemented and `method.prefersQueryParameter` is `false`, /// the value of this property will be computed from `parameters` using `JSONBodyParameters`. @@ -66,12 +66,12 @@ public extension Request { return nil } - public var queryParameters: [String: Any]? { - guard let parameters = parameters as? [String: Any], method.prefersQueryParameters else { + public var queryParameters: QueryParameters? { + guard let parameters = parameters, method.prefersQueryParameters else { return nil } - return parameters + return URLEncodedQueryParameters(parameters: parameters) } public var bodyParameters: BodyParameters? { @@ -111,8 +111,8 @@ public extension Request { var urlRequest = URLRequest(url: url) - if let queryParameters = queryParameters, !queryParameters.isEmpty { - components.percentEncodedQuery = URLEncodedSerialization.string(from: queryParameters) + if let queryString = queryParameters?.encode(), !queryString.isEmpty { + components.percentEncodedQuery = queryString } if let bodyParameters = bodyParameters { diff --git a/Tests/APIKitTests/QueryParameters/URLEncodedQueryParametersTests.swift b/Tests/APIKitTests/QueryParameters/URLEncodedQueryParametersTests.swift new file mode 100644 index 00000000..a764e00c --- /dev/null +++ b/Tests/APIKitTests/QueryParameters/URLEncodedQueryParametersTests.swift @@ -0,0 +1,19 @@ +import XCTest +import APIKit + +class URLEncodedQueryParametersTests: XCTestCase { + func testURLEncodedSuccess() { + let object: [String: Any] = ["foo": "string", "bar": 1, "q": "こんにちは"] + let parameters = URLEncodedQueryParameters(parameters: object) + guard let query = parameters.encode() else { + XCTFail() + return + } + + let items = query.components(separatedBy: "&") + XCTAssertEqual(items.count, 3) + XCTAssertTrue(items.contains("foo=string")) + XCTAssertTrue(items.contains("bar=1")) + XCTAssertTrue(items.contains("q=%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF")) + } +} From 6bb796130a211892fea6add2823d45a37020c023 Mon Sep 17 00:00:00 2001 From: Morgan Harris Date: Wed, 18 Jan 2017 09:31:03 +1100 Subject: [PATCH 03/11] Added .DS_Store to gitignore How was this not already there? --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index d8d32c87..ae8e14c9 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,5 @@ Carthage/Build fastlane/report.xml fastlane/screenshots + +.DS_Store \ No newline at end of file From 261af5fee62b133a452c798a6ad2c6996bb710ac Mon Sep 17 00:00:00 2001 From: Morgan Harris Date: Wed, 18 Jan 2017 09:44:42 +1100 Subject: [PATCH 04/11] Allow DataParser to return typed objects DataParser should not be required to return Any and have objects checked for type safety at runtime. This allows us to use the Swift build time type checking system with very minimal changes to existing code. The only API breaking change is that Request classes using the default JSONDataParser should now conform to JSONRequest, instead of just Request. This is because protocol extensions cannot declare default associated types. --- Sources/APIKit/DataParser/DataParser.swift | 4 +++- Sources/APIKit/Request.swift | 24 ++++++++++++------- Tests/APIKitTests/SessionTests.swift | 2 +- .../TestComponents/TestRequest.swift | 2 +- 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/Sources/APIKit/DataParser/DataParser.swift b/Sources/APIKit/DataParser/DataParser.swift index 671f465f..1414947b 100644 --- a/Sources/APIKit/DataParser/DataParser.swift +++ b/Sources/APIKit/DataParser/DataParser.swift @@ -2,10 +2,12 @@ import Foundation /// `DataParser` protocol provides inteface to parse HTTP response body and to state Content-Type to accept. public protocol DataParser { + associatedtype Parsed + /// Value for `Accept` header field of HTTP request. var contentType: String? { get } /// Return `Any` that expresses structure of response such as JSON and XML. /// - Throws: `Error` when parser encountered invalid format data. - func parse(data: Data) throws -> Any + func parse(data: Data) throws -> Parsed } diff --git a/Sources/APIKit/Request.swift b/Sources/APIKit/Request.swift index b14b29ab..738b28d8 100644 --- a/Sources/APIKit/Request.swift +++ b/Sources/APIKit/Request.swift @@ -11,6 +11,7 @@ import Result public protocol Request { /// The response type associated with the request type. associatedtype Response + associatedtype Parser: DataParser /// The base URL. var baseURL: URL { get } @@ -41,7 +42,7 @@ public protocol Request { var headerFields: [String: String] { get } /// The parser object that states `Content-Type` to accept and parses response body. - var dataParser: DataParser { get } + var dataParser: Parser { get } /// Intercepts `URLRequest` which is created by `Request.buildURLRequest()`. If an error is /// thrown in this method, the result of `Session.send()` turns `.failure(.requestError(error))`. @@ -53,15 +54,16 @@ public protocol Request { /// The default implementation of this method is provided to throw `RequestError.unacceptableStatusCode` /// if the HTTP status code is not in `200..<300`. /// - Throws: `Error` - func intercept(object: Any, urlResponse: HTTPURLResponse) throws -> Any + func intercept(object: Parser.Parsed, urlResponse: HTTPURLResponse) throws -> Parser.Parsed /// Build `Response` instance from raw response object. This method is called after /// `intercept(object:urlResponse:)` if it does not throw any error. /// - Throws: `Error` - func response(from object: Any, urlResponse: HTTPURLResponse) throws -> Response + func response(from object: Parser.Parsed, urlResponse: HTTPURLResponse) throws -> Response } public extension Request { + public var parameters: Any? { return nil } @@ -86,15 +88,11 @@ public extension Request { return [:] } - public var dataParser: DataParser { - return JSONDataParser(readingOptions: []) - } - public func intercept(urlRequest: URLRequest) throws -> URLRequest { return urlRequest } - public func intercept(object: Any, urlResponse: HTTPURLResponse) throws -> Any { + public func intercept(object: Parser.Parsed, urlResponse: HTTPURLResponse) throws -> Parser.Parsed { guard 200..<300 ~= urlResponse.statusCode else { throw ResponseError.unacceptableStatusCode(urlResponse.statusCode) } @@ -146,3 +144,13 @@ public extension Request { return try response(from: passedObject, urlResponse: urlResponse) } } + +public protocol JSONRequest: Request {} + +public extension JSONRequest { + + public var dataParser: JSONDataParser { + return JSONDataParser(readingOptions: []) + } + +} diff --git a/Tests/APIKitTests/SessionTests.swift b/Tests/APIKitTests/SessionTests.swift index 138fdf9f..62f0d503 100644 --- a/Tests/APIKitTests/SessionTests.swift +++ b/Tests/APIKitTests/SessionTests.swift @@ -175,7 +175,7 @@ class SessionTests: XCTestCase { waitForExpectations(timeout: 1.0, handler: nil) } - struct AnotherTestRequest: Request { + struct AnotherTestRequest: JSONRequest { typealias Response = Void var baseURL: URL { diff --git a/Tests/APIKitTests/TestComponents/TestRequest.swift b/Tests/APIKitTests/TestComponents/TestRequest.swift index e3bc69aa..32cd5317 100644 --- a/Tests/APIKitTests/TestComponents/TestRequest.swift +++ b/Tests/APIKitTests/TestComponents/TestRequest.swift @@ -1,7 +1,7 @@ import Foundation import APIKit -struct TestRequest: Request { +struct TestRequest: JSONRequest { var absoluteURL: URL? { let urlRequest = try? buildURLRequest() return urlRequest?.url From 57c38376d9f004a28fcee3c26708665ce7d88d4f Mon Sep 17 00:00:00 2001 From: taish Date: Wed, 22 Mar 2017 22:18:34 +0900 Subject: [PATCH 05/11] Add a way to get progress of uploading While the upload is in progress, `progressHandler` closure is periodically called. Therefore you can get information of uploading about `didSendBodyData` `totalBytesSent` `totalBytesExpectedToSend`. These parameters are defined by `urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)` in `URLSessionTaskDelegate`. --- Sources/APIKit/Session.swift | 47 ++++++++++--------- .../SessionAdapter/SessionAdapter.swift | 2 +- .../SessionAdapter/URLSessionAdapter.swift | 16 ++++++- .../URLSessionAdapterSubclassTests.swift | 26 ++++++++++ Tests/APIKitTests/SessionTests.swift | 24 ++++++++-- .../TestComponents/TestSessionAdapter.swift | 5 +- .../TestComponents/TestSessionTask.swift | 6 ++- 7 files changed, 96 insertions(+), 30 deletions(-) diff --git a/Sources/APIKit/Session.swift b/Sources/APIKit/Session.swift index 72a9405e..5d9cc8a9 100644 --- a/Sources/APIKit/Session.swift +++ b/Sources/APIKit/Session.swift @@ -37,8 +37,8 @@ open class Session { /// - parameter handler: The closure that receives result of the request. /// - returns: The new session task. @discardableResult - open class func send(_ request: Request, callbackQueue: CallbackQueue? = nil, handler: @escaping (Result) -> Void = { _ in }) -> SessionTask? { - return shared.send(request, callbackQueue: callbackQueue, handler: handler) + open class func send(_ request: Request, callbackQueue: CallbackQueue? = nil, progressHandler: @escaping (Int64, Int64, Int64) -> Void = { _ in }, handler: @escaping (Result) -> Void = { _ in }) -> SessionTask? { + return shared.send(request, callbackQueue: callbackQueue, progressHandler: progressHandler, handler: handler) } /// Calls `cancelRequests(with:passingTest:)` of `sharedSession`. @@ -55,7 +55,7 @@ open class Session { /// - parameter handler: The closure that receives result of the request. /// - returns: The new session task. @discardableResult - open func send(_ request: Request, callbackQueue: CallbackQueue? = nil, handler: @escaping (Result) -> Void = { _ in }) -> SessionTask? { + open func send(_ request: Request, callbackQueue: CallbackQueue? = nil, progressHandler: @escaping (Int64, Int64, Int64) -> Void = { _ in }, handler: @escaping (Result) -> Void = { _ in }) -> SessionTask? { let callbackQueue = callbackQueue ?? self.callbackQueue let urlRequest: URLRequest @@ -68,28 +68,33 @@ open class Session { return nil } - let task = adapter.createTask(with: urlRequest) { data, urlResponse, error in - let result: Result - - switch (data, urlResponse, error) { - case (_, _, let error?): - result = .failure(.connectionError(error)) + let task = adapter.createTask(with: urlRequest, + progressHandler: { bytesSent, totalBytesSent, totalBytesExpectedToSend in + progressHandler(bytesSent, totalBytesSent, totalBytesExpectedToSend) + }, + handler: { data, urlResponse, error in + let result: Result + + switch (data, urlResponse, error) { + case (_, _, let error?): + result = .failure(.connectionError(error)) + + case (let data?, let urlResponse as HTTPURLResponse, _): + do { + result = .success(try request.parse(data: data as Data, urlResponse: urlResponse)) + } catch { + result = .failure(.responseError(error)) + } - case (let data?, let urlResponse as HTTPURLResponse, _): - do { - result = .success(try request.parse(data: data as Data, urlResponse: urlResponse)) - } catch { - result = .failure(.responseError(error)) + default: + result = .failure(.responseError(ResponseError.nonHTTPURLResponse(urlResponse))) } - default: - result = .failure(.responseError(ResponseError.nonHTTPURLResponse(urlResponse))) - } - - callbackQueue.execute { - handler(result) + callbackQueue.execute { + handler(result) + } } - } + ) setRequest(request, forTask: task) task.resume() diff --git a/Sources/APIKit/SessionAdapter/SessionAdapter.swift b/Sources/APIKit/SessionAdapter/SessionAdapter.swift index d123dae0..5221ea41 100644 --- a/Sources/APIKit/SessionAdapter/SessionAdapter.swift +++ b/Sources/APIKit/SessionAdapter/SessionAdapter.swift @@ -11,7 +11,7 @@ public protocol SessionTask: class { /// with `Session`. public protocol SessionAdapter { /// Returns instance that conforms to `SessionTask`. `handler` must be called after success or failure. - func createTask(with URLRequest: URLRequest, handler: @escaping (Data?, URLResponse?, Error?) -> Void) -> SessionTask + func createTask(with URLRequest: URLRequest, progressHandler: @escaping (Int64, Int64, Int64) -> Void, handler: @escaping (Data?, URLResponse?, Error?) -> Void) -> SessionTask /// Collects tasks from backend networking stack. `handler` must be called after collecting. func getTasks(with handler: @escaping ([SessionTask]) -> Void) diff --git a/Sources/APIKit/SessionAdapter/URLSessionAdapter.swift b/Sources/APIKit/SessionAdapter/URLSessionAdapter.swift index cc96ab6b..0a930c3a 100644 --- a/Sources/APIKit/SessionAdapter/URLSessionAdapter.swift +++ b/Sources/APIKit/SessionAdapter/URLSessionAdapter.swift @@ -6,6 +6,7 @@ extension URLSessionTask: SessionTask { private var dataTaskResponseBufferKey = 0 private var taskAssociatedObjectCompletionHandlerKey = 0 +private var taskAssociatedObjectProgressHandlerKey = 0 /// `URLSessionAdapter` connects `URLSession` with `Session`. /// @@ -25,11 +26,12 @@ open class URLSessionAdapter: NSObject, SessionAdapter, URLSessionDelegate, URLS } /// Creates `URLSessionDataTask` instance using `dataTaskWithRequest(_:completionHandler:)`. - open func createTask(with URLRequest: URLRequest, handler: @escaping (Data?, URLResponse?, Error?) -> Void) -> SessionTask { + open func createTask(with URLRequest: URLRequest, progressHandler: @escaping (Int64, Int64, Int64) -> Void, handler: @escaping (Data?, URLResponse?, Error?) -> Void) -> SessionTask { let task = urlSession.dataTask(with: URLRequest) setBuffer(NSMutableData(), forTask: task) setHandler(handler, forTask: task) + setProgressHandler(progressHandler, forTask: task) return task } @@ -61,6 +63,13 @@ open class URLSessionAdapter: NSObject, SessionAdapter, URLSessionDelegate, URLS return objc_getAssociatedObject(task, &taskAssociatedObjectCompletionHandlerKey) as? (Data?, URLResponse?, Error?) -> Void } + private func setProgressHandler(_ progressHandler: @escaping (Int64, Int64, Int64) -> Void, forTask task: URLSessionTask) { + objc_setAssociatedObject(task, &taskAssociatedObjectProgressHandlerKey, progressHandler as Any, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + + private func progressHandler(for task: URLSessionTask) -> ((Int64, Int64, Int64) -> Void)? { + return objc_getAssociatedObject(task, &taskAssociatedObjectCompletionHandlerKey) as? (Int64, Int64, Int64) -> Void + } // MARK: URLSessionTaskDelegate open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { handler(for: task)?(buffer(for: task) as Data?, task.response, error) @@ -70,4 +79,9 @@ open class URLSessionAdapter: NSObject, SessionAdapter, URLSessionDelegate, URLS open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { buffer(for: dataTask)?.append(data) } + + // MARK: URLSessionDataDelegate + open func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) { + progressHandler(for: task)?(bytesSent, totalBytesSent, totalBytesExpectedToSend) + } } diff --git a/Tests/APIKitTests/SessionAdapterType/URLSessionAdapterSubclassTests.swift b/Tests/APIKitTests/SessionAdapterType/URLSessionAdapterSubclassTests.swift index 4820dde3..8836c458 100644 --- a/Tests/APIKitTests/SessionAdapterType/URLSessionAdapterSubclassTests.swift +++ b/Tests/APIKitTests/SessionAdapterType/URLSessionAdapterSubclassTests.swift @@ -15,6 +15,11 @@ class URLSessionAdapterSubclassTests: XCTestCase { functionCallFlags[(#function)] = true super.urlSession(session, dataTask: dataTask, didReceive: data) } + + override func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) { + functionCallFlags[(#function)] = true + super.urlSession(session, task: task, didSendBodyData: bytesSent, totalBytesSent: totalBytesSent, totalBytesExpectedToSend: totalBytesExpectedToSend) + } } var adapter: SessionAdapter! @@ -50,4 +55,25 @@ class URLSessionAdapterSubclassTests: XCTestCase { XCTAssertEqual(adapter.functionCallFlags["urlSession(_:task:didCompleteWithError:)"], true) XCTAssertEqual(adapter.functionCallFlags["urlSession(_:dataTask:didReceive:)"], true) } + + // Limitation: 'urlSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:' delegate method will never be called when you stub the request using subclass of URLProtocol. + func testDelegateProgressMethodCall() { + let expectation = self.expectation(description: "wait for response") + let request = TestRequest(baseURL: "https://httpbin.org", path: "/post", method: .post) + let configuration = URLSessionConfiguration.default + let adapter = SessionAdapter(configuration: configuration) + let session = Session(adapter: adapter) + + session.send(request, + handler: { result in + if case .failure = result { + XCTFail() + } + + expectation.fulfill() + }) + + waitForExpectations(timeout: 10.0, handler: nil) + XCTAssertEqual(adapter.functionCallFlags["urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)"], true) + } } diff --git a/Tests/APIKitTests/SessionTests.swift b/Tests/APIKitTests/SessionTests.swift index 138fdf9f..cf46eb29 100644 --- a/Tests/APIKitTests/SessionTests.swift +++ b/Tests/APIKitTests/SessionTests.swift @@ -223,6 +223,23 @@ class SessionTests: XCTestCase { waitForExpectations(timeout: 1.0, handler: nil) } + func testProgress() { + let dictionary = ["key": "value"] + adapter.data = try! JSONSerialization.data(withJSONObject: dictionary, options: []) + + let expectation = self.expectation(description: "wait for response") + let request = TestRequest(method: .post) + + session.send(request, progressHandler: { bytesSent, totalBytesSent, totalBytesExpectedToSend in + XCTAssertNotNil(bytesSent) + XCTAssertNotNil(totalBytesSent) + XCTAssertNotNil(totalBytesExpectedToSend) + expectation.fulfill() + }) + + waitForExpectations(timeout: 1.0, handler: nil) + } + // MARK: Class methods func testSharedSession() { XCTAssert(Session.shared === Session.shared) @@ -238,12 +255,13 @@ class SessionTests: XCTestCase { return testSesssion } - override func send(_ request: Request, callbackQueue: CallbackQueue?, handler: @escaping (Result) -> Void) -> SessionTask? { + override func send(_ request: Request, callbackQueue: CallbackQueue?, progressHandler: @escaping (Int64, Int64, Int64) -> Void, handler: @escaping (Result) -> Void) -> SessionTask? { + functionCallFlags[(#function)] = true return super.send(request) } - override func cancelRequests(with requestType: Request.Type, passingTest test: @escaping (Request) -> Bool) { + override func cancelRequests(with requestType: Request.Type, passingTest test: @escaping (Request) -> Bool) { functionCallFlags[(#function)] = true } } @@ -252,7 +270,7 @@ class SessionTests: XCTestCase { SessionSubclass.send(TestRequest()) SessionSubclass.cancelRequests(with: TestRequest.self) - XCTAssertEqual(testSession.functionCallFlags["send(_:callbackQueue:handler:)"], true) + XCTAssertEqual(testSession.functionCallFlags["send(_:callbackQueue:progressHandler:handler:)"], true) XCTAssertEqual(testSession.functionCallFlags["cancelRequests(with:passingTest:)"], true) } } diff --git a/Tests/APIKitTests/TestComponents/TestSessionAdapter.swift b/Tests/APIKitTests/TestComponents/TestSessionAdapter.swift index 4813c23c..8f879b4e 100644 --- a/Tests/APIKitTests/TestComponents/TestSessionAdapter.swift +++ b/Tests/APIKitTests/TestComponents/TestSessionAdapter.swift @@ -42,6 +42,7 @@ class TestSessionAdapter: SessionAdapter { if task.cancelled { task.handler(nil, nil, Error.cancelled) } else { + task.progressHandler(1, 1, 1) task.handler(data, urlResponse, error) } } @@ -49,8 +50,8 @@ class TestSessionAdapter: SessionAdapter { tasks = [] } - func createTask(with URLRequest: URLRequest, handler: @escaping (Data?, URLResponse?, Swift.Error?) -> Void) -> SessionTask { - let task = TestSessionTask(handler: handler) + func createTask(with URLRequest: URLRequest, progressHandler: @escaping (Int64, Int64, Int64) -> Void, handler: @escaping (Data?, URLResponse?, Swift.Error?) -> Void) -> SessionTask { + let task = TestSessionTask(progressHandler: progressHandler, handler: handler) tasks.append(task) return task diff --git a/Tests/APIKitTests/TestComponents/TestSessionTask.swift b/Tests/APIKitTests/TestComponents/TestSessionTask.swift index 5bf7927a..745cf24f 100644 --- a/Tests/APIKitTests/TestComponents/TestSessionTask.swift +++ b/Tests/APIKitTests/TestComponents/TestSessionTask.swift @@ -2,12 +2,14 @@ import Foundation import APIKit class TestSessionTask: SessionTask { - + var handler: (Data?, URLResponse?, Error?) -> Void + var progressHandler: (Int64, Int64, Int64) -> Void var cancelled = false - init(handler: @escaping (Data?, URLResponse?, Error?) -> Void) { + init(progressHandler: @escaping (Int64, Int64, Int64) -> Void, handler: @escaping (Data?, URLResponse?, Error?) -> Void) { self.handler = handler + self.progressHandler = progressHandler } func resume() { From 8ebecd04f3e3007502756645305d1d45022c9607 Mon Sep 17 00:00:00 2001 From: taish Date: Sat, 25 Mar 2017 16:41:22 +0900 Subject: [PATCH 06/11] rename argment `progressHandler` to `completionHandler` in `Session` class. --- Sources/APIKit/Session.swift | 10 +++++----- .../URLSessionAdapterSubclassTests.swift | 2 +- Tests/APIKitTests/SessionTests.swift | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Sources/APIKit/Session.swift b/Sources/APIKit/Session.swift index 5d9cc8a9..00e4efb1 100644 --- a/Sources/APIKit/Session.swift +++ b/Sources/APIKit/Session.swift @@ -37,8 +37,8 @@ open class Session { /// - parameter handler: The closure that receives result of the request. /// - returns: The new session task. @discardableResult - open class func send(_ request: Request, callbackQueue: CallbackQueue? = nil, progressHandler: @escaping (Int64, Int64, Int64) -> Void = { _ in }, handler: @escaping (Result) -> Void = { _ in }) -> SessionTask? { - return shared.send(request, callbackQueue: callbackQueue, progressHandler: progressHandler, handler: handler) + open class func send(_ request: Request, callbackQueue: CallbackQueue? = nil, progressHandler: @escaping (Int64, Int64, Int64) -> Void = { _ in }, completionHandler: @escaping (Result) -> Void = { _ in }) -> SessionTask? { + return shared.send(request, callbackQueue: callbackQueue, progressHandler: progressHandler, completionHandler: completionHandler) } /// Calls `cancelRequests(with:passingTest:)` of `sharedSession`. @@ -55,7 +55,7 @@ open class Session { /// - parameter handler: The closure that receives result of the request. /// - returns: The new session task. @discardableResult - open func send(_ request: Request, callbackQueue: CallbackQueue? = nil, progressHandler: @escaping (Int64, Int64, Int64) -> Void = { _ in }, handler: @escaping (Result) -> Void = { _ in }) -> SessionTask? { + open func send(_ request: Request, callbackQueue: CallbackQueue? = nil, progressHandler: @escaping (Int64, Int64, Int64) -> Void = { _ in }, completionHandler: @escaping (Result) -> Void = { _ in }) -> SessionTask? { let callbackQueue = callbackQueue ?? self.callbackQueue let urlRequest: URLRequest @@ -63,7 +63,7 @@ open class Session { urlRequest = try request.buildURLRequest() } catch { callbackQueue.execute { - handler(.failure(.requestError(error))) + completionHandler(.failure(.requestError(error))) } return nil } @@ -91,7 +91,7 @@ open class Session { } callbackQueue.execute { - handler(result) + completionHandler(result) } } ) diff --git a/Tests/APIKitTests/SessionAdapterType/URLSessionAdapterSubclassTests.swift b/Tests/APIKitTests/SessionAdapterType/URLSessionAdapterSubclassTests.swift index 8836c458..6e03bf67 100644 --- a/Tests/APIKitTests/SessionAdapterType/URLSessionAdapterSubclassTests.swift +++ b/Tests/APIKitTests/SessionAdapterType/URLSessionAdapterSubclassTests.swift @@ -65,7 +65,7 @@ class URLSessionAdapterSubclassTests: XCTestCase { let session = Session(adapter: adapter) session.send(request, - handler: { result in + completionHandler: { result in if case .failure = result { XCTFail() } diff --git a/Tests/APIKitTests/SessionTests.swift b/Tests/APIKitTests/SessionTests.swift index cf46eb29..66e44fce 100644 --- a/Tests/APIKitTests/SessionTests.swift +++ b/Tests/APIKitTests/SessionTests.swift @@ -255,7 +255,7 @@ class SessionTests: XCTestCase { return testSesssion } - override func send(_ request: Request, callbackQueue: CallbackQueue?, progressHandler: @escaping (Int64, Int64, Int64) -> Void, handler: @escaping (Result) -> Void) -> SessionTask? { + override func send(_ request: Request, callbackQueue: CallbackQueue?, progressHandler: @escaping (Int64, Int64, Int64) -> Void, completionHandler: @escaping (Result) -> Void) -> SessionTask? { functionCallFlags[(#function)] = true return super.send(request) @@ -270,7 +270,7 @@ class SessionTests: XCTestCase { SessionSubclass.send(TestRequest()) SessionSubclass.cancelRequests(with: TestRequest.self) - XCTAssertEqual(testSession.functionCallFlags["send(_:callbackQueue:progressHandler:handler:)"], true) + XCTAssertEqual(testSession.functionCallFlags["send(_:callbackQueue:progressHandler:completionHandler:)"], true) XCTAssertEqual(testSession.functionCallFlags["cancelRequests(with:passingTest:)"], true) } } From aa088180ffb2d28b43afbc060a7f98c520a34594 Mon Sep 17 00:00:00 2001 From: taish Date: Sat, 25 Mar 2017 17:20:20 +0900 Subject: [PATCH 07/11] rename argment `progressHandler` to `completionHandler` in `SessionAdapter` protocol. --- Sources/APIKit/Session.swift | 2 +- Sources/APIKit/SessionAdapter/SessionAdapter.swift | 2 +- Sources/APIKit/SessionAdapter/URLSessionAdapter.swift | 4 ++-- Tests/APIKitTests/TestComponents/TestSessionAdapter.swift | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Sources/APIKit/Session.swift b/Sources/APIKit/Session.swift index 00e4efb1..9726c7b7 100644 --- a/Sources/APIKit/Session.swift +++ b/Sources/APIKit/Session.swift @@ -72,7 +72,7 @@ open class Session { progressHandler: { bytesSent, totalBytesSent, totalBytesExpectedToSend in progressHandler(bytesSent, totalBytesSent, totalBytesExpectedToSend) }, - handler: { data, urlResponse, error in + completionHandler: { data, urlResponse, error in let result: Result switch (data, urlResponse, error) { diff --git a/Sources/APIKit/SessionAdapter/SessionAdapter.swift b/Sources/APIKit/SessionAdapter/SessionAdapter.swift index 5221ea41..3db2b583 100644 --- a/Sources/APIKit/SessionAdapter/SessionAdapter.swift +++ b/Sources/APIKit/SessionAdapter/SessionAdapter.swift @@ -11,7 +11,7 @@ public protocol SessionTask: class { /// with `Session`. public protocol SessionAdapter { /// Returns instance that conforms to `SessionTask`. `handler` must be called after success or failure. - func createTask(with URLRequest: URLRequest, progressHandler: @escaping (Int64, Int64, Int64) -> Void, handler: @escaping (Data?, URLResponse?, Error?) -> Void) -> SessionTask + func createTask(with URLRequest: URLRequest, progressHandler: @escaping (Int64, Int64, Int64) -> Void, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> SessionTask /// Collects tasks from backend networking stack. `handler` must be called after collecting. func getTasks(with handler: @escaping ([SessionTask]) -> Void) diff --git a/Sources/APIKit/SessionAdapter/URLSessionAdapter.swift b/Sources/APIKit/SessionAdapter/URLSessionAdapter.swift index 0a930c3a..243d9394 100644 --- a/Sources/APIKit/SessionAdapter/URLSessionAdapter.swift +++ b/Sources/APIKit/SessionAdapter/URLSessionAdapter.swift @@ -26,11 +26,11 @@ open class URLSessionAdapter: NSObject, SessionAdapter, URLSessionDelegate, URLS } /// Creates `URLSessionDataTask` instance using `dataTaskWithRequest(_:completionHandler:)`. - open func createTask(with URLRequest: URLRequest, progressHandler: @escaping (Int64, Int64, Int64) -> Void, handler: @escaping (Data?, URLResponse?, Error?) -> Void) -> SessionTask { + open func createTask(with URLRequest: URLRequest, progressHandler: @escaping (Int64, Int64, Int64) -> Void, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> SessionTask { let task = urlSession.dataTask(with: URLRequest) setBuffer(NSMutableData(), forTask: task) - setHandler(handler, forTask: task) + setHandler(completionHandler, forTask: task) setProgressHandler(progressHandler, forTask: task) return task diff --git a/Tests/APIKitTests/TestComponents/TestSessionAdapter.swift b/Tests/APIKitTests/TestComponents/TestSessionAdapter.swift index 8f879b4e..c4fb86ae 100644 --- a/Tests/APIKitTests/TestComponents/TestSessionAdapter.swift +++ b/Tests/APIKitTests/TestComponents/TestSessionAdapter.swift @@ -50,8 +50,8 @@ class TestSessionAdapter: SessionAdapter { tasks = [] } - func createTask(with URLRequest: URLRequest, progressHandler: @escaping (Int64, Int64, Int64) -> Void, handler: @escaping (Data?, URLResponse?, Swift.Error?) -> Void) -> SessionTask { - let task = TestSessionTask(progressHandler: progressHandler, handler: handler) + func createTask(with URLRequest: URLRequest, progressHandler: @escaping (Int64, Int64, Int64) -> Void, completionHandler: @escaping (Data?, URLResponse?, Swift.Error?) -> Void) -> SessionTask { + let task = TestSessionTask(progressHandler: progressHandler, handler: completionHandler) tasks.append(task) return task From 87488209692fb1e90b074b83f50943733ccf8254 Mon Sep 17 00:00:00 2001 From: taish Date: Sat, 25 Mar 2017 17:24:34 +0900 Subject: [PATCH 08/11] rename argment `progressHandler` to `completionHandler` in `TestSessionTask` --- Tests/APIKitTests/TestComponents/TestSessionAdapter.swift | 6 +++--- Tests/APIKitTests/TestComponents/TestSessionTask.swift | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Tests/APIKitTests/TestComponents/TestSessionAdapter.swift b/Tests/APIKitTests/TestComponents/TestSessionAdapter.swift index c4fb86ae..b9c3cdb1 100644 --- a/Tests/APIKitTests/TestComponents/TestSessionAdapter.swift +++ b/Tests/APIKitTests/TestComponents/TestSessionAdapter.swift @@ -40,10 +40,10 @@ class TestSessionAdapter: SessionAdapter { func executeAllTasks() { for task in tasks { if task.cancelled { - task.handler(nil, nil, Error.cancelled) + task.completionHandler(nil, nil, Error.cancelled) } else { task.progressHandler(1, 1, 1) - task.handler(data, urlResponse, error) + task.completionHandler(data, urlResponse, error) } } @@ -51,7 +51,7 @@ class TestSessionAdapter: SessionAdapter { } func createTask(with URLRequest: URLRequest, progressHandler: @escaping (Int64, Int64, Int64) -> Void, completionHandler: @escaping (Data?, URLResponse?, Swift.Error?) -> Void) -> SessionTask { - let task = TestSessionTask(progressHandler: progressHandler, handler: completionHandler) + let task = TestSessionTask(progressHandler: progressHandler, completionHandler: completionHandler) tasks.append(task) return task diff --git a/Tests/APIKitTests/TestComponents/TestSessionTask.swift b/Tests/APIKitTests/TestComponents/TestSessionTask.swift index 745cf24f..af230b48 100644 --- a/Tests/APIKitTests/TestComponents/TestSessionTask.swift +++ b/Tests/APIKitTests/TestComponents/TestSessionTask.swift @@ -3,12 +3,12 @@ import APIKit class TestSessionTask: SessionTask { - var handler: (Data?, URLResponse?, Error?) -> Void + var completionHandler: (Data?, URLResponse?, Error?) -> Void var progressHandler: (Int64, Int64, Int64) -> Void var cancelled = false - init(progressHandler: @escaping (Int64, Int64, Int64) -> Void, handler: @escaping (Data?, URLResponse?, Error?) -> Void) { - self.handler = handler + init(progressHandler: @escaping (Int64, Int64, Int64) -> Void, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) { + self.completionHandler = completionHandler self.progressHandler = progressHandler } From 20eebe810489a3c3dbb100c72701e69bf0c5f0d0 Mon Sep 17 00:00:00 2001 From: taish Date: Wed, 29 Mar 2017 16:30:21 +0900 Subject: [PATCH 09/11] Fixed `progressHandler` to work properly --- Sources/APIKit/SessionAdapter/URLSessionAdapter.swift | 2 +- .../URLSessionAdapterSubclassTests.swift | 11 +++-------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/Sources/APIKit/SessionAdapter/URLSessionAdapter.swift b/Sources/APIKit/SessionAdapter/URLSessionAdapter.swift index 243d9394..9c6cb0fc 100644 --- a/Sources/APIKit/SessionAdapter/URLSessionAdapter.swift +++ b/Sources/APIKit/SessionAdapter/URLSessionAdapter.swift @@ -68,7 +68,7 @@ open class URLSessionAdapter: NSObject, SessionAdapter, URLSessionDelegate, URLS } private func progressHandler(for task: URLSessionTask) -> ((Int64, Int64, Int64) -> Void)? { - return objc_getAssociatedObject(task, &taskAssociatedObjectCompletionHandlerKey) as? (Int64, Int64, Int64) -> Void + return objc_getAssociatedObject(task, &taskAssociatedObjectProgressHandlerKey) as? (Int64, Int64, Int64) -> Void } // MARK: URLSessionTaskDelegate open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { diff --git a/Tests/APIKitTests/SessionAdapterType/URLSessionAdapterSubclassTests.swift b/Tests/APIKitTests/SessionAdapterType/URLSessionAdapterSubclassTests.swift index 6e03bf67..a75b329e 100644 --- a/Tests/APIKitTests/SessionAdapterType/URLSessionAdapterSubclassTests.swift +++ b/Tests/APIKitTests/SessionAdapterType/URLSessionAdapterSubclassTests.swift @@ -64,14 +64,9 @@ class URLSessionAdapterSubclassTests: XCTestCase { let adapter = SessionAdapter(configuration: configuration) let session = Session(adapter: adapter) - session.send(request, - completionHandler: { result in - if case .failure = result { - XCTFail() - } - - expectation.fulfill() - }) + session.send(request, progressHandler: { _, _, _ in + expectation.fulfill() + }) waitForExpectations(timeout: 10.0, handler: nil) XCTAssertEqual(adapter.functionCallFlags["urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)"], true) From 3593a025340b9ec3518480f97a3f0bc2cd41a58c Mon Sep 17 00:00:00 2001 From: Yosuke Ishikawa Date: Tue, 11 Apr 2017 05:17:16 +0900 Subject: [PATCH 10/11] Rename Request.Parser to Request.DataParser --- Sources/APIKit/Request.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Sources/APIKit/Request.swift b/Sources/APIKit/Request.swift index 88341ec2..6e27bcb8 100644 --- a/Sources/APIKit/Request.swift +++ b/Sources/APIKit/Request.swift @@ -11,7 +11,7 @@ import Result public protocol Request { /// The response type associated with the request type. associatedtype Response - associatedtype Parser: DataParser + associatedtype DataParser: APIKit.DataParser /// The base URL. var baseURL: URL { get } @@ -42,7 +42,7 @@ public protocol Request { var headerFields: [String: String] { get } /// The parser object that states `Content-Type` to accept and parses response body. - var dataParser: Parser { get } + var dataParser: DataParser { get } /// Intercepts `URLRequest` which is created by `Request.buildURLRequest()`. If an error is /// thrown in this method, the result of `Session.send()` turns `.failure(.requestError(error))`. @@ -54,12 +54,12 @@ public protocol Request { /// The default implementation of this method is provided to throw `RequestError.unacceptableStatusCode` /// if the HTTP status code is not in `200..<300`. /// - Throws: `Error` - func intercept(object: Parser.Parsed, urlResponse: HTTPURLResponse) throws -> Parser.Parsed + func intercept(object: DataParser.Parsed, urlResponse: HTTPURLResponse) throws -> DataParser.Parsed /// Build `Response` instance from raw response object. This method is called after /// `intercept(object:urlResponse:)` if it does not throw any error. /// - Throws: `Error` - func response(from object: Parser.Parsed, urlResponse: HTTPURLResponse) throws -> Response + func response(from object: DataParser.Parsed, urlResponse: HTTPURLResponse) throws -> Response } public extension Request { @@ -92,7 +92,7 @@ public extension Request { return urlRequest } - public func intercept(object: Parser.Parsed, urlResponse: HTTPURLResponse) throws -> Parser.Parsed { + public func intercept(object: DataParser.Parsed, urlResponse: HTTPURLResponse) throws -> DataParser.Parsed { guard 200..<300 ~= urlResponse.statusCode else { throw ResponseError.unacceptableStatusCode(urlResponse.statusCode) } From 1afc176534cac89a8fb51158deb793290e506031 Mon Sep 17 00:00:00 2001 From: davdroman Date: Sat, 14 Mar 2020 16:43:01 +0000 Subject: [PATCH 11/11] Remove stale document --- Documentation/APIKit4MigrationGuide.md | 1 - 1 file changed, 1 deletion(-) delete mode 100644 Documentation/APIKit4MigrationGuide.md diff --git a/Documentation/APIKit4MigrationGuide.md b/Documentation/APIKit4MigrationGuide.md deleted file mode 100644 index 1572cbf4..00000000 --- a/Documentation/APIKit4MigrationGuide.md +++ /dev/null @@ -1 +0,0 @@ -# APIKit 4 Migration Guide