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"