NSItemProvider loadItem method returns _NSItemProviderSandboxedResource instead of URL

Just found a solution for this issue.

Prior to iOS 13 when calling

attachment.loadItem(forTypeIdentifier: "com.apple.pkpass" as String, options: nil) data, error in

data could be downcasted also to an URL as follows

if let pkPassURL = data as? URL

As described in the question above, in iOS 13 this is not possible any longer.

Instead, it is now required to call loadItem with "public.file-url" as type identifier:

attachment.loadItem(forTypeIdentifier: "public.file-url" as String, options: nil) { (data, error) in

In the specific case of PKPass items, I have noticed that when sharing them

  • from the Wallet app the attachment will conform only to "com.apple.pkpass"
  • from e.g. Mail app the attachment will conform to both "com.apple.pkpass" and "public.file-url"

So following code can be used to deal with both cases:

if let inputItems = self.extensionContext?.inputItems,
   let inputItem = inputItems.first as? NSExtensionItem,
   let attachments = inputItem.attachments,
   let attachment = attachments.first,
   attachment.hasItemConformingToTypeIdentifier("com.apple.pkpass" as String) {
    if attachment.hasItemConformingToTypeIdentifier("public.file-url" as String) {
        // extension is being called e.g. from Mail app
        attachment.loadItem(forTypeIdentifier: "public.file-url" as String, options: nil) { (data, error) in 
            if let sourcePKPassURL = data as? URL {
                //handle url here
            }
        }
    } else {
        // extension is being called from Wallet app
        attachment.loadItem(forTypeIdentifier: "com.apple.pkpass" as String, options: nil) { (data, error) in
            if let pkPassData = data as? Data,
               let pkPass = try? PKPass(data: pkPassData) {
               // handle pkPass here
            }
        }
    }
}

This works for both iOS 12 and iOS 13.