-
Notifications
You must be signed in to change notification settings - Fork 731
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Remove Public Promise #709
Changes from all commits
b691926
2fb1335
263e9f9
6b6b859
55295dd
14a4ab4
d0037ba
88b0b52
7e42e4a
18b1d77
7cf841c
fbdb1f7
1fc4b86
0f31624
a1b2450
8f24739
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -50,14 +50,14 @@ public final class ApolloStore { | |
/// 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. | ||
/// | ||
/// - Returns: A promise which fulfills when the Cache is cleared. | ||
public func clearCache() -> Promise<Void> { | ||
return Promise<Void> { fulfill, reject in | ||
queue.async(flags: .barrier) { | ||
self.cacheLock.withWriteLock { | ||
self.cache.clear() | ||
public func clearCache(callbackQueue: DispatchQueue = .main, completion: ((Result<Void, Error>) -> Void)? = nil) { | ||
queue.async(flags: .barrier) { | ||
self.cacheLock.withWriteLock { | ||
self.cache.clearPromise() | ||
}.andThen { | ||
fulfill(()) | ||
} | ||
DispatchQueue.apollo_returnResultAsyncIfNeeded(on: callbackQueue, | ||
action: completion, | ||
result: .success(())) | ||
} | ||
} | ||
} | ||
|
@@ -66,7 +66,7 @@ public final class ApolloStore { | |
return Promise<Void> { fulfill, reject in | ||
queue.async(flags: .barrier) { | ||
self.cacheLock.withWriteLock { | ||
self.cache.merge(records: records) | ||
self.cache.mergePromise(records: records) | ||
}.andThen { changedKeys in | ||
self.didChangeKeys(changedKeys, context: context) | ||
fulfill(()) | ||
|
@@ -87,7 +87,7 @@ public final class ApolloStore { | |
} | ||
} | ||
|
||
public func withinReadTransaction<T>(_ body: @escaping (ReadTransaction) throws -> Promise<T>) -> Promise<T> { | ||
func withinReadTransactionPromise<T>(_ body: @escaping (ReadTransaction) throws -> Promise<T>) -> Promise<T> { | ||
return Promise<ReadTransaction> { fulfill, reject in | ||
self.queue.async { | ||
self.cacheLock.lockForReading() | ||
|
@@ -99,14 +99,32 @@ public final class ApolloStore { | |
self.cacheLock.unlock() | ||
} | ||
} | ||
|
||
public func withinReadTransaction<T>(_ body: @escaping (ReadTransaction) throws -> T) -> Promise<T> { | ||
return withinReadTransaction { | ||
Promise(fulfilled: try body($0)) | ||
|
||
/// Performs an operation within a read transaction | ||
/// | ||
/// - Parameters: | ||
/// - body: The body of the operation to perform. | ||
/// - 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. | ||
/// - completion: [optional] The completion block to perform when the read transaction completes. Defaults to nil. | ||
public func withinReadTransaction<T>(_ body: @escaping (ReadTransaction) throws -> T, | ||
callbackQueue: DispatchQueue? = nil, | ||
completion: ((Result<T, Error>) -> Void)? = nil) { | ||
_ = self.withinReadTransactionPromise { | ||
Promise(fulfilled: try body($0)) | ||
} | ||
.andThen { object in | ||
DispatchQueue.apollo_returnResultAsyncIfNeeded(on: callbackQueue, | ||
action: completion, | ||
result: .success(object)) | ||
} | ||
.catch { error in | ||
DispatchQueue.apollo_returnResultAsyncIfNeeded(on: callbackQueue, | ||
action: completion, | ||
result: .failure(error)) | ||
} | ||
} | ||
|
||
public func withinReadWriteTransaction<T>(_ body: @escaping (ReadWriteTransaction) throws -> Promise<T>) -> Promise<T> { | ||
func withinReadWriteTransactionPromise<T>(_ body: @escaping (ReadWriteTransaction) throws -> Promise<T>) -> Promise<T> { | ||
return Promise<ReadWriteTransaction> { fulfill, reject in | ||
self.queue.async(flags: .barrier) { | ||
self.cacheLock.lockForWriting() | ||
|
@@ -117,15 +135,33 @@ public final class ApolloStore { | |
self.cacheLock.unlock() | ||
} | ||
} | ||
|
||
public func withinReadWriteTransaction<T>(_ body: @escaping (ReadWriteTransaction) throws -> T) -> Promise<T> { | ||
return withinReadWriteTransaction { | ||
Promise(fulfilled: try body($0)) | ||
} | ||
|
||
/// Performs an operation within a read-write transaction | ||
/// | ||
/// - Parameters: | ||
/// - body: The body of the operation to perform | ||
/// - callbackQueue: [optional] a callback queue to perform the action on. Will perform on the current queue if not provided. Defaults to nil. | ||
/// - completion: [optional] a completion block to fire when the read-write transaction completes. Defaults to nil. | ||
public func withinReadWriteTransaction<T>(_ body: @escaping (ReadWriteTransaction) throws -> T, | ||
callbackQueue: DispatchQueue? = nil, | ||
completion: ((Result<T, Error>) -> Void)? = nil) { | ||
_ = self.withinReadWriteTransactionPromise { | ||
Promise(fulfilled: try body($0)) | ||
} | ||
.andThen { object in | ||
DispatchQueue.apollo_returnResultAsyncIfNeeded(on: callbackQueue, | ||
action: completion, | ||
result: .success(object)) | ||
} | ||
.catch { error in | ||
DispatchQueue.apollo_returnResultAsyncIfNeeded(on: callbackQueue, | ||
action: completion, | ||
result: .failure(error)) | ||
} | ||
} | ||
|
||
public func load<Query: GraphQLQuery>(query: Query) -> Promise<GraphQLResult<Query.Data>> { | ||
return withinReadTransaction { transaction in | ||
func load<Query: GraphQLQuery>(query: Query) -> Promise<GraphQLResult<Query.Data>> { | ||
return withinReadTransactionPromise { transaction in | ||
let mapper = GraphQLSelectionSetMapper<Query.Data>() | ||
let dependencyTracker = GraphQLDependencyTracker() | ||
|
||
|
@@ -152,7 +188,7 @@ public final class ApolloStore { | |
fileprivate let cache: NormalizedCache | ||
fileprivate let cacheKeyForObject: CacheKeyForObject? | ||
|
||
fileprivate lazy var loader: DataLoader<CacheKey, Record?> = DataLoader(self.cache.loadRecords) | ||
fileprivate lazy var loader: DataLoader<CacheKey, Record?> = DataLoader(self.cache.loadRecordsPromise) | ||
|
||
init(cache: NormalizedCache, cacheKeyForObject: CacheKeyForObject?) { | ||
self.cache = cache | ||
|
@@ -168,8 +204,12 @@ public final class ApolloStore { | |
return try execute(selections: type.selections, onObjectWithKey: key, variables: variables, accumulator: mapper).await() | ||
} | ||
|
||
public func loadRecords(forKeys keys: [CacheKey]) -> Promise<[Record?]> { | ||
return cache.loadRecords(forKeys: keys) | ||
public func loadRecords(forKeys keys: [CacheKey], | ||
callbackQueue: DispatchQueue = .main, | ||
completion: @escaping (Result<[Record?], Error>) -> Void) { | ||
self.cache.loadRecords(forKeys: keys, | ||
callbackQueue: callbackQueue, | ||
completion: completion) | ||
} | ||
|
||
private final func complete(value: Any?) -> ResultOrPromise<JSONValue?> { | ||
|
@@ -249,7 +289,7 @@ public final class ApolloStore { | |
|
||
_ = try executor.execute(selections: selections, on: object, withKey: key, variables: variables, accumulator: normalizer) | ||
.flatMap { | ||
self.cache.merge(records: $0) | ||
self.cache.mergePromise(records: $0) | ||
}.andThen { changedKeys in | ||
if let didChangeKeysFunc = self.updateChangedKeysFunc { | ||
didChangeKeysFunc(changedKeys, nil) | ||
|
@@ -258,3 +298,48 @@ public final class ApolloStore { | |
} | ||
} | ||
} | ||
|
||
internal extension NormalizedCache { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's probably not worth the trouble, but I wonder if we could use something like Node's There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since I'd eventually like to remove our promises system I'm not entirely convinced making them easier to create is worth it. 😆 |
||
func loadRecordsPromise(forKeys keys: [CacheKey]) -> Promise<[Record?]> { | ||
return Promise { fulfill, reject in | ||
self.loadRecords( | ||
forKeys: keys, | ||
callbackQueue: nil) { result in | ||
switch result { | ||
case .success(let records): | ||
fulfill(records) | ||
case .failure(let error): | ||
reject(error) | ||
} | ||
} | ||
} | ||
} | ||
|
||
func mergePromise(records: RecordSet) -> Promise<Set<CacheKey>> { | ||
return Promise { fulfill, reject in | ||
self.merge( | ||
records: records, | ||
callbackQueue: nil) { result in | ||
switch result { | ||
case .success(let cacheKeys): | ||
fulfill(cacheKeys) | ||
case .failure(let error): | ||
reject(error) | ||
} | ||
} | ||
} | ||
} | ||
|
||
func clearPromise() -> Promise<Void> { | ||
return Promise { fulfill, reject in | ||
self.clear(callbackQueue: nil) { result in | ||
switch result { | ||
case .success(let success): | ||
fulfill(success) | ||
case .failure(let error): | ||
reject(error) | ||
} | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// | ||
// DispatchQueue+Optional.swift | ||
// Apollo | ||
// | ||
// Created by Ellen Shapiro on 8/13/19. | ||
// Copyright © 2019 Apollo GraphQL. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
|
||
public extension DispatchQueue { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this extension need to be public? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, in order for it to be usable in the |
||
|
||
static func apollo_performAsyncIfNeeded(on callbackQueue: DispatchQueue?, action: @escaping () -> Void) { | ||
if let callbackQueue = callbackQueue { | ||
// A callback queue was provided, perform the action on that queue | ||
callbackQueue.async { | ||
action() | ||
} | ||
} else { | ||
// Perform the action on the current queue | ||
action() | ||
} | ||
} | ||
|
||
static func apollo_returnResultAsyncIfNeeded<T>(on callbackQueue: DispatchQueue?, action: ((Result<T, Error>) -> Void)?, result: Result<T, Error>) { | ||
guard let action = action else { | ||
return | ||
} | ||
|
||
self.apollo_performAsyncIfNeeded(on: callbackQueue) { | ||
action(result) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,31 @@ | ||
public protocol NormalizedCache { | ||
|
||
/// Loads records corresponding to the given keys. | ||
/// - returns: A promise that fulfills with an array, with each index containing either the | ||
/// record corresponding to the key at that index or nil if not found. | ||
func loadRecords(forKeys keys: [CacheKey]) -> Promise<[Record?]> | ||
|
||
/// | ||
/// - Parameters: | ||
/// - keys: The cache keys to load data for | ||
/// - callbackQueue: [optional] An alternate queue to fire the completion closure on. If nil, will fire on the current queue. | ||
/// - 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. | ||
func loadRecords(forKeys keys: [CacheKey], | ||
callbackQueue: DispatchQueue?, | ||
completion: @escaping (Result<[Record?], Error>) -> Void) | ||
|
||
/// Merges a set of records into the cache. | ||
/// - returns: A promise that fulfills with 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:). | ||
func merge(records: RecordSet) -> Promise<Set<CacheKey>> | ||
/// | ||
/// - Parameters: | ||
/// - records: The set of records to merge. | ||
/// - callbackQueue: [optional] An alternate queue to fire the completion closure on. If nil, will fire on the current queue. | ||
/// - 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:). | ||
func merge(records: RecordSet, | ||
callbackQueue: DispatchQueue?, | ||
completion: @escaping (Result<Set<CacheKey>, Error>) -> Void) | ||
|
||
// Clears all records | ||
func clear() -> Promise<Void> | ||
/// | ||
/// - Parameters: | ||
/// - callbackQueue: [optional] An alternate queue to fire the completion closure on. If nil, will fire on the current queue. | ||
/// - completion: [optional] A completion closure to fire when the clear function has completed. | ||
func clear(callbackQueue: DispatchQueue?, | ||
completion: ((Result<Void, Error>) -> Void)?) | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This may not be worth it, but maybe a generic wrapper method like Node's
callbackify
could be used to abstract this conversion.