apollo-ios-dev
apollo-ios-dev copied to clipboard
Replace ApolloStoreSubscriber didChangeKeys with ApolloStore.Activity…
The following changes are to support the need to hook up data residing in separate stores into the life-cycle of the data in ApolloStore. This PR represents 1 of 2 ways I considered accomplishing this.
- Implement
NormalizeCache
by wrapping another implementation ofNormalizedCache
and forwarding calls to it. - A more robust
ApolloStoreSubsciber
, i.e. this PR
I decided that I was submitting 2 as a PR because I believe the interface is an improvement to what is currently in main (the didChangeKeys subscriber method). The didChangeKeys call follows a call to cache.merge(records:)
, which is covered in this PR's implementation as the following:
public func store(_ store: Apollo.ApolloStore,
activity: Apollo.ApolloStore.Activity,
contextIdentifier: UUID?) throws {
if case .did(perform: .merge, outcome: .changedKeys(let changedKeys)) = activity {
self.store(store, didChangeKeys: changedKeys, contextIdentifier: contextIdentifier)
}
}
Example:
final class MyOtherStoreApolloStoreSubscriber {
private class UnsubscribeRef {
private let _unsubscribe: () -> Void
public init(unsubscribe: @escaping () -> Void) {
_unsubscribe = unsubscribe
}
deinit {
_unsubscribe()
}
}
private lazy var unsubscribeRefs = [String: UnsubscribeRef]()
private func shouldSubscribeToMyOtherStore(for record: Apollo.Record) -> Bool {
/* boolean to determine whether or not you should subscribe to my other store for the provided record */
return unsubscribeRefs[record.key] == nil
}
private func subscribeToMyOtherStore(_ otherStoreListener: () -> Void) -> () -> Void {
/* stuff to subscribe to my other store in here */
return { /* stuff to unsubscribe from my other store in here, weak self please! */ }
}
private func syncMyOtherStoreWith(store: ApolloStore, records: [Apollo.CacheKey: Apollo.Record]) throws {
for (key, record) in records {
if shouldSubscribeToMyOtherStore(for: record) {
unsubscribeRefs[key] = UnsubscribeRef(unsubscribe: subscribeToMyOtherStore({
/* stuff to handle my other store changes in here */
}))
}
}
}
}
extension MyOtherStoreApolloStoreSubscriber: ApolloStoreSubscriber {
public func store(_ store: Apollo.ApolloStore, didChangeKeys changedKeys: Set<Apollo.CacheKey>, contextIdentifier: UUID?) {
/* not implemented, this is now deprecated */
}
public func store(_ store: Apollo.ApolloStore, activity: Apollo.ApolloStore.Activity, contextIdentifier: UUID?) throws {
switch activity {
case .will(perform: .merge(records: let records)):
try syncMyOtherStoreWith(store: store, records: records.storage)
case .did(perform: .loadRecords, outcome: .records(let records)):
try syncMyOtherStoreWith(store: store, records: records)
case .did(perform: .removeRecord(for: let key), outcome: .success):
unsubscribeRefs.removeValue(forKey: key)
case .did(perform: .removeRecords(matching: let pattern), outcome: .success):
for key in unsubscribeRefs.keys {
if key.range(of: pattern, options: .caseInsensitive) != nil {
unsubscribeRefs.removeValue(forKey: key)
}
}
case .did(perform: .clear, outcome: .success):
unsubscribeRefs.removeAll()
default:
return
}
}
}