-
Notifications
You must be signed in to change notification settings - Fork 749
Expand file tree
/
Copy pathGraphQLQueryWatcher.swift
More file actions
87 lines (72 loc) · 2.91 KB
/
GraphQLQueryWatcher.swift
File metadata and controls
87 lines (72 loc) · 2.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import Dispatch
/// A `GraphQLQueryWatcher` is responsible for watching the store, and calling the result handler with a new result whenever any of the data the previous result depends on changes.
///
/// NOTE: The store retains the watcher while subscribed. You must call `cancel()` on your query watcher when you no longer need results. Failure to call `cancel()` before releasing your reference to the returned watcher will result in a memory leak.
public final class GraphQLQueryWatcher<Query: GraphQLQuery>: Cancellable, ApolloStoreSubscriber {
weak var client: ApolloClientProtocol?
public let query: Query
let resultHandler: GraphQLResultHandler<Query.Data>
private var context = 0
private weak var fetching: Cancellable?
private var dependentKeys: Set<CacheKey>?
/// Designated initializer
///
/// - Parameters:
/// - client: The client protocol to pass in
/// - query: The query to watch
/// - resultHandler: The result handler to call with changes.
public init(client: ApolloClientProtocol,
query: Query,
resultHandler: @escaping GraphQLResultHandler<Query.Data>) {
self.client = client
self.query = query
self.resultHandler = resultHandler
client.store.subscribe(self)
}
/// Refetch a query from the server.
public func refetch() {
fetch(cachePolicy: .fetchIgnoringCacheData)
}
func fetch(cachePolicy: CachePolicy) {
// Cancel any fetch already in flight before starting a new fetch, if the new fetch includes the server.
// Without this extra condition, the store subscription can entirely cancel a server fetch if a dependent key is modified
// while it is in flight.
if cachePolicy.alwaysIncludesServerFetch {
fetching?.cancel()
}
fetching = client?.fetch(query: query, cachePolicy: cachePolicy, context: &context, queue: .main) { [weak self] result in
guard let `self` = self else { return }
switch result {
case .success(let graphQLResult):
self.dependentKeys = graphQLResult.dependentKeys
case .failure:
break
}
self.resultHandler(result)
}
}
/// Cancel any in progress fetching operations and unsubscribe from the store.
public func cancel() {
fetching?.cancel()
client?.store.unsubscribe(self)
}
func store(_ store: ApolloStore,
didChangeKeys changedKeys: Set<CacheKey>,
context: UnsafeMutableRawPointer?) {
if context == &self.context { return }
guard let dependentKeys = dependentKeys else { return }
if !dependentKeys.isDisjoint(with: changedKeys) {
fetch(cachePolicy: .returnCacheDataElseFetch)
}
}
}
private extension CachePolicy {
var alwaysIncludesServerFetch: Bool {
switch self {
case .fetchIgnoringCacheCompletely, .fetchIgnoringCacheData, .returnCacheDataAndFetch:
return true
case .returnCacheDataDontFetch, .returnCacheDataElseFetch:
return false
}
}
}