Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions Sources/Apollo/GraphQLQueryWatcher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,14 @@ public final class GraphQLQueryWatcher<Query: GraphQLQuery>: Cancellable, Apollo
guard let self = self else { return }

switch result {
case .success:
case .success(let graphQLResult):
self.callbackQueue.async { [weak self] in
self?.resultHandler(result)
guard let self = self else {
return
}

self.dependentKeys = graphQLResult.dependentKeys
self.resultHandler(result)
}
case .failure:
// If the cache fetch is not successful, for instance if the data is missing, refresh from the server.
Expand Down
2 changes: 1 addition & 1 deletion Sources/ApolloTestSupport/MockNetworkTransport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Apollo
import Dispatch

public final class MockNetworkTransport: NetworkTransport {
let body: JSONObject
public var body: JSONObject

public var clientName = "MockNetworkTransport"
public var clientVersion = "mock_version"
Expand Down
122 changes: 122 additions & 0 deletions Tests/ApolloCacheDependentTests/WatchQueryTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -575,4 +575,126 @@ class WatchQueryTests: XCTestCase, CacheTesting {
waitForExpectations(timeout: 5, handler: nil)
}
}

func testWatchedQueryDependentKeysAreUpdated() {
withCache { cache in
let networkTransport = MockNetworkTransport(body: [
"data": [
"hero": [
"id": "0",
"name": "Artoo",
"__typename": "Droid",
"friends": [
[
"id": "10",
"__typename": "Human",
"name": "Luke Skywalker"
]
]
]
]
])

let store = ApolloStore(cache: cache)
let client = ApolloClient(networkTransport: networkTransport, store: store)
client.store.cacheKeyForObject = { $0["id"] }

let query = HeroAndFriendsNamesWithIDsQuery()
let hasPicardFriendExpecation = self.expectation(description: "Has friend named Jean-Luc Picard")
let hasHanSoloFriendExpecation = self.expectation(description: "Has friend named Han Solo")
let initialFetchExpectation = self.expectation(description: "Initial fetch")
var isInitialFetch = true
var expectedDependentKeys = [
"0.__typename",
"0.friends",
"0.id",
"0.name",
"10.__typename",
"10.id",
"10.name",
"QUERY_ROOT.hero",
]

_ = client.watch(query: query) { result in
defer {
if isInitialFetch {
isInitialFetch = false
initialFetchExpectation.fulfill()
}
}
switch result {
case .success(let graphQLResult):
XCTAssertEqual(graphQLResult.dependentKeys?.sorted(), expectedDependentKeys)
guard let friends = graphQLResult.data?.hero?.friends else {
XCTFail("404 friends not found")
return
}

if friends.contains(where: { $0?.name == "Jean-Luc Picard" }) {
hasPicardFriendExpecation.fulfill()
}
if friends.contains(where: { $0?.name == "Han Solo" }) {
hasHanSoloFriendExpecation.fulfill()
}
case .failure(let error):
XCTFail("Watcher error: \(error)")
}
}
wait(for: [initialFetchExpectation], timeout: 1)

/// Add an additional friend to the results so that the watcher for this query knows to look for updates to friend #11
let updateInitialQueryExpectation = self.expectation(description: "Update initial query")
store.withinReadWriteTransaction({ transaction in
try transaction.update(query: query) { (data: inout HeroAndFriendsNamesWithIDsQuery.Data) in
data.hero?.friends?.append(try .init(jsonObject: [
"id": "11",
"__typename": "Human",
"name": "Jean-Luc Picard"
]))
updateInitialQueryExpectation.fulfill()
}
})

/// The dependent keys should have changed here since we've now added a new friend to the user's friends.
expectedDependentKeys = [
"0.__typename",
"0.friends",
"0.id",
"0.name",
"10.__typename",
"10.id",
"10.name",
"11.__typename",
"11.id",
"11.name",
"QUERY_ROOT.hero"
]

wait(for: [updateInitialQueryExpectation, hasPicardFriendExpecation], timeout: 1)


/// Send an update that updates friend #11 on a different query
networkTransport.body = [
"data": [
"hero": [
"id": "2",
"name": "R2-D2",
"__typename": "Droid",
"friends": [
[
"id": "11",
"__typename": "Human",
"name": "Han Solo"
]
]
]
]
]

/// This fetch should trigger our watcher on friend #11
client.fetch(query: HeroAndFriendsNamesWithIDsQuery(episode: .newhope), cachePolicy: .fetchIgnoringCacheData)

self.wait(for: [hasHanSoloFriendExpecation], timeout: 1)
}
}
}