From a2215fa324c11d91c4736c9af504a8ea07a15561 Mon Sep 17 00:00:00 2001 From: Craig Tweedy Date: Thu, 10 May 2018 15:30:03 +0100 Subject: [PATCH 1/4] Added ability to call next / prev / first / last on a paginated response which allows users to more easily paginate through responses rather than constructing new requests themselves --- Sources/SDK/Requests/MoltinRequest.swift | 27 ++++++++++++ Sources/SDK/Utils/Error.swift | 4 ++ Sources/SDK/Utils/Pagination.swift | 44 +++++++++++++++++++ .../xcschemes/xcschememanagement.plist | 8 ++-- 4 files changed, 79 insertions(+), 4 deletions(-) diff --git a/Sources/SDK/Requests/MoltinRequest.swift b/Sources/SDK/Requests/MoltinRequest.swift index 12b45cd..d31468d 100644 --- a/Sources/SDK/Requests/MoltinRequest.swift +++ b/Sources/SDK/Requests/MoltinRequest.swift @@ -242,6 +242,33 @@ public class MoltinRequest { return self } + /** + Construct a pagination request, and return an instance of type `T`, which must be `Codable` + + - Author: + Craig Tweedy + + - parameters: + - withURL: The URL constructed from a path to call + - completionHandler: The handler to be called on success or failure + - returns: + A instance of `MoltinRequest` which encapsulates the request. + */ + @discardableResult internal func paginationRequest(withURL url: URL, completionHandler: @escaping CollectionRequestHandler) -> Self { + + let urlRequest = URLRequest(url: url) + + self.send(withURLRequest: urlRequest) { (data, response, error) in + if error != nil { + completionHandler(.failure(error: MoltinError.responseError(underlyingError: error))) + return + } + self.parser.collectionHandler(withData: data, withResponse: response, completionHandler: completionHandler) + } + + return self + } + private func send(withURLRequest urlRequest: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) { self.auth.authenticate { [weak self] (result) in switch result { diff --git a/Sources/SDK/Utils/Error.swift b/Sources/SDK/Utils/Error.swift index cef13be..a1aad69 100644 --- a/Sources/SDK/Utils/Error.swift +++ b/Sources/SDK/Utils/Error.swift @@ -25,6 +25,8 @@ public enum MoltinError: Error { case unacceptableRequest /// Thrown if no data was returned from the API case noData + /// Thrown if no next or previous page for this call was available + case noPageAvailable } extension MoltinError: LocalizedError { @@ -46,6 +48,8 @@ extension MoltinError: LocalizedError { return "Could not compose request" case .couldNotSetData: return "Could not serialize data" + case .noPageAvailable: + return "No page available" } } } diff --git a/Sources/SDK/Utils/Pagination.swift b/Sources/SDK/Utils/Pagination.swift index 75b726e..4207256 100644 --- a/Sources/SDK/Utils/Pagination.swift +++ b/Sources/SDK/Utils/Pagination.swift @@ -23,6 +23,50 @@ open class PaginatedResponse: Codable { init() { fatalError("Swift 4.1 broke Codable synthesized inits") } + + @discardableResult public func next(withConfig config: MoltinConfig, withCompletion completionHandler: @escaping (Result>) -> Void) -> MoltinRequest? { + guard let page = self.links?["next"] as? String, + let url = URL(string: page) else { + completionHandler(.failure(error: MoltinError.noPageAvailable)) + return nil + } + + let request = MoltinRequest(withConfiguration: config) + return request.paginationRequest(withURL: url, completionHandler: completionHandler) + } + + @discardableResult public func previous(withConfig config: MoltinConfig, withCompletion completionHandler: @escaping (Result>) -> Void) -> MoltinRequest? { + guard let page = self.links?["prev"] as? String, + let url = URL(string: page) else { + completionHandler(.failure(error: MoltinError.noPageAvailable)) + return nil + } + + let request = MoltinRequest(withConfiguration: config) + return request.paginationRequest(withURL: url, completionHandler: completionHandler) + } + + @discardableResult public func first(withConfig config: MoltinConfig, withCompletion completionHandler: @escaping (Result>) -> Void) -> MoltinRequest? { + guard let page = self.links?["first"] as? String, + let url = URL(string: page) else { + completionHandler(.failure(error: MoltinError.noPageAvailable)) + return nil + } + + let request = MoltinRequest(withConfiguration: config) + return request.paginationRequest(withURL: url, completionHandler: completionHandler) + } + + @discardableResult public func last(withConfig config: MoltinConfig, withCompletion completionHandler: @escaping (Result>) -> Void) -> MoltinRequest? { + guard let page = self.links?["last"] as? String, + let url = URL(string: page) else { + completionHandler(.failure(error: MoltinError.noPageAvailable)) + return nil + } + + let request = MoltinRequest(withConfiguration: config) + return request.paginationRequest(withURL: url, completionHandler: completionHandler) + } } /// `PaginationMeta` gives information about the pagination details to the user, such as result information and page information diff --git a/moltin.xcodeproj/xcuserdata/craigtweedy.xcuserdatad/xcschemes/xcschememanagement.plist b/moltin.xcodeproj/xcuserdata/craigtweedy.xcuserdatad/xcschemes/xcschememanagement.plist index a209196..93eccfd 100644 --- a/moltin.xcodeproj/xcuserdata/craigtweedy.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/moltin.xcodeproj/xcuserdata/craigtweedy.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,17 +7,17 @@ moltin WatchKit Example.xcscheme orderHint - 6 + 5 moltin iOS Example.xcscheme orderHint - 5 + 4 moltin iOS Tests.xcscheme orderHint - 4 + 3 moltin iOS.xcscheme_^#shared#^_ @@ -27,7 +27,7 @@ moltin tvOS Example.xcscheme orderHint - 7 + 6 moltin tvOS Tests.xcscheme From 9161af303b58877b1bde7c8916cf23ef01ab3622 Mon Sep 17 00:00:00 2001 From: Craig Tweedy Date: Wed, 23 Jan 2019 19:25:22 +0000 Subject: [PATCH 2/4] Removed init --- Sources/SDK/Utils/Pagination.swift | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Sources/SDK/Utils/Pagination.swift b/Sources/SDK/Utils/Pagination.swift index 311b493..9bc1bf4 100644 --- a/Sources/SDK/Utils/Pagination.swift +++ b/Sources/SDK/Utils/Pagination.swift @@ -19,11 +19,6 @@ open class PaginatedResponse: Codable { /// The meta information for this response public var meta: PaginationMeta? - @available(*, deprecated, message: "Do not use.") - init() { - fatalError("Swift 4.1 broke Codable synthesized inits") - } - @discardableResult public func next(withConfig config: MoltinConfig, withCompletion completionHandler: @escaping (Result>) -> Void) -> MoltinRequest? { guard let page = self.links?["next"] as? String, let url = URL(string: page) else { From 15608e49fc2317a5d8a8fe4289950c43d35d18ac Mon Sep 17 00:00:00 2001 From: Craig Tweedy Date: Wed, 23 Jan 2019 19:32:50 +0000 Subject: [PATCH 3/4] Added usage in playground --- moltin.playground/Contents.swift | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/moltin.playground/Contents.swift b/moltin.playground/Contents.swift index 095d438..85747e0 100644 --- a/moltin.playground/Contents.swift +++ b/moltin.playground/Contents.swift @@ -8,15 +8,6 @@ PlaygroundPage.current.needsIndefiniteExecution = true let moltin = Moltin(withClientID: "j6hSilXRQfxKohTndUuVrErLcSJWP15P347L6Im0M4") -moltin.product.all { (result: Result>) in - switch result { - case .success(let products): - print(products) - case .failure(let error): - print(error) - } -} - moltin.product.all { (result) in guard case .success(let products) = result else { // something went wrong @@ -25,4 +16,15 @@ moltin.product.all { (result) in } print(products) + + products.next(withConfig: moltin.config) { (paginatedResult) in + guard case .success(let nextPage) = paginatedResult else { + // something went wrong + if case .failure(let error) = paginatedResult { print(error) } + return + } + + print(nextPage) + + } } From 1e8a8f119a18746d75cc888435d3b20c7b19bdb8 Mon Sep 17 00:00:00 2001 From: Craig Tweedy Date: Wed, 23 Jan 2019 19:35:09 +0000 Subject: [PATCH 4/4] Added doc blocks for the pagination utilties --- Sources/SDK/Utils/Pagination.swift | 48 ++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/Sources/SDK/Utils/Pagination.swift b/Sources/SDK/Utils/Pagination.swift index 9bc1bf4..73a10be 100644 --- a/Sources/SDK/Utils/Pagination.swift +++ b/Sources/SDK/Utils/Pagination.swift @@ -19,6 +19,18 @@ open class PaginatedResponse: Codable { /// The meta information for this response public var meta: PaginationMeta? + /** + Get the next page for this response + + - Author: + Craig Tweedy + + - parameters: + - withConfig: The moltin config to use + - withCompletion: The handler to be called on success or failure + - returns: + A instance of `MoltinRequest` which encapsulates the request. + */ @discardableResult public func next(withConfig config: MoltinConfig, withCompletion completionHandler: @escaping (Result>) -> Void) -> MoltinRequest? { guard let page = self.links?["next"] as? String, let url = URL(string: page) else { @@ -30,6 +42,18 @@ open class PaginatedResponse: Codable { return request.paginationRequest(withURL: url, completionHandler: completionHandler) } + /** + Get the previous page for this response + + - Author: + Craig Tweedy + + - parameters: + - withConfig: The moltin config to use + - withCompletion: The handler to be called on success or failure + - returns: + A instance of `MoltinRequest` which encapsulates the request. + */ @discardableResult public func previous(withConfig config: MoltinConfig, withCompletion completionHandler: @escaping (Result>) -> Void) -> MoltinRequest? { guard let page = self.links?["prev"] as? String, let url = URL(string: page) else { @@ -41,6 +65,18 @@ open class PaginatedResponse: Codable { return request.paginationRequest(withURL: url, completionHandler: completionHandler) } + /** + Get the first page for this response + + - Author: + Craig Tweedy + + - parameters: + - withConfig: The moltin config to use + - withCompletion: The handler to be called on success or failure + - returns: + A instance of `MoltinRequest` which encapsulates the request. + */ @discardableResult public func first(withConfig config: MoltinConfig, withCompletion completionHandler: @escaping (Result>) -> Void) -> MoltinRequest? { guard let page = self.links?["first"] as? String, let url = URL(string: page) else { @@ -52,6 +88,18 @@ open class PaginatedResponse: Codable { return request.paginationRequest(withURL: url, completionHandler: completionHandler) } + /** + Get the last page for this response + + - Author: + Craig Tweedy + + - parameters: + - withConfig: The moltin config to use + - withCompletion: The handler to be called on success or failure + - returns: + A instance of `MoltinRequest` which encapsulates the request. + */ @discardableResult public func last(withConfig config: MoltinConfig, withCompletion completionHandler: @escaping (Result>) -> Void) -> MoltinRequest? { guard let page = self.links?["last"] as? String, let url = URL(string: page) else {