Tracking down the source of mutation causing view updates

Two questions here:

1) I have a fairly complex SwiftUI app which is in certain situations updating its views far more than I expect it to be. It seems that something in the data model is appearing (to the framework) to be changing much more often than I expect it to. Are there any good tools for tracking this down?

2) One thing I tried is to use Combine to subscribe to all the publishers I suspect of being possible culprits, and print a message from each one's sink(receiveValue:). Sure enough, I've found one that is publishing unexpected updates... but I'm not sure why. The data in question is a struct that contains a dictionary (which is used as a cache of previously computed values), and has a member function for retrieving a value. This function is marked as mutating because it sometimes needs to add to the cache dictionary. As long as the requested key is present in the dictionary cache, however, it returns an existing value. The problem I am seeing is that even when only asked for the same value over and over again, it publishes updates.

So is calling a mutating function enough to trigger a published update, even if it doesn't actually change the struct? If so, how can I avoid this without externalizing the fact that there is a cache and that the client of the object must first check the cache and then do the mutation if it didn't find what it wanted?


Just as an FYI to any readers... my "solution" to this problem was to provide a function on my object (i.e. an instance of a class) which owns the state data (i.e. an instance of a struct). The struct provides 2 functions, a non-mutating one which gets cached data via a key, and a second which is marked mutating. The second one checks the cache and returns the cached data (without modifying state), but if it is not in the cache it computes it and adds it to the cache (thereby modifying state). Only the owning object ever calls these two functions, and it does so from the method it exposes, first trying the non-mutating method and only calling the mutating method if the requested data was not in the cache.

This is ugly and inefficient (multiple mutex locks are required), but appears to be forced upon me by the language / framework design. I would like to have a better solution.

Code Block
struct D {
func get_cached(key: Int) -> ValueType { ... }
mutating func create_cached(key: Int) -> ValueType { ... } // always causes a SwiftUI update
}
class A {
var data : D
func get_value(key: Int) {
if let value = data.get_cached(key) {
return value
}
else {
return data.create_cached(key)
}
}


Tracking down the source of mutation causing view updates
 
 
Q