Skip to content
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

removeObject(for:) is not updating Query Watchers #3493

Open
majda107 opened this issue Dec 30, 2024 · 3 comments
Open

removeObject(for:) is not updating Query Watchers #3493

majda107 opened this issue Dec 30, 2024 · 3 comments
Labels
question Issues that have a question which should be addressed

Comments

@majda107
Copy link

Question

Hello Apollo-iOS team,

Is there a built-in mechanism in Apollo-iOS to automatically propagate data to a query watcher subscription when the underlying cached data is modified? Alternatively, is there a way to make the query watcher automatically re-fetch data from the server when part of the cached response is evicted or updated?

Here’s an example of what I’m trying to achieve:

I’m building a simple post ↔ comments mechanism. I have a GraphQL object Comment with a unique id identifier, and I’ve defined a custom cache key for it like so:

public static func cacheKeyInfo(for type: ApolloAPI.Object, object: ApolloAPI.ObjectData) -> CacheKeyInfo? {
    switch type {
    case Objects.Comment:
        return try? CacheKeyInfo(jsonValue: object["id"])
    default:
        return nil
    }
}

I also have a CommentsQuery(postId) that fetches all comments for a given post. When the UI modifies or deletes a comment, I want to evict this comment from the cache:

Network.shared.apolloClient.store.withinReadWriteTransaction { transaction in
    try? transaction.removeObject(for: "Comment:\(comment.id)")
}

Alternatively, I might modify the comment using transaction.updateObject(). However, neither of these methods seem to trigger the query watcher to re-run, even when the cached data associated with the query has changed.

I’ve tried experimenting with different fetch policies, but none of them seem to produce the desired behavior.

For example:

  • When a cached object is evicted or updated, I would like the query watcher to automatically re-fetch the data from the server.
  • Ideally, I’d like the watcher to remain "live" and responsive to changes in both the cache and the server.
    I know that the Apollo Web client offers a nextFetchPolicy mechanism, which allows the client to first fetch data from the server and then listen for live updates from the cache. Is there a similar feature or recommended approach in Apollo-iOS to achieve this behavior?

PS: I understand that I can manually trigger updates by using the watcher.refetch() call to fetch new data. However, I would prefer to leverage the built-in mechanisms in Apollo-iOS to handle this more elegantly, especially for more complex use cases. For example, the evicted comment might appear in multiple parts of the UI - such as a notifications section or a "recent comments" list - and I want to propagate updates to all affected parts of the app automatically, without manually managing each instance. This level of automation would help maintain consistency and reduce boilerplate code across the application.

Thank you for your guidance!

@majda107 majda107 added the question Issues that have a question which should be addressed label Dec 30, 2024
@AnthonyMDev
Copy link
Contributor

Sorry this question fell through the cracks @majda107. It was posted during the holidays and we must have just missed it.

What you're asking for is definitely an intended use of query watchers. Looking at our code now, I'm seeing that the removeObject(for key:) function is not calling the updateChangedKeysFunc. We can definitely get that fixed. Thanks for bringing it to our attention.

But that doesn't explain why using updateObject() is not working for you, since that function is calling updateChangedKeysFunc. I'd like to get more information from you on how you were using updateObject() so we can fix that use case.

@AnthonyMDev AnthonyMDev changed the title Auto-propagating Cache Changes to Query Watcher in Apollo-iOS removeObject(for:) is not updating Query Watchers Jan 17, 2025
@AnthonyMDev
Copy link
Contributor

AnthonyMDev commented Jan 17, 2025

Looking into implementing the fix for the removeObject(for key:). It is going to require a change to the NormalizedCache protocol, so that it returns the cache keys of the removed records.

The watcher is currently keeping track of the keys of each field that is watched, but not the actual entity itself. So the cache needs to return a list of the actual cache keys for each of the fields that are being removed for us to update watchers. This probably isn't ideal, but would require a more involved rewrite of the entire query watcher and caching system to solve right now.

This will be a major breaking change, and due to that, it is probably better left for the 2.0 version.

@AnthonyMDev AnthonyMDev added this to the Next Major Release milestone Jan 17, 2025
@majda107
Copy link
Author

majda107 commented Jan 18, 2025

@AnthonyMDev

Thank you for the feedback. I indeed planned to heavily rely on the query watchers specifically for this use case, so I’m quite happy to hear that this issue will be further investigated.

I’m not entirely sure about the update functionality—I’ll look into it in more detail and provide some examples in the next few days.

However, I wanted to ask if there’s any possibility for me to "hotfix" the removeObject(forKey:). I don’t need any other advanced features from Apollo, as my application is relatively simple, but I would like the query watchers to update when a cache object is removed. I’m open to any workaround (eg. fork the project and use my own hotfixed version of the package), as I’m currently "refetching" the watchers at the view model layer using a singleton cache clearance service...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Issues that have a question which should be addressed
Projects
None yet
Development

No branches or pull requests

2 participants