9

In one of the apps I develop, I use a share extension to import Wallet passes (PKPass).

On iOS 13 (including the latest beta 8), when calling the share extension from within the iOS Mail app, the extension is not receiving the data in the expected format (URL).

This is the relevant snippet from the share extension’s ShareViewController:

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){
    attachment.loadItem(forTypeIdentifier: "com.apple.pkpass" as String, options: nil) { data, error in
        print ("data: \(String(describing: data))")
}

On iOS 12 (latest version) this works fine also for an attachment in the iOS Mail app (in addition e.g. to a file in the Files app); data holds an optional URL. The print statement above shows as follows in the console:

data: Optional(file:///var/mobile/Library/Mail/8EF174CF-68B9-414E-A166-D04C9DBE020E/INBOX.imapmbox/Attachments/13846/2/Attachment-1.pkpass)

On iOS 13 (beta 8), in the iOS Mail app, data holds an optional _NSItemProviderSandboxedResource. The print statement above shows as follows in the console:

data: Optional(<_NSItemProviderSandboxedResource: 0x2839aa9e0>)

This seems to affect only the Mail app. In the Files app data holds - as expected - an URL.

Is this a bug (actually I have already reported this using Feedback Assistant back on beta 4) or some new security feature introduced by iOS 13? How can I access the attachment's url/data in this case?

mmklug
  • 2,252
  • 2
  • 16
  • 31

3 Answers3

8

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.

mmklug
  • 2,252
  • 2
  • 16
  • 31
1

I'm getting the same problem in Apple Mail attachments since iOS 13. Also checking for UTI before and loading that type doesn't work from the code below.

if let item = extensionContext?.inputItems.first as? NSExtensionItem {
        if let itemProvider = item.attachments?.first {
            let type = itemProvider.registeredTypeIdentifiers
            let uti = type.first!
            if itemProvider.hasItemConformingToTypeIdentifier(uti) {
                itemProvider.loadItem(forTypeIdentifier:uti, options: nil, completionHandler: { (item, error) -> Void in
                    print(item) //<_NSItemProviderSandboxedResource: 0x2839aa9e0>
                })
            }
        }}
Tobe
  • 508
  • 5
  • 14
  • 1
    My understanding is that currently from Mail app you must work with the URL ("public.file-url") instead of the PKPass data ("com.apple.pkpass"), as loadItem for the latter is returning the (undocumented) _NSItemProviderSandboxedResource. Your code above always calls loadItem for the PKPass (type.first == "com.apple.pkpass") and therefore is not working from the Mail app. The code published in my answer tries to get the URL if it's available (e.g. in the Mail app). When it's not available (e.g. Wallet app), it cals loadItem for "com.apple.pkpass", which there is working as expected. – mmklug Sep 28 '19 at 08:11
  • thanks, explicitly checking for kUTTypeFileURL in my case works for me – Tobe Sep 30 '19 at 13:51
0

I had faced the same bug , looks like I got a solution now

Try using attachments.last instead of .first

Looks like Apple returns multiple types for Mail app attachments.

  • Unfortunately this is not a general solution; in my case attachments.count == 1 so .last and .first are the same item. – mmklug Sep 25 '19 at 11:11
  • Can you try accessing ```provider.registeredTypeIdentifiers.``` and then check the types rather than hardcoding? Where provider is ```inputItem.attachments?.first``` Let me know if this works. – Roshan Sequeira Sep 27 '19 at 05:07