Skip to content

Commit 4693ad8

Browse files
Merge pull request #709 from apollographql/rm/public-promise
Remove Public Promise
2 parents bd6f03e + 8f24739 commit 4693ad8

16 files changed

Lines changed: 525 additions & 265 deletions

Apollo.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
9B95EDC022CAA0B000702BB2 /* GETTransformerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B95EDBF22CAA0AF00702BB2 /* GETTransformerTests.swift */; };
1515
9BA1244A22D8A8EA00BF1D24 /* JSONSerialziation+Sorting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BA1244922D8A8EA00BF1D24 /* JSONSerialziation+Sorting.swift */; };
1616
9BA1245E22DE116B00BF1D24 /* Result+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BA1245D22DE116B00BF1D24 /* Result+Helpers.swift */; };
17+
9BA3130E2302BEA5007B7FC5 /* DispatchQueue+Optional.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BA3130D2302BEA5007B7FC5 /* DispatchQueue+Optional.swift */; };
1718
9BDE43D122C6655300FD7C7F /* Cancellable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BDE43D022C6655200FD7C7F /* Cancellable.swift */; };
1819
9BDE43DD22C6705300FD7C7F /* GraphQLHTTPResponseError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BDE43DC22C6705300FD7C7F /* GraphQLHTTPResponseError.swift */; };
1920
9BDE43DF22C6708600FD7C7F /* GraphQLHTTPRequestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BDE43DE22C6708600FD7C7F /* GraphQLHTTPRequestError.swift */; };
@@ -270,6 +271,7 @@
270271
9B95EDBF22CAA0AF00702BB2 /* GETTransformerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GETTransformerTests.swift; sourceTree = "<group>"; };
271272
9BA1244922D8A8EA00BF1D24 /* JSONSerialziation+Sorting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "JSONSerialziation+Sorting.swift"; sourceTree = "<group>"; };
272273
9BA1245D22DE116B00BF1D24 /* Result+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Result+Helpers.swift"; sourceTree = "<group>"; };
274+
9BA3130D2302BEA5007B7FC5 /* DispatchQueue+Optional.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DispatchQueue+Optional.swift"; sourceTree = "<group>"; };
273275
9BDE43D022C6655200FD7C7F /* Cancellable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cancellable.swift; sourceTree = "<group>"; };
274276
9BDE43DC22C6705300FD7C7F /* GraphQLHTTPResponseError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphQLHTTPResponseError.swift; sourceTree = "<group>"; };
275277
9BDE43DE22C6708600FD7C7F /* GraphQLHTTPRequestError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphQLHTTPRequestError.swift; sourceTree = "<group>"; };
@@ -711,6 +713,7 @@
711713
children = (
712714
9F578D8F1D8D2CB300C0EA36 /* Utilities.swift */,
713715
9FCDFD221E33A0D8007519DC /* AsynchronousOperation.swift */,
716+
9BA3130D2302BEA5007B7FC5 /* DispatchQueue+Optional.swift */,
714717
9FE941CF1E62C771007CDD89 /* Promise.swift */,
715718
9BA1245D22DE116B00BF1D24 /* Result+Helpers.swift */,
716719
9F19D8431EED568200C57247 /* ResultOrPromise.swift */,
@@ -1230,6 +1233,7 @@
12301233
9FE941D01E62C771007CDD89 /* Promise.swift in Sources */,
12311234
9BA1245E22DE116B00BF1D24 /* Result+Helpers.swift in Sources */,
12321235
9FC750631D2A59F600458D91 /* ApolloClient.swift in Sources */,
1236+
9BA3130E2302BEA5007B7FC5 /* DispatchQueue+Optional.swift in Sources */,
12331237
9F86B6901E65533D00B885FF /* GraphQLResponseGenerator.swift in Sources */,
12341238
9FC9A9C21E2D3CAF0023C4D5 /* GraphQLInputValue.swift in Sources */,
12351239
);

Sources/Apollo/ApolloClient.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,8 @@ extension ApolloClient: ApolloClientProtocol {
118118
}
119119
}
120120

121-
public func clearCache() -> Promise<Void> {
122-
return self.store.clearCache()
121+
public func clearCache(callbackQueue: DispatchQueue = .main, completion: ((Result<Void, Error>) -> Void)? = nil) {
122+
self.store.clearCache(completion: completion)
123123
}
124124

125125
@discardableResult public func fetch<Query: GraphQLQuery>(query: Query,

Sources/Apollo/ApolloClientProtocol.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ public protocol ApolloClientProtocol: class {
1212
/// Clears the underlying cache.
1313
/// Be aware: In more complex setups, the same underlying cache can be used across multiple instances, so if you call this on one instance, it'll clear that cache across all instances which share that cache.
1414
///
15-
/// - Returns: Promise which fulfills when clear is complete.
16-
func clearCache() -> Promise<Void>
15+
/// - Parameters:
16+
/// - callbackQueue: The queue to fall back on. Should default to the main queue.
17+
/// - completion: [optional] A completion closure to execute when clearing has completed. Should default to nil.
18+
func clearCache(callbackQueue: DispatchQueue, completion: ((Result<Void, Error>) -> Void)?)
1719

1820
/// Fetches a query from the server or from the local cache, depending on the current contents of the cache and the specified cache policy.
1921
///

Sources/Apollo/ApolloStore.swift

Lines changed: 110 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,14 @@ public final class ApolloStore {
5050
/// Clears the instance of the cache. Note that a cache can be shared across multiple `ApolloClient` objects, so clearing that underlying cache will clear it for all clients.
5151
///
5252
/// - Returns: A promise which fulfills when the Cache is cleared.
53-
public func clearCache() -> Promise<Void> {
54-
return Promise<Void> { fulfill, reject in
55-
queue.async(flags: .barrier) {
56-
self.cacheLock.withWriteLock {
57-
self.cache.clear()
53+
public func clearCache(callbackQueue: DispatchQueue = .main, completion: ((Result<Void, Error>) -> Void)? = nil) {
54+
queue.async(flags: .barrier) {
55+
self.cacheLock.withWriteLock {
56+
self.cache.clearPromise()
5857
}.andThen {
59-
fulfill(())
60-
}
58+
DispatchQueue.apollo_returnResultAsyncIfNeeded(on: callbackQueue,
59+
action: completion,
60+
result: .success(()))
6161
}
6262
}
6363
}
@@ -66,7 +66,7 @@ public final class ApolloStore {
6666
return Promise<Void> { fulfill, reject in
6767
queue.async(flags: .barrier) {
6868
self.cacheLock.withWriteLock {
69-
self.cache.merge(records: records)
69+
self.cache.mergePromise(records: records)
7070
}.andThen { changedKeys in
7171
self.didChangeKeys(changedKeys, context: context)
7272
fulfill(())
@@ -87,7 +87,7 @@ public final class ApolloStore {
8787
}
8888
}
8989

90-
public func withinReadTransaction<T>(_ body: @escaping (ReadTransaction) throws -> Promise<T>) -> Promise<T> {
90+
func withinReadTransactionPromise<T>(_ body: @escaping (ReadTransaction) throws -> Promise<T>) -> Promise<T> {
9191
return Promise<ReadTransaction> { fulfill, reject in
9292
self.queue.async {
9393
self.cacheLock.lockForReading()
@@ -99,14 +99,32 @@ public final class ApolloStore {
9999
self.cacheLock.unlock()
100100
}
101101
}
102-
103-
public func withinReadTransaction<T>(_ body: @escaping (ReadTransaction) throws -> T) -> Promise<T> {
104-
return withinReadTransaction {
105-
Promise(fulfilled: try body($0))
102+
103+
/// Performs an operation within a read transaction
104+
///
105+
/// - Parameters:
106+
/// - body: The body of the operation to perform.
107+
/// - callbackQueue: [optional] The callback queue to use to perform the completion block on. Will perform on the current queue if not provided. Defaults to nil.
108+
/// - completion: [optional] The completion block to perform when the read transaction completes. Defaults to nil.
109+
public func withinReadTransaction<T>(_ body: @escaping (ReadTransaction) throws -> T,
110+
callbackQueue: DispatchQueue? = nil,
111+
completion: ((Result<T, Error>) -> Void)? = nil) {
112+
_ = self.withinReadTransactionPromise {
113+
Promise(fulfilled: try body($0))
114+
}
115+
.andThen { object in
116+
DispatchQueue.apollo_returnResultAsyncIfNeeded(on: callbackQueue,
117+
action: completion,
118+
result: .success(object))
119+
}
120+
.catch { error in
121+
DispatchQueue.apollo_returnResultAsyncIfNeeded(on: callbackQueue,
122+
action: completion,
123+
result: .failure(error))
106124
}
107125
}
108126

109-
public func withinReadWriteTransaction<T>(_ body: @escaping (ReadWriteTransaction) throws -> Promise<T>) -> Promise<T> {
127+
func withinReadWriteTransactionPromise<T>(_ body: @escaping (ReadWriteTransaction) throws -> Promise<T>) -> Promise<T> {
110128
return Promise<ReadWriteTransaction> { fulfill, reject in
111129
self.queue.async(flags: .barrier) {
112130
self.cacheLock.lockForWriting()
@@ -117,15 +135,33 @@ public final class ApolloStore {
117135
self.cacheLock.unlock()
118136
}
119137
}
120-
121-
public func withinReadWriteTransaction<T>(_ body: @escaping (ReadWriteTransaction) throws -> T) -> Promise<T> {
122-
return withinReadWriteTransaction {
123-
Promise(fulfilled: try body($0))
124-
}
138+
139+
/// Performs an operation within a read-write transaction
140+
///
141+
/// - Parameters:
142+
/// - body: The body of the operation to perform
143+
/// - callbackQueue: [optional] a callback queue to perform the action on. Will perform on the current queue if not provided. Defaults to nil.
144+
/// - completion: [optional] a completion block to fire when the read-write transaction completes. Defaults to nil.
145+
public func withinReadWriteTransaction<T>(_ body: @escaping (ReadWriteTransaction) throws -> T,
146+
callbackQueue: DispatchQueue? = nil,
147+
completion: ((Result<T, Error>) -> Void)? = nil) {
148+
_ = self.withinReadWriteTransactionPromise {
149+
Promise(fulfilled: try body($0))
150+
}
151+
.andThen { object in
152+
DispatchQueue.apollo_returnResultAsyncIfNeeded(on: callbackQueue,
153+
action: completion,
154+
result: .success(object))
155+
}
156+
.catch { error in
157+
DispatchQueue.apollo_returnResultAsyncIfNeeded(on: callbackQueue,
158+
action: completion,
159+
result: .failure(error))
160+
}
125161
}
126162

127-
public func load<Query: GraphQLQuery>(query: Query) -> Promise<GraphQLResult<Query.Data>> {
128-
return withinReadTransaction { transaction in
163+
func load<Query: GraphQLQuery>(query: Query) -> Promise<GraphQLResult<Query.Data>> {
164+
return withinReadTransactionPromise { transaction in
129165
let mapper = GraphQLSelectionSetMapper<Query.Data>()
130166
let dependencyTracker = GraphQLDependencyTracker()
131167

@@ -152,7 +188,7 @@ public final class ApolloStore {
152188
fileprivate let cache: NormalizedCache
153189
fileprivate let cacheKeyForObject: CacheKeyForObject?
154190

155-
fileprivate lazy var loader: DataLoader<CacheKey, Record?> = DataLoader(self.cache.loadRecords)
191+
fileprivate lazy var loader: DataLoader<CacheKey, Record?> = DataLoader(self.cache.loadRecordsPromise)
156192

157193
init(cache: NormalizedCache, cacheKeyForObject: CacheKeyForObject?) {
158194
self.cache = cache
@@ -168,8 +204,12 @@ public final class ApolloStore {
168204
return try execute(selections: type.selections, onObjectWithKey: key, variables: variables, accumulator: mapper).await()
169205
}
170206

171-
public func loadRecords(forKeys keys: [CacheKey]) -> Promise<[Record?]> {
172-
return cache.loadRecords(forKeys: keys)
207+
public func loadRecords(forKeys keys: [CacheKey],
208+
callbackQueue: DispatchQueue = .main,
209+
completion: @escaping (Result<[Record?], Error>) -> Void) {
210+
self.cache.loadRecords(forKeys: keys,
211+
callbackQueue: callbackQueue,
212+
completion: completion)
173213
}
174214

175215
private final func complete(value: Any?) -> ResultOrPromise<JSONValue?> {
@@ -249,7 +289,7 @@ public final class ApolloStore {
249289

250290
_ = try executor.execute(selections: selections, on: object, withKey: key, variables: variables, accumulator: normalizer)
251291
.flatMap {
252-
self.cache.merge(records: $0)
292+
self.cache.mergePromise(records: $0)
253293
}.andThen { changedKeys in
254294
if let didChangeKeysFunc = self.updateChangedKeysFunc {
255295
didChangeKeysFunc(changedKeys, nil)
@@ -258,3 +298,48 @@ public final class ApolloStore {
258298
}
259299
}
260300
}
301+
302+
internal extension NormalizedCache {
303+
func loadRecordsPromise(forKeys keys: [CacheKey]) -> Promise<[Record?]> {
304+
return Promise { fulfill, reject in
305+
self.loadRecords(
306+
forKeys: keys,
307+
callbackQueue: nil) { result in
308+
switch result {
309+
case .success(let records):
310+
fulfill(records)
311+
case .failure(let error):
312+
reject(error)
313+
}
314+
}
315+
}
316+
}
317+
318+
func mergePromise(records: RecordSet) -> Promise<Set<CacheKey>> {
319+
return Promise { fulfill, reject in
320+
self.merge(
321+
records: records,
322+
callbackQueue: nil) { result in
323+
switch result {
324+
case .success(let cacheKeys):
325+
fulfill(cacheKeys)
326+
case .failure(let error):
327+
reject(error)
328+
}
329+
}
330+
}
331+
}
332+
333+
func clearPromise() -> Promise<Void> {
334+
return Promise { fulfill, reject in
335+
self.clear(callbackQueue: nil) { result in
336+
switch result {
337+
case .success(let success):
338+
fulfill(success)
339+
case .failure(let error):
340+
reject(error)
341+
}
342+
}
343+
}
344+
}
345+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//
2+
// DispatchQueue+Optional.swift
3+
// Apollo
4+
//
5+
// Created by Ellen Shapiro on 8/13/19.
6+
// Copyright © 2019 Apollo GraphQL. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
public extension DispatchQueue {
12+
13+
static func apollo_performAsyncIfNeeded(on callbackQueue: DispatchQueue?, action: @escaping () -> Void) {
14+
if let callbackQueue = callbackQueue {
15+
// A callback queue was provided, perform the action on that queue
16+
callbackQueue.async {
17+
action()
18+
}
19+
} else {
20+
// Perform the action on the current queue
21+
action()
22+
}
23+
}
24+
25+
static func apollo_returnResultAsyncIfNeeded<T>(on callbackQueue: DispatchQueue?, action: ((Result<T, Error>) -> Void)?, result: Result<T, Error>) {
26+
guard let action = action else {
27+
return
28+
}
29+
30+
self.apollo_performAsyncIfNeeded(on: callbackQueue) {
31+
action(result)
32+
}
33+
}
34+
}

Sources/Apollo/InMemoryNormalizedCache.swift

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,35 @@ public final class InMemoryNormalizedCache: NormalizedCache {
44
public init(records: RecordSet = RecordSet()) {
55
self.records = records
66
}
7-
8-
public func loadRecords(forKeys keys: [CacheKey]) -> Promise<[Record?]> {
7+
8+
public func loadRecords(forKeys keys: [CacheKey],
9+
callbackQueue: DispatchQueue?,
10+
completion: @escaping (Result<[Record?], Error>) -> Void) {
911
let records = keys.map { self.records[$0] }
10-
return Promise(fulfilled: records)
12+
DispatchQueue.apollo_returnResultAsyncIfNeeded(on: callbackQueue,
13+
action: completion,
14+
result: .success(records))
1115
}
12-
13-
public func merge(records: RecordSet) -> Promise<Set<CacheKey>> {
14-
return Promise(fulfilled: self.records.merge(records: records))
16+
17+
public func merge(records: RecordSet,
18+
callbackQueue: DispatchQueue?,
19+
completion: @escaping (Result<Set<CacheKey>, Error>) -> Void) {
20+
let cacheKeys = self.records.merge(records: records)
21+
DispatchQueue.apollo_returnResultAsyncIfNeeded(on: callbackQueue,
22+
action: completion,
23+
result: .success(cacheKeys))
1524
}
1625

17-
public func clear() -> Promise<Void> {
18-
records.clear()
19-
return Promise(fulfilled: ())
26+
public func clear(callbackQueue: DispatchQueue?,
27+
completion: ((Result<Void, Error>) -> Void)?) {
28+
self.records.clear()
29+
30+
guard let completion = completion else {
31+
return
32+
}
33+
34+
DispatchQueue.apollo_returnResultAsyncIfNeeded(on: callbackQueue,
35+
action: completion,
36+
result: .success(()))
2037
}
2138
}
Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,31 @@
11
public protocol NormalizedCache {
2-
2+
33
/// Loads records corresponding to the given keys.
4-
/// - returns: A promise that fulfills with an array, with each index containing either the
5-
/// record corresponding to the key at that index or nil if not found.
6-
func loadRecords(forKeys keys: [CacheKey]) -> Promise<[Record?]>
7-
4+
///
5+
/// - Parameters:
6+
/// - keys: The cache keys to load data for
7+
/// - callbackQueue: [optional] An alternate queue to fire the completion closure on. If nil, will fire on the current queue.
8+
/// - completion: A completion closure to fire when the load has completed. If successful, will contain an array. Each index will contain either the record corresponding to the key at the same index in the passed-in array of cache keys, or nil if that record was not found.
9+
func loadRecords(forKeys keys: [CacheKey],
10+
callbackQueue: DispatchQueue?,
11+
completion: @escaping (Result<[Record?], Error>) -> Void)
12+
813
/// Merges a set of records into the cache.
9-
/// - returns: A promise that fulfills with a set of keys corresponding to *fields* that have
10-
/// changed (i.e. QUERY_ROOT.Foo.myField). These are the same type of keys as are
11-
/// returned by RecordSet.merge(records:).
12-
func merge(records: RecordSet) -> Promise<Set<CacheKey>>
14+
///
15+
/// - Parameters:
16+
/// - records: The set of records to merge.
17+
/// - callbackQueue: [optional] An alternate queue to fire the completion closure on. If nil, will fire on the current queue.
18+
/// - completion: A completion closure to fire when the merge has completed. If successful, will contain a set of keys corresponding to *fields* that have changed (i.e. QUERY_ROOT.Foo.myField). These are the same type of keys as are returned by RecordSet.merge(records:).
19+
func merge(records: RecordSet,
20+
callbackQueue: DispatchQueue?,
21+
completion: @escaping (Result<Set<CacheKey>, Error>) -> Void)
1322

1423
// Clears all records
15-
func clear() -> Promise<Void>
24+
///
25+
/// - Parameters:
26+
/// - callbackQueue: [optional] An alternate queue to fire the completion closure on. If nil, will fire on the current queue.
27+
/// - completion: [optional] A completion closure to fire when the clear function has completed.
28+
func clear(callbackQueue: DispatchQueue?,
29+
completion: ((Result<Void, Error>) -> Void)?)
1630
}
31+

0 commit comments

Comments
 (0)