I am encountering a strange issue. I have a class that manages a selection of generic items T in an Array. It's a work in progress, but I'l try to give a gist of the setup.
class FileManagerItemModel: NSObject, Identifiable, Codable, NSCopying, Transferable, NSItemProviderReading, NSItemProviderWriting {
var id: URL
static func == (lhs: FileManagerItemModel, rhs: FileManagerItemModel) -> Bool {
lhs.fileURL == rhs.fileURL
}
var fileURL: URL {
FileManagerItemModel.normalizedFileURL(type: type,
rootURL: rootURL,
filePath: filePath)
}
init(type: FileManagerItemType, rootURL: URL, fileURL: URL) {
self.type = type
self.rootURL = rootURL
self.filePath = FileManagerItemModel.filePathRelativeToRootURL(fileURL: fileURL, rootURL: rootURL) ?? "[unknown]"
self.id = FileManagerItemModel.normalizedFileURL(type: type,
rootURL: rootURL,
filePath: filePath)
}
}
The class that manages the selection of these FileManagerItemModels is like so:
@Observable
class MultiSelectDragDropCoordinator<T: Hashable>: ObservableObject, CustomDebugStringConvertible {
private(set) var multiSelectedItems: [T] = []
func addToSelection(_ item: T) {
if !multiSelectedItems.contains(where: { $0 == item }) {
multiSelectedItems.append(item)
}
}
...
}
My issue is that the check if !multiSelectedItems.contains(where: { $0 == item })
in func addToSelection
fails. The if
is always executed, even if multiSelectedItems
contains the given item.
Now, my first thought would be to suspect the static func ==
check. But that check works fine and does what it should do. Equality is defined by the whole fileURL.
So, the if
should have worked. And If I put a breakpoint in func addToSelection
on the if
, and type po multiSelectedItems.contains(where: { $0 == item })
in the debug console, it actually returns true if the item is in multiSelectedItems. And it properly return false
if the item is not in multiSelectedItems
.
Still, if I then continue stepping through the app after the breakpoint was hit and I confirmed that the contains
should return true
, the app still goes into the if, and adds a duplicate item.
I tried assigning to a variable, I tried using a function and returning the true/false. Nothing helps.
Does anyone have an idea on why the debugger shows one (the correct and expected) thing but the actual code still does something different?
Is there a reason for your FileManagerItemModel
class to subclass NSObject
?
I ask because the concept of equality is expressed differently in Swift and Objective-C, and NSObject
subclasses have to follow the Objective-C rules. So, the correct fix here depends on whether you need to be an NSObject
subclass or not.
In summary:
-
In Swift, equality is handled by two protocols: Equatable and Hashable.
-
OTOH, all
NSObject
subclasses are automatically equatable and hashable based pointer equality. If you want different behaviour, override the default implementation of theisEqual(_:)
method and thehash
property getter.
There’s a great article, Adopting Common Protocols that goes into this in more depth.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"