From 5040fbc46a9f7706755811a2fa47983a4fce5d26 Mon Sep 17 00:00:00 2001 From: Hesham Salman Date: Mon, 15 Jul 2024 17:38:19 -0400 Subject: [PATCH] Support Partial Success --- .../AsyncGraphQLQueryPagerTests.swift | 37 ++++----- .../BidirectionalPaginationTests.swift | 22 ++--- .../ForwardPaginationTests.swift | 10 +-- .../GraphQLQueryPagerTests.swift | 40 ++++----- Tests/ApolloPaginationTests/OffsetTests.swift | 7 +- .../PagerCoordinator+Erase.swift | 36 ++------ .../ReversePaginationTests.swift | 4 +- .../SubscribeTests.swift | 4 +- .../AsyncGraphQLQueryPager.swift | 70 ++-------------- .../AsyncGraphQLQueryPagerCoordinator.swift | 41 +++++---- .../GraphQLQueryPager+Convenience.swift | 41 ++------- .../ApolloPagination/GraphQLQueryPager.swift | 83 ++----------------- .../GraphQLQueryPagerOutput.swift | 10 ++- 13 files changed, 120 insertions(+), 285 deletions(-) diff --git a/Tests/ApolloPaginationTests/AsyncGraphQLQueryPagerTests.swift b/Tests/ApolloPaginationTests/AsyncGraphQLQueryPagerTests.swift index 95364f223..3353165e8 100644 --- a/Tests/ApolloPaginationTests/AsyncGraphQLQueryPagerTests.swift +++ b/Tests/ApolloPaginationTests/AsyncGraphQLQueryPagerTests.swift @@ -122,8 +122,7 @@ final class AsyncGraphQLQueryPagerTests: XCTestCase { let subscription = pager .compactMap { output -> [ViewModel]? in guard case .success((let output, _)) = output else { return nil } - let inOrderData = output.previousPages + [output.initialPage] + output.nextPages - let models = inOrderData.flatMap { data in + let models = output.allPages.flatMap { data in data.hero.friendsConnection.friends.map { friend in ViewModel(name: friend.name) } } return models @@ -169,9 +168,8 @@ final class AsyncGraphQLQueryPagerTests: XCTestCase { return nil } } , - transform: { previous, first, next in - let inOrderData = previous + [first] + next - return inOrderData.flatMap { data in + transform: { output in + output.allPages.flatMap { data in data.hero.friendsConnection.friends.map { friend in friend.name } } } @@ -231,9 +229,8 @@ final class AsyncGraphQLQueryPagerTests: XCTestCase { return nil } }, - transform: { previous, first, next in - let inOrderData = previous + [first] + next - return inOrderData.flatMap { data in + transform: { output in + output.allPages.flatMap { data in data.hero.friendsConnection.friends.map { friend in friend.name } } } @@ -274,8 +271,10 @@ final class AsyncGraphQLQueryPagerTests: XCTestCase { } let anyPager = createPager().eraseToAnyPager { data in - data.hero.friendsConnection.friends.map { - ViewModel(name: $0.name) + data.allPages.flatMap { data in + data.hero.friendsConnection.friends.map { + ViewModel(name: $0.name) + } } } @@ -306,11 +305,11 @@ final class AsyncGraphQLQueryPagerTests: XCTestCase { } func test_passesBackSeparateData() async throws { - let anyPager = createPager().eraseToAnyPager { _, initial, next in - if let latestPage = next.last { + let anyPager = createPager().eraseToAnyPager { output in + if let latestPage = output.nextPages.last { return latestPage.hero.friendsConnection.friends.last?.name } - return initial.hero.friendsConnection.friends.last?.name + return output.initialPage?.hero.friendsConnection.friends.last?.name } let initialExpectation = expectation(description: "Initial") @@ -355,16 +354,14 @@ final class AsyncGraphQLQueryPagerTests: XCTestCase { } func test_equatable() async { - let pagerA = AsyncGraphQLQueryPager(pager: createPager(), transform: { previous, initial, next in - let allPages = previous + [initial] + next - return allPages.flatMap { data in + let pagerA = AsyncGraphQLQueryPager(pager: createPager(), transform: { output in + output.allPages.flatMap { data in data.hero.friendsConnection.friends.map { $0.name } } }) - let pagerB = AsyncGraphQLQueryPager(pager: createPager(), transform: { previous, initial, next in - let allPages = previous + [initial] + next - return allPages.flatMap { data in + let pagerB = AsyncGraphQLQueryPager(pager: createPager(), transform: { output in + return output.allPages.flatMap { data in data.hero.friendsConnection.friends.map { $0.name } } }) @@ -404,7 +401,7 @@ final class AsyncGraphQLQueryPagerTests: XCTestCase { subscription.cancel() } - func test_errors_noSuccess() async throws { + func test_errors_noData() async throws { let pager = AsyncGraphQLQueryPager(pager: createPager()) var expectedResults: [Result<(PaginationOutput, UpdateSource), any Error>] = [] let serverExpectation = Mocks.Hero.FriendsQuery.expectationForFirstPageErrorsOnly(server: server) diff --git a/Tests/ApolloPaginationTests/BidirectionalPaginationTests.swift b/Tests/ApolloPaginationTests/BidirectionalPaginationTests.swift index c62f29201..95b2e7634 100644 --- a/Tests/ApolloPaginationTests/BidirectionalPaginationTests.swift +++ b/Tests/ApolloPaginationTests/BidirectionalPaginationTests.swift @@ -104,8 +104,8 @@ final class BidirectionalPaginationTests: XCTestCase, CacheDependentTesting { results.append(result) XCTAssertSuccessResult(result) { (output, source) in XCTAssertTrue(output.nextPages.isEmpty) - XCTAssertEqual(output.initialPage.hero.friendsConnection.friends.count, 1) - XCTAssertEqual(output.initialPage.hero.friendsConnection.totalCount, 3) + XCTAssertEqual(output.initialPage?.hero.friendsConnection.friends.count, 1) + XCTAssertEqual(output.initialPage?.hero.friendsConnection.totalCount, 3) XCTAssertEqual(source, .fetch) } @@ -192,10 +192,12 @@ final class BidirectionalPaginationTests: XCTestCase, CacheDependentTesting { let (result, _) = try await XCTUnwrapping(try await pager.currentValue?.get()) XCTAssertFalse(result.previousPages.isEmpty) - XCTAssertEqual(result.initialPage.hero.friendsConnection.friends.count, 1) + XCTAssertEqual(result.initialPage?.hero.friendsConnection.friends.count, 1) XCTAssertFalse(result.nextPages.isEmpty) - let friends = (result.previousPages.first?.hero.friendsConnection.friends ?? []) + result.initialPage.hero.friendsConnection.friends + (result.nextPages.first?.hero.friendsConnection.friends ?? []) + let friends = result.previousPages.flatMap(\.hero.friendsConnection.friends) + + (result.initialPage?.hero.friendsConnection.friends ?? []) + + result.nextPages.flatMap(\.hero.friendsConnection.friends) XCTAssertEqual(Set(friends).count, 3) } @@ -218,8 +220,8 @@ final class BidirectionalPaginationTests: XCTestCase, CacheDependentTesting { results.append(result) XCTAssertSuccessResult(result) { (output, source) in XCTAssertTrue(output.nextPages.isEmpty) - XCTAssertEqual(output.initialPage.hero.friendsConnection.friends.count, 1) - XCTAssertEqual(output.initialPage.hero.friendsConnection.totalCount, 3) + XCTAssertEqual(output.initialPage?.hero.friendsConnection.friends.count, 1) + XCTAssertEqual(output.initialPage?.hero.friendsConnection.totalCount, 3) XCTAssertEqual(source, .fetch) } @@ -298,12 +300,12 @@ final class BidirectionalPaginationTests: XCTestCase, CacheDependentTesting { let result = try await XCTUnwrapping(try await pager.pager.currentValue?.get().0) XCTAssertFalse(result.previousPages.isEmpty) - XCTAssertEqual(result.initialPage.hero.friendsConnection.friends.count, 1) + XCTAssertEqual(result.initialPage?.hero.friendsConnection.friends.count, 1) XCTAssertFalse(result.nextPages.isEmpty) - let friends = (result.previousPages.first?.hero.friendsConnection.friends ?? []) - + result.initialPage.hero.friendsConnection.friends - + (result.nextPages.first?.hero.friendsConnection.friends ?? []) + let friends = result.previousPages.flatMap(\.hero.friendsConnection.friends) + + (result.initialPage?.hero.friendsConnection.friends ?? []) + + result.nextPages.flatMap(\.hero.friendsConnection.friends) XCTAssertEqual(Set(friends).count, 3) } diff --git a/Tests/ApolloPaginationTests/ForwardPaginationTests.swift b/Tests/ApolloPaginationTests/ForwardPaginationTests.swift index b10144323..5ea245962 100644 --- a/Tests/ApolloPaginationTests/ForwardPaginationTests.swift +++ b/Tests/ApolloPaginationTests/ForwardPaginationTests.swift @@ -59,8 +59,8 @@ final class ForwardPaginationTests: XCTestCase, CacheDependentTesting { results.append(result) XCTAssertSuccessResult(result) { (output, source) in XCTAssertTrue(output.nextPages.isEmpty) - XCTAssertEqual(output.initialPage.hero.friendsConnection.friends.count, 2) - XCTAssertEqual(output.initialPage.hero.friendsConnection.totalCount, 3) + XCTAssertEqual(output.initialPage?.hero.friendsConnection.friends.count, 2) + XCTAssertEqual(output.initialPage?.hero.friendsConnection.totalCount, 3) XCTAssertEqual(source, .fetch) } @@ -185,8 +185,8 @@ final class ForwardPaginationTests: XCTestCase, CacheDependentTesting { let result = try await XCTUnwrapping(await pager.currentValue) XCTAssertSuccessResult(result) { (output, source) in XCTAssertTrue(output.nextPages.isEmpty) - XCTAssertEqual(output.initialPage.hero.friendsConnection.friends.count, 2) - XCTAssertEqual(output.initialPage.hero.friendsConnection.totalCount, 3) + XCTAssertEqual(output.initialPage?.hero.friendsConnection.friends.count, 2) + XCTAssertEqual(output.initialPage?.hero.friendsConnection.totalCount, 3) XCTAssertEqual(source, .fetch) } @@ -228,7 +228,7 @@ final class ForwardPaginationTests: XCTestCase, CacheDependentTesting { await fulfillment(of: [transactionExpectation, mutationExpectation]) let finalResult = try await XCTUnwrapping(await pager.currentValue) XCTAssertSuccessResult(finalResult) { (output, _) in - XCTAssertEqual(output.initialPage.hero.name, "C3PO") + XCTAssertEqual(output.initialPage?.hero.name, "C3PO") XCTAssertEqual(output.nextPages.count, 1) XCTAssertEqual(output.nextPages.first?.hero.name, "C3PO") } diff --git a/Tests/ApolloPaginationTests/GraphQLQueryPagerTests.swift b/Tests/ApolloPaginationTests/GraphQLQueryPagerTests.swift index 2b3bdfd2e..f7407193b 100644 --- a/Tests/ApolloPaginationTests/GraphQLQueryPagerTests.swift +++ b/Tests/ApolloPaginationTests/GraphQLQueryPagerTests.swift @@ -131,8 +131,10 @@ final class GraphQLQueryPagerTests: XCTestCase { } let anyPager = createPager().eraseToAnyPager { data in - data.hero.friendsConnection.friends.map { - ViewModel(name: $0.name) + data.allPages.flatMap { data in + data.hero.friendsConnection.friends.map { + ViewModel(name: $0.name) + } } } @@ -168,8 +170,10 @@ final class GraphQLQueryPagerTests: XCTestCase { } let anyPager = createPager().eraseToAnyPager { data in - data.hero.friendsConnection.friends.map { - ViewModel(name: $0.name) + data.allPages.flatMap { data in + data.hero.friendsConnection.friends.map { + ViewModel(name: $0.name) + } } } @@ -221,11 +225,11 @@ final class GraphQLQueryPagerTests: XCTestCase { } func test_passesBackSeparateData() throws { - let anyPager = createPager().eraseToAnyPager { _, initial, next in - if let latestPage = next.last { + let anyPager = createPager().eraseToAnyPager { output in + if let latestPage = output.nextPages.last { return latestPage.hero.friendsConnection.friends.last?.name } - return initial.hero.friendsConnection.friends.last?.name + return output.initialPage?.hero.friendsConnection.friends.last?.name } let initialExpectation = expectation(description: "Initial") @@ -260,11 +264,11 @@ final class GraphQLQueryPagerTests: XCTestCase { } func test_reversePager_loadPrevious() throws { - let anyPager = createReversePager().eraseToAnyPager { previous, initial, _ in - if let latestPage = previous.last { + let anyPager = createReversePager().eraseToAnyPager { output in + if let latestPage = output.previousPages.last { return latestPage.hero.friendsConnection.friends.first?.name } - return initial.hero.friendsConnection.friends.first?.name + return output.initialPage?.hero.friendsConnection.friends.first?.name } let initialExpectation = expectation(description: "Initial") @@ -303,11 +307,11 @@ final class GraphQLQueryPagerTests: XCTestCase { @available(iOS 16.0, macOS 13.0, *) func test_pager_reset_calls_callback() async throws { server.customDelay = .milliseconds(1) - let pager = createPager().eraseToAnyPager { _, initial, next in - if let latestPage = next.last { + let pager = createPager().eraseToAnyPager { output in + if let latestPage = output.nextPages.last { return latestPage.hero.friendsConnection.friends.last?.name } - return initial.hero.friendsConnection.friends.last?.name + return output.initialPage?.hero.friendsConnection.friends.last?.name } let serverExpectation = Mocks.Hero.FriendsQuery.expectationForFirstPage(server: server) @@ -325,16 +329,14 @@ final class GraphQLQueryPagerTests: XCTestCase { } func test_equatable() { - let pagerA = GraphQLQueryPager(pager: createPager(), transform: { previous, initial, next in - let allPages = previous + [initial] + next - return allPages.flatMap { data in + let pagerA = GraphQLQueryPager(pager: createPager(), transform: { output in + output.allPages.flatMap { data in data.hero.friendsConnection.friends.map { $0.name } } }) - let pagerB = GraphQLQueryPager(pager: createPager(), transform: { previous, initial, next in - let allPages = previous + [initial] + next - return allPages.flatMap { data in + let pagerB = GraphQLQueryPager(pager: createPager(), transform: { output in + output.allPages.flatMap { data in data.hero.friendsConnection.friends.map { $0.name } } }) diff --git a/Tests/ApolloPaginationTests/OffsetTests.swift b/Tests/ApolloPaginationTests/OffsetTests.swift index 81a442c38..731142889 100644 --- a/Tests/ApolloPaginationTests/OffsetTests.swift +++ b/Tests/ApolloPaginationTests/OffsetTests.swift @@ -36,8 +36,7 @@ final class OffsetTests: XCTestCase { case .initial(let data, let output), .paginated(let data, let output): var totalOffset: Int = 0 if let output { - let pages = (output.previousPages + [output.initialPage] + output.nextPages) - pages.forEach { page in + output.allPages.forEach { page in totalOffset += page.hero.friends.count } } @@ -95,9 +94,7 @@ final class OffsetTests: XCTestCase { let cancellable = pager.map { value in switch value { case .success((let output, _)): - let pages = output.previousPages + [output.initialPage] + output.nextPages - - let friends = pages.flatMap { data in + let friends = output.allPages.flatMap { data in data.hero.friends.map { friend in ViewModel(name: friend.name) } diff --git a/Tests/ApolloPaginationTests/PagerCoordinator+Erase.swift b/Tests/ApolloPaginationTests/PagerCoordinator+Erase.swift index 38eccd0f6..b9f7631f5 100644 --- a/Tests/ApolloPaginationTests/PagerCoordinator+Erase.swift +++ b/Tests/ApolloPaginationTests/PagerCoordinator+Erase.swift @@ -2,36 +2,24 @@ extension GraphQLQueryPagerCoordinator { func eraseToAnyPager( - transform: @escaping ([PaginatedQuery.Data], InitialQuery.Data, [PaginatedQuery.Data]) throws -> T + transform: @escaping (PaginationOutput) throws -> T ) -> GraphQLQueryPager { GraphQLQueryPager(pager: self, transform: transform) } func eraseToAnyPager( - initialTransform: @escaping (InitialQuery.Data) throws -> S, - nextPageTransform: @escaping (PaginatedQuery.Data) throws -> S - ) -> GraphQLQueryPager where T == S.Element { - GraphQLQueryPager( - pager: self, - initialTransform: initialTransform, - pageTransform: nextPageTransform - ) - } - - func eraseToAnyPager( - transform: @escaping (InitialQuery.Data) throws -> S + transform: @escaping (PaginationOutput) throws -> S ) -> GraphQLQueryPager where InitialQuery == PaginatedQuery, T == S.Element { GraphQLQueryPager( pager: self, - initialTransform: transform, - pageTransform: transform + transform: transform ) } } extension AsyncGraphQLQueryPagerCoordinator { nonisolated func eraseToAnyPager( - transform: @escaping ([PaginatedQuery.Data], InitialQuery.Data, [PaginatedQuery.Data]) throws -> T + transform: @escaping (PaginationOutput) throws -> T ) -> AsyncGraphQLQueryPager { AsyncGraphQLQueryPager( pager: self, @@ -40,23 +28,11 @@ extension AsyncGraphQLQueryPagerCoordinator { } nonisolated func eraseToAnyPager( - initialTransform: @escaping (InitialQuery.Data) throws -> S, - pageTransform: @escaping (PaginatedQuery.Data) throws -> S - ) -> AsyncGraphQLQueryPager where T == S.Element { - AsyncGraphQLQueryPager( - pager: self, - initialTransform: initialTransform, - pageTransform: pageTransform - ) - } - - nonisolated func eraseToAnyPager( - transform: @escaping (InitialQuery.Data) throws -> S + transform: @escaping (PaginationOutput) throws -> S ) -> AsyncGraphQLQueryPager where InitialQuery == PaginatedQuery, T == S.Element { AsyncGraphQLQueryPager( pager: self, - initialTransform: transform, - pageTransform: transform + transform: transform ) } } diff --git a/Tests/ApolloPaginationTests/ReversePaginationTests.swift b/Tests/ApolloPaginationTests/ReversePaginationTests.swift index 83f76d62d..33cd7e001 100644 --- a/Tests/ApolloPaginationTests/ReversePaginationTests.swift +++ b/Tests/ApolloPaginationTests/ReversePaginationTests.swift @@ -59,8 +59,8 @@ final class ReversePaginationTests: XCTestCase, CacheDependentTesting { results.append(result) XCTAssertSuccessResult(result) { (output, source) in XCTAssertTrue(output.nextPages.isEmpty) - XCTAssertEqual(output.initialPage.hero.friendsConnection.friends.count, 2) - XCTAssertEqual(output.initialPage.hero.friendsConnection.totalCount, 3) + XCTAssertEqual(output.initialPage?.hero.friendsConnection.friends.count, 2) + XCTAssertEqual(output.initialPage?.hero.friendsConnection.totalCount, 3) XCTAssertEqual(source, .fetch) } diff --git a/Tests/ApolloPaginationTests/SubscribeTests.swift b/Tests/ApolloPaginationTests/SubscribeTests.swift index 552be1376..518b6797b 100644 --- a/Tests/ApolloPaginationTests/SubscribeTests.swift +++ b/Tests/ApolloPaginationTests/SubscribeTests.swift @@ -64,8 +64,8 @@ final class SubscribeTest: XCTestCase, CacheDependentTesting { let result = try XCTUnwrap(results.first) XCTAssertSuccessResult(result) { (output, source) in XCTAssertTrue(output.nextPages.isEmpty) - XCTAssertEqual(output.initialPage.hero.friendsConnection.friends.count, 2) - XCTAssertEqual(output.initialPage.hero.friendsConnection.totalCount, 3) + XCTAssertEqual(output.initialPage?.hero.friendsConnection.friends.count, 2) + XCTAssertEqual(output.initialPage?.hero.friendsConnection.totalCount, 3) XCTAssertEqual(source, .fetch) XCTAssertEqual(results.count, otherResults.count) } diff --git a/apollo-ios-pagination/Sources/ApolloPagination/AsyncGraphQLQueryPager.swift b/apollo-ios-pagination/Sources/ApolloPagination/AsyncGraphQLQueryPager.swift index eeea21506..209d874d9 100644 --- a/apollo-ios-pagination/Sources/ApolloPagination/AsyncGraphQLQueryPager.swift +++ b/apollo-ios-pagination/Sources/ApolloPagination/AsyncGraphQLQueryPager.swift @@ -17,7 +17,7 @@ public class AsyncGraphQLQueryPager: Publisher { init, InitialQuery, PaginatedQuery>( pager: Pager, - transform: @escaping ([PaginatedQuery.Data], InitialQuery.Data, [PaginatedQuery.Data]) throws -> Model + transform: @escaping (PaginationOutput) throws -> Model ) { self.pager = pager Task { @@ -28,7 +28,7 @@ public class AsyncGraphQLQueryPager: Publisher { switch result { case let .success((output, source)): do { - let transformedModels = try transform(output.previousPages, output.initialPage, output.nextPages) + let transformedModels = try transform(output) returnValue = .success((transformedModels, source)) } catch { returnValue = .failure(error) @@ -77,62 +77,15 @@ public class AsyncGraphQLQueryPager: Publisher { ) where Model: RangeReplaceableCollection, Model.Element == Element { self.init( pager: pager, - transform: { previousData, initialData, nextData in - let previous = try previousData.flatMap { try pageTransform($0) } - let initial = try initialTransform(initialData) - let next = try nextData.flatMap { try pageTransform($0) } + transform: { output in + let previous = try output.previousPages.flatMap { try pageTransform($0) } + let initial = try output.initialPage.flatMap(initialTransform) ?? .init() + let next = try output.nextPages.flatMap { try pageTransform($0) } return previous + initial + next } ) } - public convenience init< - P: PaginationInfo, - InitialQuery: GraphQLQuery, - PaginatedQuery: GraphQLQuery, - Element - >( - client: any ApolloClientProtocol, - initialQuery: InitialQuery, - watcherDispatchQueue: DispatchQueue = .main, - extractPageInfo: @escaping (PageExtractionData) -> P, - pageResolver: ((P, PaginationDirection) -> PaginatedQuery?)?, - initialTransform: @escaping (InitialQuery.Data) throws -> Model, - pageTransform: @escaping (PaginatedQuery.Data) throws -> Model - ) where Model: RangeReplaceableCollection, Model.Element == Element { - let pager = AsyncGraphQLQueryPagerCoordinator( - client: client, - initialQuery: initialQuery, - watcherDispatchQueue: watcherDispatchQueue, - extractPageInfo: { data in - switch data { - case .initial(let data, let output): - return extractPageInfo(.initial(data, convertOutput(result: output))) - case .paginated(let data, let output): - return extractPageInfo(.paginated(data, convertOutput(result: output))) - } - }, - pageResolver: pageResolver - ) - self.init( - pager: pager, - initialTransform: initialTransform, - pageTransform: pageTransform - ) - - func convertOutput(result: PaginationOutput?) -> Model? { - guard let result else { return nil } - - let transform: ([PaginatedQuery.Data], InitialQuery.Data, [PaginatedQuery.Data]) throws -> Model = { previousData, initialData, nextData in - let previous = try previousData.flatMap { try pageTransform($0) } - let initial = try initialTransform(initialData) - let next = try nextData.flatMap { try pageTransform($0) } - return previous + initial + next - } - return try? transform(result.previousPages, result.initialPage, result.nextPages) - } - } - public convenience init< P: PaginationInfo, InitialQuery: GraphQLQuery, @@ -166,7 +119,7 @@ public class AsyncGraphQLQueryPager: Publisher { watcherDispatchQueue: DispatchQueue = .main, extractPageInfo: @escaping (PageExtractionData) -> P, pageResolver: ((P, PaginationDirection) -> PaginatedQuery?)?, - transform: @escaping ([PaginatedQuery.Data], InitialQuery.Data, [PaginatedQuery.Data]) throws -> Model + transform: @escaping (PaginationOutput) throws -> Model ) { let pager = AsyncGraphQLQueryPagerCoordinator( client: client, @@ -175,9 +128,9 @@ public class AsyncGraphQLQueryPager: Publisher { extractPageInfo: { data in switch data { case .initial(let data, let output): - return extractPageInfo(.initial(data, convertOutput(result: output))) + return extractPageInfo(.initial(data, try? output.flatMap(transform))) case .paginated(let data, let output): - return extractPageInfo(.paginated(data, convertOutput(result: output))) + return extractPageInfo(.paginated(data, try? output.flatMap(transform))) } }, pageResolver: pageResolver @@ -186,11 +139,6 @@ public class AsyncGraphQLQueryPager: Publisher { pager: pager, transform: transform ) - - func convertOutput(result: PaginationOutput?) -> Model? { - guard let result else { return nil } - return try? transform(result.previousPages, result.initialPage, result.nextPages) - } } diff --git a/apollo-ios-pagination/Sources/ApolloPagination/AsyncGraphQLQueryPagerCoordinator.swift b/apollo-ios-pagination/Sources/ApolloPagination/AsyncGraphQLQueryPagerCoordinator.swift index 522ae8dfd..bdc340caa 100644 --- a/apollo-ios-pagination/Sources/ApolloPagination/AsyncGraphQLQueryPagerCoordinator.swift +++ b/apollo-ios-pagination/Sources/ApolloPagination/AsyncGraphQLQueryPagerCoordinator.swift @@ -307,38 +307,47 @@ actor AsyncGraphQLQueryPagerCoordinator, UpdateSource), any Error>? - var output: ([PaginatedQuery.Data], InitialQuery.Data, [PaginatedQuery.Data])? + var output: PaginationOutput? switch fetchType { case .initial: - guard let pageData = data.data as? InitialQuery.Data else { return } - initialPageResult = pageData + initialPageResult = data.data as? InitialQuery.Data if let latest { - output = (latest.previous, pageData, latest.next) + output = .init( + previousPages: latest.previous, + initialPage: latest.initial, + nextPages: latest.next, + errors: data.errors ?? [] + ) + } else { + output = .init( + previousPages: [], + initialPage: nil, + nextPages: [], + errors: data.errors ?? [] + ) } case .paginated(let direction, let query): - guard let pageData = data.data as? PaginatedQuery.Data else { return } - let variables = Set(query.__variables?.underlyingJson ?? []) switch direction { case .next: - nextPageVarMap[variables] = pageData + nextPageVarMap[variables] = data.data as? PaginatedQuery.Data case .previous: - previousPageVarMap[variables] = pageData + previousPageVarMap[variables] = data.data as? PaginatedQuery.Data } if let latest { - output = latest + output = .init( + previousPages: latest.previous, + initialPage: latest.initial, + nextPages: latest.next, + errors: data.errors ?? [] + ) } } - value = output.flatMap { previousPages, initialPage, nextPages in + value = output.flatMap { paginationOutput in Result.success(( - PaginationOutput( - previousPages: previousPages, - initialPage: initialPage, - nextPages: nextPages, - errors: data.errors ?? [] - ), + paginationOutput, data.source == .cache ? .cache : .fetch )) } diff --git a/apollo-ios-pagination/Sources/ApolloPagination/GraphQLQueryPager+Convenience.swift b/apollo-ios-pagination/Sources/ApolloPagination/GraphQLQueryPager+Convenience.swift index c879defda..9e31eeeb3 100644 --- a/apollo-ios-pagination/Sources/ApolloPagination/GraphQLQueryPager+Convenience.swift +++ b/apollo-ios-pagination/Sources/ApolloPagination/GraphQLQueryPager+Convenience.swift @@ -33,7 +33,7 @@ public extension GraphQLQueryPager { initialQuery: InitialQuery, extractPageInfo: @escaping (InitialQuery.Data) -> P, pageResolver: @escaping (P, PaginationDirection) -> InitialQuery?, - transform: @escaping ([InitialQuery.Data], InitialQuery.Data, [InitialQuery.Data]) throws -> Model + transform: @escaping (PaginationOutput) throws -> Model ) { self.init( pager: GraphQLQueryPagerCoordinator( @@ -55,7 +55,7 @@ public extension GraphQLQueryPager { initialQuery: InitialQuery, extractPageInfo: @escaping (InitialQuery.Data) -> P, pageResolver: @escaping (P, PaginationDirection) -> InitialQuery?, - transform: @escaping (InitialQuery.Data) throws -> Model + transform: @escaping (PaginationOutput) throws -> Model ) where Model: RangeReplaceableCollection, T == Model.Element { self.init( pager: GraphQLQueryPagerCoordinator( @@ -65,8 +65,7 @@ public extension GraphQLQueryPager { extractPageInfo: pageExtraction(transform: extractPageInfo), pageResolver: pageResolver ), - initialTransform: transform, - pageTransform: transform + transform: transform ) } @@ -102,7 +101,7 @@ public extension GraphQLQueryPager { extractInitialPageInfo: @escaping (InitialQuery.Data) -> P, extractNextPageInfo: @escaping (PaginatedQuery.Data) -> P, pageResolver: @escaping (P, PaginationDirection) -> PaginatedQuery?, - transform: @escaping ([PaginatedQuery.Data], InitialQuery.Data, [PaginatedQuery.Data]) throws -> Model + transform: @escaping (PaginationOutput) throws -> Model ) where Model == PaginationOutput { self.init( pager: .init( @@ -118,34 +117,6 @@ public extension GraphQLQueryPager { transform: transform ) } - - /// Convenience initializer for creating a multi-query pager that - /// transforms output responses into collections - convenience init( - client: any ApolloClientProtocol, - initialQuery: InitialQuery, - watcherDispatchQueue: DispatchQueue = .main, - extractInitialPageInfo: @escaping (InitialQuery.Data) -> P, - extractNextPageInfo: @escaping (PaginatedQuery.Data) -> P, - pageResolver: @escaping (P, PaginationDirection) -> PaginatedQuery?, - initialTransform: @escaping (InitialQuery.Data) throws -> Model, - pageTransform: @escaping (PaginatedQuery.Data) throws -> Model - ) where Model: RangeReplaceableCollection, T == Model.Element { - self.init( - pager: .init( - client: client, - initialQuery: initialQuery, - watcherDispatchQueue: watcherDispatchQueue, - extractPageInfo: pageExtraction( - initialTransfom: extractInitialPageInfo, - paginatedTransform: extractNextPageInfo - ), - pageResolver: pageResolver - ), - initialTransform: initialTransform, - pageTransform: pageTransform - ) - } } // MARK: - AsyncGraphQLQueryPager Convenience Functions @@ -178,7 +149,7 @@ public extension AsyncGraphQLQueryPager { initialQuery: InitialQuery, extractPageInfo: @escaping (InitialQuery.Data) -> P, pageResolver: @escaping (P, PaginationDirection) -> InitialQuery?, - transform: @escaping ([InitialQuery.Data], InitialQuery.Data, [InitialQuery.Data]) throws -> Model + transform: @escaping (PaginationOutput) throws -> Model ) { self.init( pager: AsyncGraphQLQueryPagerCoordinator( @@ -248,7 +219,7 @@ public extension AsyncGraphQLQueryPager { extractInitialPageInfo: @escaping (InitialQuery.Data) -> P, extractNextPageInfo: @escaping (PaginatedQuery.Data) -> P, pageResolver: @escaping (P, PaginationDirection) -> PaginatedQuery?, - transform: @escaping ([PaginatedQuery.Data], InitialQuery.Data, [PaginatedQuery.Data]) throws -> Model + transform: @escaping (PaginationOutput) throws -> Model ) where Model == PaginationOutput { self.init( pager: .init( diff --git a/apollo-ios-pagination/Sources/ApolloPagination/GraphQLQueryPager.swift b/apollo-ios-pagination/Sources/ApolloPagination/GraphQLQueryPager.swift index dcd47afa8..d896bcc11 100644 --- a/apollo-ios-pagination/Sources/ApolloPagination/GraphQLQueryPager.swift +++ b/apollo-ios-pagination/Sources/ApolloPagination/GraphQLQueryPager.swift @@ -17,7 +17,7 @@ public class GraphQLQueryPager: Publisher { init, InitialQuery, PaginatedQuery>( pager: Pager, - transform: @escaping ([PaginatedQuery.Data], InitialQuery.Data, [PaginatedQuery.Data]) throws -> Model + transform: @escaping (PaginationOutput) throws -> Model ) { self.pager = pager pager.subscribe { [weak self] result in @@ -27,7 +27,7 @@ public class GraphQLQueryPager: Publisher { switch result { case let .success((output, source)): do { - let transformedModels = try transform(output.previousPages, output.initialPage, output.nextPages) + let transformedModels = try transform(output) returnValue = .success((transformedModels, source)) } catch { returnValue = .failure(error) @@ -50,27 +50,6 @@ public class GraphQLQueryPager: Publisher { } } - convenience init< - Pager: GraphQLQueryPagerCoordinator, - InitialQuery, - PaginatedQuery, - Element - >( - pager: Pager, - initialTransform: @escaping (InitialQuery.Data) throws -> Model, - pageTransform: @escaping (PaginatedQuery.Data) throws -> Model - ) where Model: RangeReplaceableCollection, Model.Element == Element { - self.init( - pager: pager, - transform: { previousData, initialData, nextData in - let previous = try previousData.flatMap { try pageTransform($0) } - let initial = try initialTransform(initialData) - let next = try nextData.flatMap { try pageTransform($0) } - return previous + initial + next - } - ) - } - public convenience init< P: PaginationInfo, InitialQuery: GraphQLQuery, @@ -92,53 +71,6 @@ public class GraphQLQueryPager: Publisher { self.init(pager: pager) } - public convenience init< - P: PaginationInfo, - InitialQuery: GraphQLQuery, - PaginatedQuery: GraphQLQuery, - Element - >( - client: any ApolloClientProtocol, - initialQuery: InitialQuery, - watcherDispatchQueue: DispatchQueue = .main, - extractPageInfo: @escaping (PageExtractionData) -> P, - pageResolver: ((P, PaginationDirection) -> PaginatedQuery?)?, - initialTransform: @escaping (InitialQuery.Data) throws -> Model, - pageTransform: @escaping (PaginatedQuery.Data) throws -> Model - ) where Model: RangeReplaceableCollection, Model.Element == Element { - let pager = GraphQLQueryPagerCoordinator( - client: client, - initialQuery: initialQuery, - watcherDispatchQueue: watcherDispatchQueue, - extractPageInfo: { data in - switch data { - case .initial(let data, let output): - return extractPageInfo(.initial(data, convertOutput(result: output))) - case .paginated(let data, let output): - return extractPageInfo(.paginated(data, convertOutput(result: output))) - } - }, - pageResolver: pageResolver - ) - self.init( - pager: pager, - initialTransform: initialTransform, - pageTransform: pageTransform - ) - - func convertOutput(result: PaginationOutput?) -> Model? { - guard let result else { return nil } - - let transform: ([PaginatedQuery.Data], InitialQuery.Data, [PaginatedQuery.Data]) throws -> Model = { previousData, initialData, nextData in - let previous = try previousData.flatMap { try pageTransform($0) } - let initial = try initialTransform(initialData) - let next = try nextData.flatMap { try pageTransform($0) } - return previous + initial + next - } - return try? transform(result.previousPages, result.initialPage, result.nextPages) - } - } - public convenience init< P: PaginationInfo, InitialQuery: GraphQLQuery, @@ -149,7 +81,7 @@ public class GraphQLQueryPager: Publisher { watcherDispatchQueue: DispatchQueue = .main, extractPageInfo: @escaping (PageExtractionData) -> P, pageResolver: ((P, PaginationDirection) -> PaginatedQuery?)?, - transform: @escaping ([PaginatedQuery.Data], InitialQuery.Data, [PaginatedQuery.Data]) throws -> Model + transform: @escaping (PaginationOutput) throws -> Model ) { let pager = GraphQLQueryPagerCoordinator( client: client, @@ -158,9 +90,9 @@ public class GraphQLQueryPager: Publisher { extractPageInfo: { data in switch data { case .initial(let data, let output): - return extractPageInfo(.initial(data, convertOutput(result: output))) + return extractPageInfo(.initial(data, try? output.flatMap(transform))) case .paginated(let data, let output): - return extractPageInfo(.paginated(data, convertOutput(result: output))) + return extractPageInfo(.paginated(data, try? output.flatMap(transform))) } }, pageResolver: pageResolver @@ -169,11 +101,6 @@ public class GraphQLQueryPager: Publisher { pager: pager, transform: transform ) - - func convertOutput(result: PaginationOutput?) -> Model? { - guard let result else { return nil } - return try? transform(result.previousPages, result.initialPage, result.nextPages) - } } deinit { diff --git a/apollo-ios-pagination/Sources/ApolloPagination/GraphQLQueryPagerOutput.swift b/apollo-ios-pagination/Sources/ApolloPagination/GraphQLQueryPagerOutput.swift index 3ce35c574..c19ca1ef0 100644 --- a/apollo-ios-pagination/Sources/ApolloPagination/GraphQLQueryPagerOutput.swift +++ b/apollo-ios-pagination/Sources/ApolloPagination/GraphQLQueryPagerOutput.swift @@ -9,7 +9,7 @@ public struct PaginationOutput