Skip to content

Commit

Permalink
[ApolloPagination] Remove async requirement from AsyncGraphQLQueryPag…
Browse files Browse the repository at this point in the history
…er initializers
  • Loading branch information
Iron-Ham committed Mar 12, 2024
1 parent d52bedc commit 9002b2a
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 65 deletions.
12 changes: 6 additions & 6 deletions Tests/ApolloPaginationTests/AsyncGraphQLQueryPagerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ final class AsyncGraphQLQueryPagerTests: XCTestCase {
"first": 2,
"after": GraphQLNullable<String>.null
]
let pager = await AsyncGraphQLQueryPager(
let pager = AsyncGraphQLQueryPager(
client: client,
initialQuery: initialQuery,
extractPageInfo: { data in
Expand Down Expand Up @@ -84,7 +84,7 @@ final class AsyncGraphQLQueryPagerTests: XCTestCase {
"first": 2,
"after": GraphQLNullable<String>.null
]
let pager = await AsyncGraphQLQueryPager(
let pager = AsyncGraphQLQueryPager(
client: client,
initialQuery: initialQuery,
extractPageInfo: { data in
Expand Down Expand Up @@ -146,7 +146,7 @@ final class AsyncGraphQLQueryPagerTests: XCTestCase {
"first": 2,
"after": GraphQLNullable<String>.null
]
let pager = await AsyncGraphQLQueryPager(
let pager = AsyncGraphQLQueryPager(
client: client,
initialQuery: initialQuery,
extractPageInfo: { data in
Expand Down Expand Up @@ -208,7 +208,7 @@ final class AsyncGraphQLQueryPagerTests: XCTestCase {
"first": 2,
"after": GraphQLNullable<String>.null
]
let pager = await AsyncGraphQLQueryPager(
let pager = AsyncGraphQLQueryPager(
client: client,
initialQuery: initialQuery,
extractPageInfo: { data in
Expand Down Expand Up @@ -273,7 +273,7 @@ final class AsyncGraphQLQueryPagerTests: XCTestCase {
let name: String
}

let anyPager = await createPager().eraseToAnyPager { data in
let anyPager = createPager().eraseToAnyPager { data in
data.hero.friendsConnection.friends.map {
ViewModel(name: $0.name)
}
Expand Down Expand Up @@ -306,7 +306,7 @@ final class AsyncGraphQLQueryPagerTests: XCTestCase {
}

func test_passesBackSeparateData() async throws {
let anyPager = await createPager().eraseToAnyPager { _, initial, next in
let anyPager = createPager().eraseToAnyPager { _, initial, next in
if let latestPage = next.last {
return latestPage.hero.friendsConnection.friends.last?.name
}
Expand Down
2 changes: 1 addition & 1 deletion Tests/ApolloPaginationTests/OffsetTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ final class OffsetTests: XCTestCase {
return nextQuery
}
)
return await AsyncGraphQLQueryPager(pager: pager)
return AsyncGraphQLQueryPager(pager: pager)
}

// This is due to a timing issue in unit tests only wherein we deinit immediately after waiting for expectations
Expand Down
12 changes: 6 additions & 6 deletions Tests/ApolloPaginationTests/PagerCoordinator+Erase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ extension GraphQLQueryPagerCoordinator {
extension AsyncGraphQLQueryPagerCoordinator {
nonisolated func eraseToAnyPager<T>(
transform: @escaping ([PaginatedQuery.Data], InitialQuery.Data, [PaginatedQuery.Data]) throws -> T
) async -> AsyncGraphQLQueryPager<T> {
await AsyncGraphQLQueryPager(
) -> AsyncGraphQLQueryPager<T> {
AsyncGraphQLQueryPager(
pager: self,
transform: transform
)
Expand All @@ -42,8 +42,8 @@ extension AsyncGraphQLQueryPagerCoordinator {
nonisolated func eraseToAnyPager<T, S: RangeReplaceableCollection>(
initialTransform: @escaping (InitialQuery.Data) throws -> S,
pageTransform: @escaping (PaginatedQuery.Data) throws -> S
) async -> AsyncGraphQLQueryPager<S> where T == S.Element {
await AsyncGraphQLQueryPager(
) -> AsyncGraphQLQueryPager<S> where T == S.Element {
AsyncGraphQLQueryPager(
pager: self,
initialTransform: initialTransform,
pageTransform: pageTransform
Expand All @@ -52,8 +52,8 @@ extension AsyncGraphQLQueryPagerCoordinator {

nonisolated func eraseToAnyPager<T, S: RangeReplaceableCollection>(
transform: @escaping (InitialQuery.Data) throws -> S
) async -> AsyncGraphQLQueryPager<S> where InitialQuery == PaginatedQuery, T == S.Element {
await AsyncGraphQLQueryPager(
) -> AsyncGraphQLQueryPager<S> where InitialQuery == PaginatedQuery, T == S.Element {
AsyncGraphQLQueryPager(
pager: self,
initialTransform: transform,
pageTransform: transform
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public class AsyncGraphQLQueryPager<Model>: Publisher {
public typealias Output = Result<(Model, UpdateSource), Error>
let _subject: CurrentValueSubject<Output?, Never> = .init(nil)
var publisher: AnyPublisher<Output, Never> { _subject.compactMap({ $0 }).eraseToAnyPublisher() }
public var cancellables: Set<AnyCancellable> = []
@Atomic public var cancellables: Set<AnyCancellable> = []
public let pager: any AsyncPagerType

public var canLoadNext: Bool { get async { await pager.canLoadNext } }
Expand All @@ -18,45 +18,51 @@ public class AsyncGraphQLQueryPager<Model>: Publisher {
init<Pager: AsyncGraphQLQueryPagerCoordinator<InitialQuery, PaginatedQuery>, InitialQuery, PaginatedQuery>(
pager: Pager,
transform: @escaping ([PaginatedQuery.Data], InitialQuery.Data, [PaginatedQuery.Data]) throws -> Model
) async {
) {
self.pager = pager
await pager.subscribe { [weak self] result in
guard let self else { return }
let returnValue: Output

switch result {
case let .success((output, source)):
do {
let transformedModels = try transform(output.previousPages, output.initialPage, output.nextPages)
returnValue = .success((transformedModels, source))
} catch {
Task {
let cancellable = await pager.subscribe { [weak self] result in
guard let self else { return }
let returnValue: Output

switch result {
case let .success((output, source)):
do {
let transformedModels = try transform(output.previousPages, output.initialPage, output.nextPages)
returnValue = .success((transformedModels, source))
} catch {
returnValue = .failure(error)
}
case let .failure(error):
returnValue = .failure(error)
}
case let .failure(error):
returnValue = .failure(error)
}

_subject.send(returnValue)
}.store(in: &cancellables)
_subject.send(returnValue)
}
_ = $cancellables.mutate { $0.insert(cancellable) }
}
}

init<Pager: AsyncGraphQLQueryPagerCoordinator<InitialQuery, PaginatedQuery>, InitialQuery, PaginatedQuery>(
pager: Pager
) async where Model == PaginationOutput<InitialQuery, PaginatedQuery> {
) where Model == PaginationOutput<InitialQuery, PaginatedQuery> {
self.pager = pager
await pager.subscribe { [weak self] result in
guard let self else { return }
let returnValue: Output

switch result {
case let .success((output, source)):
returnValue = .success((output, source))
case let .failure(error):
returnValue = .failure(error)
}
Task {
let cancellable = await pager.subscribe { [weak self] result in
guard let self else { return }
let returnValue: Output

switch result {
case let .success((output, source)):
returnValue = .success((output, source))
case let .failure(error):
returnValue = .failure(error)
}

_subject.send(returnValue)
}.store(in: &cancellables)
_subject.send(returnValue)
}
_ = $cancellables.mutate { $0.insert(cancellable) }
}
}

convenience init<
Expand All @@ -68,8 +74,8 @@ public class AsyncGraphQLQueryPager<Model>: Publisher {
pager: Pager,
initialTransform: @escaping (InitialQuery.Data) throws -> Model,
pageTransform: @escaping (PaginatedQuery.Data) throws -> Model
) async where Model: RangeReplaceableCollection, Model.Element == Element {
await self.init(
) where Model: RangeReplaceableCollection, Model.Element == Element {
self.init(
pager: pager,
transform: { previousData, initialData, nextData in
let previous = try previousData.flatMap { try pageTransform($0) }
Expand All @@ -93,7 +99,7 @@ public class AsyncGraphQLQueryPager<Model>: Publisher {
pageResolver: ((P, PaginationDirection) -> PaginatedQuery?)?,
initialTransform: @escaping (InitialQuery.Data) throws -> Model,
pageTransform: @escaping (PaginatedQuery.Data) throws -> Model
) async where Model: RangeReplaceableCollection, Model.Element == Element {
) where Model: RangeReplaceableCollection, Model.Element == Element {
let pager = AsyncGraphQLQueryPagerCoordinator(
client: client,
initialQuery: initialQuery,
Expand All @@ -108,7 +114,7 @@ public class AsyncGraphQLQueryPager<Model>: Publisher {
},
pageResolver: pageResolver
)
await self.init(
self.init(
pager: pager,
initialTransform: initialTransform,
pageTransform: pageTransform
Expand Down Expand Up @@ -137,15 +143,15 @@ public class AsyncGraphQLQueryPager<Model>: Publisher {
watcherDispatchQueue: DispatchQueue = .main,
extractPageInfo: @escaping (PageExtractionData<InitialQuery, PaginatedQuery, Model?>) -> P,
pageResolver: ((P, PaginationDirection) -> PaginatedQuery?)?
) async where Model == PaginationOutput<InitialQuery, PaginatedQuery> {
) where Model == PaginationOutput<InitialQuery, PaginatedQuery> {
let pager = AsyncGraphQLQueryPagerCoordinator(
client: client,
initialQuery: initialQuery,
watcherDispatchQueue: watcherDispatchQueue,
extractPageInfo: extractPageInfo,
pageResolver: pageResolver
)
await self.init(
self.init(
pager: pager
)
}
Expand All @@ -161,7 +167,7 @@ public class AsyncGraphQLQueryPager<Model>: Publisher {
extractPageInfo: @escaping (PageExtractionData<InitialQuery, PaginatedQuery, Model?>) -> P,
pageResolver: ((P, PaginationDirection) -> PaginatedQuery?)?,
transform: @escaping ([PaginatedQuery.Data], InitialQuery.Data, [PaginatedQuery.Data]) throws -> Model
) async {
) {
let pager = AsyncGraphQLQueryPagerCoordinator(
client: client,
initialQuery: initialQuery,
Expand All @@ -176,7 +182,7 @@ public class AsyncGraphQLQueryPager<Model>: Publisher {
},
pageResolver: pageResolver
)
await self.init(
self.init(
pager: pager,
transform: transform
)
Expand All @@ -191,9 +197,10 @@ public class AsyncGraphQLQueryPager<Model>: Publisher {
/// Subscribe to the results of the pager, with the management of the subscriber being stored internally to the `AnyGraphQLQueryPager`.
/// - Parameter completion: The closure to trigger when new values come in.
public func subscribe(completion: @MainActor @escaping (Output) -> Void) {
publisher.sink { result in
let cancellable = publisher.sink { result in
Task { await completion(result) }
}.store(in: &cancellables)
}
_ = $cancellables.mutate { $0.insert(cancellable) }
}

/// Load the next page, if available.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,8 @@ public extension AsyncGraphQLQueryPager {
initialQuery: InitialQuery,
extractPageInfo: @escaping (InitialQuery.Data) -> P,
pageResolver: @escaping (P, PaginationDirection) -> InitialQuery?
) async where Model == PaginationOutput<InitialQuery, InitialQuery> {
await self.init(
) where Model == PaginationOutput<InitialQuery, InitialQuery> {
self.init(
pager: AsyncGraphQLQueryPagerCoordinator(
client: client,
initialQuery: initialQuery,
Expand All @@ -179,8 +179,8 @@ public extension AsyncGraphQLQueryPager {
extractPageInfo: @escaping (InitialQuery.Data) -> P,
pageResolver: @escaping (P, PaginationDirection) -> InitialQuery?,
transform: @escaping ([InitialQuery.Data], InitialQuery.Data, [InitialQuery.Data]) throws -> Model
) async {
await self.init(
) {
self.init(
pager: AsyncGraphQLQueryPagerCoordinator(
client: client,
initialQuery: initialQuery,
Expand All @@ -201,8 +201,8 @@ public extension AsyncGraphQLQueryPager {
extractPageInfo: @escaping (InitialQuery.Data) -> P,
pageResolver: @escaping (P, PaginationDirection) -> InitialQuery?,
transform: @escaping (InitialQuery.Data) throws -> Model
) async where Model: RangeReplaceableCollection, T == Model.Element {
await self.init(
) where Model: RangeReplaceableCollection, T == Model.Element {
self.init(
pager: AsyncGraphQLQueryPagerCoordinator(
client: client,
initialQuery: initialQuery,
Expand All @@ -224,8 +224,8 @@ public extension AsyncGraphQLQueryPager {
extractInitialPageInfo: @escaping (InitialQuery.Data) -> P,
extractNextPageInfo: @escaping (PaginatedQuery.Data) -> P,
pageResolver: @escaping (P, PaginationDirection) -> PaginatedQuery?
) async where Model == PaginationOutput<InitialQuery, PaginatedQuery> {
await self.init(
) where Model == PaginationOutput<InitialQuery, PaginatedQuery> {
self.init(
pager: .init(
client: client,
initialQuery: initialQuery,
Expand All @@ -249,8 +249,8 @@ public extension AsyncGraphQLQueryPager {
extractNextPageInfo: @escaping (PaginatedQuery.Data) -> P,
pageResolver: @escaping (P, PaginationDirection) -> PaginatedQuery?,
transform: @escaping ([PaginatedQuery.Data], InitialQuery.Data, [PaginatedQuery.Data]) throws -> Model
) async where Model == PaginationOutput<InitialQuery, PaginatedQuery> {
await self.init(
) where Model == PaginationOutput<InitialQuery, PaginatedQuery> {
self.init(
pager: .init(
client: client,
initialQuery: initialQuery,
Expand All @@ -276,8 +276,8 @@ public extension AsyncGraphQLQueryPager {
pageResolver: @escaping (P, PaginationDirection) -> PaginatedQuery?,
initialTransform: @escaping (InitialQuery.Data) throws -> Model,
pageTransform: @escaping (PaginatedQuery.Data) throws -> Model
) async where Model: RangeReplaceableCollection, T == Model.Element {
await self.init(
) where Model: RangeReplaceableCollection, T == Model.Element {
self.init(
pager: .init(
client: client,
initialQuery: initialQuery,
Expand Down

0 comments on commit 9002b2a

Please sign in to comment.