-
Notifications
You must be signed in to change notification settings - Fork 748
Expand file tree
/
Copy pathCacheReadInterceptor.swift
More file actions
129 lines (115 loc) · 3.89 KB
/
CacheReadInterceptor.swift
File metadata and controls
129 lines (115 loc) · 3.89 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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import Foundation
#if !COCOAPODS
import ApolloMigrationAPI
#endif
/// An interceptor that reads data from the cache for queries, following the `HTTPRequest`'s `cachePolicy`.
public struct CacheReadInterceptor: ApolloInterceptor {
private let store: ApolloStore
public var id: String = UUID().uuidString
/// Designated initializer
///
/// - Parameter store: The store to use when reading from the cache.
public init(store: ApolloStore) {
self.store = store
}
public func interceptAsync<Operation: GraphQLOperation>(
chain: any RequestChain,
request: HTTPRequest<Operation>,
response: HTTPResponse<Operation>?,
completion: @escaping (Result<GraphQLResult<Operation.Data>, any Error>) -> Void) {
switch Operation.operationType {
case .mutation,
.subscription:
// Mutations and subscriptions don't need to hit the cache.
chain.proceedAsync(
request: request,
response: response,
interceptor: self,
completion: completion
)
case .query:
switch request.cachePolicy {
case .fetchIgnoringCacheCompletely,
.fetchIgnoringCacheData:
// Don't bother with the cache, just keep going
chain.proceedAsync(
request: request,
response: response,
interceptor: self,
completion: completion
)
case .returnCacheDataAndFetch:
self.fetchFromCache(for: request, chain: chain) { cacheFetchResult in
switch cacheFetchResult {
case .failure:
// Don't return a cache miss error, just keep going
break
case .success(let graphQLResult):
chain.returnValueAsync(
for: request,
value: graphQLResult,
completion: completion
)
}
// In either case, keep going asynchronously
chain.proceedAsync(
request: request,
response: response,
interceptor: self,
completion: completion
)
}
case .returnCacheDataElseFetch:
self.fetchFromCache(for: request, chain: chain) { cacheFetchResult in
switch cacheFetchResult {
case .failure:
// Cache miss, proceed to network without returning error
chain.proceedAsync(
request: request,
response: response,
interceptor: self,
completion: completion
)
case .success(let graphQLResult):
// Cache hit! We're done.
chain.returnValueAsync(
for: request,
value: graphQLResult,
completion: completion
)
}
}
case .returnCacheDataDontFetch:
self.fetchFromCache(for: request, chain: chain) { cacheFetchResult in
switch cacheFetchResult {
case .failure(let error):
// Cache miss - don't hit the network, just return the error.
chain.handleErrorAsync(
error,
request: request,
response: response,
completion: completion
)
case .success(let result):
chain.returnValueAsync(
for: request,
value: result,
completion: completion
)
}
}
}
}
}
private func fetchFromCache<Operation: GraphQLOperation>(
for request: HTTPRequest<Operation>,
chain: any RequestChain,
completion: @escaping (Result<GraphQLResult<Operation.Data>, any Error>) -> Void) {
self.store.load(request.operation) { loadResult in
guard !chain.isCancelled else {
return
}
completion(loadResult)
}
}
}