Post

Replies

Boosts

Views

Activity

Reply to Drag-and-Drop from macOS Safari to NSItemProvider fails due to URL not being a file:// URL
(Re: previous answers - skip if you haven't followed the saga) Adding some context for the answer. I added AppKit specifically because it's not UIKit, as I didn't want an answer for iOS or ipadOS, but macOS. The only SwiftUI line is the onDrag, that I faithfully added to the line as the "caller", but I didn't think I'd have a different DropHandler for macOS and ipadOS, the culprit seemingly being the onDrag SwiftUI part that tries to give me only the "good thing" on macOS, but somehow is unusable. As I repeated, I wanted to quickly iterate and used the hacks instead of trying to use the Async versions, where some versions aren't actually available. The final code will be in Async, thank you. My first SwiftUI app was with GCD because there wasn't an alternative back then. There's one now. But I'm also butting heads with SwiftData, where models cannot be Sendable, but Async are also required to be sendable, so I've had a lot of "fun" on this topic. With the nearly 200 trials I had to do in order to make it work, I wasn't about to create good code only to suit the taste of Internet good will helpers, I'm sure you'll agree with me. So. I did another 8 hours on this, and I was able to do it properly! (/snip) The answer lies in using the .onDrop with the .image type, where it will magically send me the Drag & Drops where images are accessible. But then, to actually ignore the provider, and do the work yourself from the .drag pasteboard!! In essence, the previous piece of code (still not asynchronous- that's my next work) got trimmed down on macOS only to do things through the same code I'm using for pasting things. private func handleItemProviders(_ itemProviders: [NSItemProvider]) -> Bool { guard let itemProvider = itemProviders.first else { return false } let registeredContentTypes = itemProvider.registeredContentTypes print("ContentTypes: \(registeredContentTypes)") guard let contentType = registeredContentTypes.first else { return false } #if canImport(AppKit) handlePasteboard(NSPasteboard(name: .drag)) #else [snip] And the handlePasteboard operation is a long-winded function that processes whatever the pasteboard has in it. First by optionally getting an URL (file or web) var url: URL? let urlType = item.availableType(from: Self.supportedPasteboardURLTypes) if let urlType, let urlString = item.string(forType: urlType) { url = URL(string: urlString) Then by trying to get the data from the pasteboard itself (used in case of a drag-and-drop from Photos or Preview, or even Safari) if let imageType = item.availableType(from: Self.supportedPasteboardTypes) { guard let data = item.data(forType: imageType) else { Then, if not functional, by trying to get the image from the URL through a coordinator (used in case of a drag-and-drop of an actual image from a file) } else if let url { if urlType == .fileURL { let queue = OperationQueue() queue.underlyingQueue = .global(qos: .utility) let intent = NSFileAccessIntent.readingIntent(with: url, options: .withoutChanges) let coordinator = NSFileCoordinator() coordinator.coordinate(with: [intent], queue: queue) { error in Finally, when trying to drag-and-drop an actual link to an image, by trying to retrieve it with a downloadTask (don't forget the incoming network client capability!) } else if urlType == .URL { let session = URLSession.shared.downloadTask(with: url) { url, response, error in In the last part, I check for the mime type for image, and download the URL if working. So this works perfectly on macOS, under all circumstances. For iOS, a piece of code similar to what was written in the original question actually works, since the actual image got retrieved by Safari and the system sends back a local device URL, and not a https link. So... Problem averted for now! And the code for the pasteboard handling can be reused with .general for a regular paste in SwiftUI. .onDrop(of: Self.supportedDropItemUTIs, delegate: self) .contextMenu { Button(action: onPasteButton) { Text("Paste") Image(systemName: "doc.on.clipboard") } } with public func onPasteButton() { handlePasteboard(.general) }
Topic: UI Frameworks SubTopic: AppKit Tags:
Sep ’25
Reply to Drag-and-Drop from macOS Safari to NSItemProvider fails due to URL not being a file:// URL
Ty @Etresoft, for the answer. Yay comments being borked and not giving a notification is so fun. So, I comtinued my search. 3rd day on this. As far as I can tell, and I don't know if that's a bug in the latest Beta or always was like that (or a case of it doesn't work on my machine), but it seems the .onDrop SwiftUI operation will only return one single element in passed NSItemProvider. So it's either one or zero representation. I can put everything in the of: [UTType], and it'll coalesce it into a single internal type. This means, at this point, that unless I do loops and hoops, it's effectively a bug in the SwiftUI behaviour, where it will not allow me to drag and drop from Safari, since the URL is not file-based, and SwiftUI seemingly coded their things for file-based URLs, not web URLs. That's 100% the opposite of PH photo providers, where it'll happily send me everything. Or the paste operation that's functional because it provides me the NSPasteboard directly through the contextual menu. Because of that, once I am in the delegate, it's truly "too late" as the contents of the Drag pasteboard already got processed by the system. I tried asking for text, item, data, url, everything, but the only thing the onDrop sends me back is the unretrievable image (even asking for .url returns me the public.jpeg). So I will file a bug report and merely move on. I'm sure I can do a ViewRepresentation and do the code in Obj-C, and actually retrieve the thing myself, but TBH, I don't mind much, and hope Apple fixes it someday. I learned the VERY hard way (during SwiftUI's initial developer previews) you have to follow the intents of Apple, not try to do it how you envisioned it. If it's not how they made it to work, it won't work, or you'll have to backtrack the next year's version.
Topic: UI Frameworks SubTopic: AppKit Tags:
Sep ’25
Reply to @Model and Encodable cause the error Ambiguous use of 'setValue(for:to:)'
This is not the first time I am getting such issue. And it's very annoying. But here is my solution. First, I add the following public disambiguation functions (most are useless, provided for completion, we're there, why not make them): public extension BackingData { func setPersistentModelValue<Value>(forKey key: KeyPath<Self.Model, Value>, to newValue: Value) where Value : PersistentModel { setValue(forKey: key, to: newValue) } func setPersistentModelValue<Value>(forKey key: KeyPath<Self.Model, Value?>, to newValue: Value?) where Value : PersistentModel { setValue(forKey: key, to: newValue) } func setRelationshipCollectionValue<Value, OtherModel>(forKey key: KeyPath<Self.Model, Value>, to newValue: Value) where Value : RelationshipCollection, OtherModel == Value.PersistentElement { setValue(forKey: key, to: newValue) } func getPersistentModelValue<Value>(forKey key: KeyPath<Self.Model, Value>) -> Value where Value : PersistentModel { getValue(forKey: key) } func getPersistentModelValue<Value>(forKey key: KeyPath<Self.Model, Value?>) -> Value? where Value : PersistentModel { getValue(forKey: key) } func getRelationshipCollectionValue<Value, OtherModel>(forKey key: KeyPath<Self.Model, Value>) -> Value where Value : RelationshipCollection, OtherModel == Value.PersistentElement { getValue(forKey: key) } } public extension PersistentModel { func setPersistentModelValue<Value>(forKey key: KeyPath<Self, Value>, to newValue: Value) where Value : PersistentModel { setValue(forKey: key, to: newValue) } func setPersistentModelValue<Value>(forKey key: KeyPath<Self, Value?>, to newValue: Value?) where Value : PersistentModel { setValue(forKey: key, to: newValue) } func setRelationshipCollectionValue<Value, OtherModel>(forKey key: KeyPath<Self, Value>, to newValue: Value) where Value : RelationshipCollection, OtherModel == Value.PersistentElement { setValue(forKey: key, to: newValue) } func getPersistentModelValue<Value>(forKey key: KeyPath<Self, Value>) -> Value where Value : PersistentModel { getValue(forKey: key) } func getPersistentModelValue<Value>(forKey key: KeyPath<Self, Value?>) -> Value? where Value : PersistentModel { getValue(forKey: key) } func getRelationshipCollectionValue<Value, OtherModel>(forKey key: KeyPath<Self, Value>) -> Value where Value : RelationshipCollection, OtherModel == Value.PersistentElement { getValue(forKey: key) } } Then, for every type, I add up a generic getter/setter (only the setter is used in the initial value, but hey, I'm there!) for my precise type I want. To use the initial Book example: public extension BackingData { func setValue(forKey key: KeyPath<Self.Model, Book>, to newValue: Book) { setPersistentModelValue(forKey: key, to: newValue) } func setValue(forKey key: KeyPath<Self.Model, Book?>, to newValue: Book?) { setPersistentModelValue(forKey: key, to: newValue) } func getValue(forKey key: KeyPath<Self.Model, Book>) -> Book { getPersistentModelValue(forKey: key) } func getValue(forKey key: KeyPath<Self.Model, Book?>) -> Book? { getPersistentModelValue(forKey: key) } } Finally, for every type that has relationships, I add up an extension to the type itself. In initial example, let's assume Trip includes some Book, as Book? or [Book]: extension Trip { func setValue(forKey key: KeyPath<Trip, Book>, to newValue: Book) { setPersistentModelValue(forKey: key, to: newValue) } func setValue(forKey key: KeyPath<Trip, Book?>, to newValue: Book?) { setPersistentModelValue(forKey: key, to: newValue) } func getValue(forKey key: KeyPath<Trip, Book>) -> Book { getPersistentModelValue(forKey: key) } func getValue(forKey key: KeyPath<Trip, Book?>) -> Book? { getPersistentModelValue(forKey: key) } } This relies on the fact the swift compiler first tries non-generic versions of a function. In which case, we created some. Then, it tries to use the generic version, which is ambiguous. So we are providing the exact version it wants first. This pattern can be used for other ambiguous situations, such as the RelationshipCollection, OtherModel setters and getters. I have provided them in the generic functions too.
Nov ’23
Reply to Manage hit test mask in SwiftUI for an Image with transparency
@OOPer I only thought of the code, I don't have the actual code itself, but it mostly boils down to doing a side channel to the traditional Tap, through a tap delegate, and the global coordinates are sent in Environment. Then, they are processed by any object requiring a tap, where the objects are responsible to know if they fall on top of themselves (through Geometry checks), as well as the actual check in the image's pixel, for its colouring (probably be worthwhile to do a 5-tap so it's not too precise). Lastly, the objects need to have a holder to know their respective Z ordering (mine would be in model, as I already know the innate z ordering). The problem is it doesn't actually scale, as every tappable objects would need to listen to this. So an optimization might be to keep track of the global geometries of all the objects in an ordered list, and go down that list through the manager itself, foregoing the entire SwiftUI system itself, then have a callback to the object to say "you got tapped there, want it?". So ... mostly, recreating an entire artificial makeshift slow event passing system on the side of SwiftUI just because... There might be other solutions, but if none exists, I would propose Apple to add a Gesture handler to tell if it can "accept", "drain" or "ignore" a Gesture, alongside local coordinates, and the object could give its blessings. That would solve my issue, as well as allow very complex operations.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Oct ’21
Reply to [PHPicker] Permission error followed by "no such file" error
I had a lot of "fun" finding a solution for this one. As explained by the Frameworks Engineer, the problem lies in Apple's JPEG transcoding. You actually ask for an "Image" representation, and here lies the issue. In the Simulator example images, they are helpful in providing multiple image formats. One includes a ".heic" file, that's not a JPEG. And this is the one that usually borks. My solution is going through all the provided representations for every photo, figure out what it is, and only transcode as a last resort. With this, I haven't had any No such file or directory since then. let supportedRepresentations = [UTType.rawImage.identifier,                                 UTType.tiff.identifier,                                 UTType.bmp.identifier,                                 UTType.png.identifier,                                 UTType.heif.identifier,                                 UTType.heic.identifier,                                 UTType.jpeg.identifier, &#9;                              UTType.webP.identifier,                                 UTType.gif.identifier, ] for representation in supportedRepresentations { &#9;  if result.itemProvider.hasRepresentationConforming(toTypeIdentifier: representation, fileOptions: .init()) {         result.itemProvider.loadInPlaceFileRepresentation(forTypeIdentifier: representation) { (originalUrl, inPlace, error) in
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Apr ’21