Dragging and Dropping Links on iPad

Apple recently released iOS 11, and one of the headline features is drag and drop support on iPad.

Of course, apps have long been able to implement their own drag and drop features — it’s a major part of how you edit audio in Ferrite, and it makes smaller appearances in other Wooji Juice apps too. Both Grain Science and Mitosynth let you use drag and drop to change the order of special effects, for example.

But now with iOS 11, iPad users can drag and drop between apps, which is super convenient. You can see what that looks like in Ferrite to import audio, archives, images and links.

When two apps already use the same data format, new features of APFS (Apple’s new file system) allow this to happen instantly — while still remaining secure, and without wasting storage space. This is great, and I think it’s one of the reasons Apple waited until iOS 11 before adding the feature.

But apps can also negotiate to find a format that, while it’s not their preferred choice, allows the two to communicate, converting the data “in transit” to something they both understand.

One example of this is when you drag a URL from Safari to another app. Safari includes additional information with the URL, but if you drop it into a plain text editor, it will get converted to just the bare URL. This ensures the widest compatibility and means you can nearly always drag links into any text field.

But apps can do more with links, and I’m going to show you how. In the Ferrite drag and drop video earlier, you may have noticed when you drop a link into the time bar to create a new chapter, Ferrite takes the text of the link as well as the link itself, and uses that as the initial title of the chapter.

Here’s how to do this. I’m assuming you’re already familiar with how to handle drag and drop, and are currently loading the link as plain text. It’s pretty straightforward:

  • You’ll need to import MobileCoreServices if you’re not already.
  • Use loadDataRepresentation(forTypeIdentifier: kUTTypeURL) to retrieve the data from the drag-drop session.
  • When you receive the callback with the data, decode it as a property list, e.g. let plist = try PropertyListSerialization.propertyList(from: data, options: [], format: nil)
  • The result is an array, where the first item is a string containing the URL, and the third item is a dictionary containing metadata. (The second item always seems to be an empty string in my tests, but if you know better, please let me know!)
  • The title is simply stored in the dictionary under the key title.

Note: There may be an “official”, built-in way to decode this information on iOS, but if so, I haven’t yet found it — there are a few API calls that look promising, but don’t pan out. Stuff like NSURL’s init?(pasteboardPropertyList propertyList:ofType:) is Mac-only at this time, init(dataRepresentation:relativeTo:) does not do the right thing at all, object(withItemProviderData:typeIdentifier:) gets the URL but strips out the metadata, etc.

So, I’d recommend programming defensively when you read this data — checking the types, checking the array bounds etc, Swift makes this very easy — so that if it fails, you can cleanly fall back to the old method. I use something like:

Swift

Parsing URL .plists:
guard let plist = plist as? [Any], plist.count >= 3, let urlString = plist[0] as? String, let url = URL(string: urlString) else { throw Errors.unableToLoad } if let meta = plist[2] as? NSDictionary, let title = meta["title"] as? String { return (url, title) } return (url, nil)