Thank you Quinn! Yes, the reason is that I use the FileManagerItemModel for drag & drop operations with a custom preview. For .onDrag(data, preview), I need to pass the data parameter as a closure returning an NSItemProvider. So in the closure, I create the NSItemProvider as NSItemProvider(object: item), where item is of type FileManagerItemModel.
And in order to do that, the FileManagerItemModel needs to implement NSItemProviderWriting. And that requires the FileManagerItemModel to be derived from NSObject (and to be a class instead of a struct too).
So, now I manage equality in FileManagerItemModel like so:
class FileManagerItemModel: NSObject, NSCopying, NSItemProviderReading, NSItemProviderWriting, Identifiable, Codable, Transferable {
// MARK: - Identification. The items are identified by type, rootURL and filePath.
// MARK: Swift Identifiable and Equatable
var id: URL
static func == (lhs: FileManagerItemModel, rhs: FileManagerItemModel) -> Bool {
lhs.id == rhs.id
}
// MARK: ObjC equality
override var hash: Int {
id.absoluteString.hashValue
}
override func isEqual(_ object: Any?) -> Bool {
guard let other = object as? FileManagerItemModel else { return false }
return self.hashValue == other.hashValue
}
...
init(type: FileManagerItemType, rootURL: URL, filePath: String) {
self.type = type
self.rootURL = rootURL
self.filePath = filePath
self.id = FileManagerItemModel.normalizedFileURL(type: type,
rootURL: rootURL,
filePath: filePath)
}
...
private static func normalizedFileURL(type: FileManagerItemType, rootURL: URL, filePath: String) -> URL {
switch type {
case .genericFolder(_):
return rootURL.appending(folderPathString: filePath)
default:
return rootURL.appending(filePathString: filePath) ?? rootURL
}
}
...
}
I'm sure this will not be the final implementation, but it works for now.
Note that this is a pet project that I am using to learn things, while at the same time having something useful in the end (a two-window file manager, a bit like Norton Commander). So my design decisions are not always the best. My main objective is to find out what works and what does not.
In-app multi-item drag&drop between two different Views is currently a bit of a difficult thing, when using SwiftUI. Let's just say that I have been learning a lot. ;)