Summary
Summary:
It appears that equality and hasing is inconsistent between model built in memory versus models hydrated from the cahe.
Details:
Here’s a tiny example, using some small object defined in our schema:
type FolderItemConnectionEdge implements EdgeInterface {
cursor: String
node: Item!
}
And here’s a fragment using the above type
fragment folderItemEdge on FolderItemConnectionEdge @apollo_client_ios_localCacheMutation {
id: cursor
}
Creating 2 instances of the semantically same FolderItemEdge model in memory (e.g. FolderItemEdge(id: "File:15"), and the instances behave has expected. If the id's are the same the instances are considered equal. However, if you read an instance out of The Graph/cache, that instance will not equate with the in memory instances.
Version
1.22
Steps to reproduce the behavior
So here’s some swift code. First confirming that in-memory objects equate/hash as expected
let e1 = FolderItemEdge(id: "File:15")
let e2 = FolderItemEdge(id: "File:15")
print(e1 == e2) // -> true
var uniqueEdges = Set<FolderItemEdge>([])
print("mergeEdges (e1): \(uniqueEdges.insert(e1))")
print("mergeEdges (e2): \(uniqueEdges.insert(e2))")
// uniqueEdges only has 1 entry, since both e1 and e2 are the same
And now I save an instance to The Graph. And read it back out... things are broken.
let key = "FolderItemConnectionEdge:File:15"
// Write the `FolderItemEdge(id: "File:15")` object to The Graph
apolloClient.store.withinReadWriteTransaction { transaction in
do {
try transaction.write(selectionSet: e1, withKey: key)
} catch {
}
}
// Read the `FolderItemEdge(id: "File:15")` out of The Graph
apolloClient.store.withinReadTransaction { transaction in
let d1 = try? transaction.readObject(ofType: FolderItemEdge.self, withKey: key)
if let d1 {
// And evidently... they are now different - they don't hash well together!
print("equality: \(e1 == d1)") // This fails
print("hash (d1): \(uniqueEdges.insert(d1))") // `d1` is added to the set!
print("set count: \(uniqueEdges.count)") // and the set contains 2 items (not 1)!
}
}
The equality check fails. e1 != d1???
Digging deeper in the debug I found this:
po lhs.__data._data["__typename"] == rhs.__data._data["__typename"]
==> true
po lhs.__data._data["id"] == rhs.__data._data["id"]
==> false
So something about the id's is different
Here’s a dump of the DataDicts:
(lldb) p e1.__data._data
([String : AnyHashable]) 2 key/value pairs {
[0] = {
key = "__typename"
value = {
_box = (_baseHashable = "FolderItemConnectionEdge")
}
}
[1] = {
key = "id"
value = {
_box = {
_baseHashable = "File:15"
}
}
}
}
(lldb) p d1.__data._data
([String : AnyHashable]) 2 key/value pairs {
[0] = {
key = "__typename"
value = {
_box = (_baseHashable = "FolderItemConnectionEdge")
}
}
[1] = {
key = "id"
value = {
_box = (_baseHashable = "File:15")
}
}
}
I noted that the “value” of the “id” key prints slightly differently. Here’s e1 which spans multiple lines with brackets { and }
_box = {
_baseHashable = "File:15"
}
vs all on 1 line for d1 using parens.
_box = (_baseHashable = "File:15")
Logs
Anything else?
No response
Summary
Summary:
It appears that equality and hasing is inconsistent between model built in memory versus models hydrated from the cahe.
Details:
Here’s a tiny example, using some small object defined in our schema:
And here’s a fragment using the above type
Creating 2 instances of the semantically same
FolderItemEdgemodel in memory (e.g.FolderItemEdge(id: "File:15"), and the instances behave has expected. If the id's are the same the instances are considered equal. However, if you read an instance out of The Graph/cache, that instance will not equate with the in memory instances.Version
1.22
Steps to reproduce the behavior
So here’s some swift code. First confirming that in-memory objects equate/hash as expected
And now I save an instance to The Graph. And read it back out... things are broken.
The equality check fails. e1 != d1???
Digging deeper in the debug I found this:
So something about the
id'sis differentHere’s a dump of the DataDicts:
I noted that the “value” of the “id” key prints slightly differently. Here’s e1 which spans multiple lines with brackets { and }
vs all on 1 line for d1 using parens.
Logs
Anything else?
No response