TumblrArchive/ios-extension-issues

Share extensions will *only* show up if they explicitly support *all* of the provided activity items

irace opened this issue · 10 comments

This is a doozy. It’s the most important issue we’ve found, and one that probably deserves a blog post of its own.

Here’s how applications pass data to share extensions:

  • An application configures a UIActivityViewController with an array of “activity items”
  • The activity controller displays the system activities and share extensions that can operate on the types of items provided

Here’s how we think this should work, using the Tumblr app as an example:

  • The user long-presses on a photo
  • We put the image data, the posts’s URL, and maybe a text summary of the post, all in the activity items array
  • We’d expect share extensions that support either image data or URLs or text to all show up in the activity controller

What actually happens is that only share extensions that explicitly support images and URLs and text will show up.

This is a problem, because the simplest way to specify what your extension supports – and by far the best documented – is by adding NSExtensionActivationRule keys like:

`NSExtensionActivationSupportsText` : `YES`

This looks like it would mean “show my extension as long as any of the activity items are text,” but it really means “show my extension as long as there is only one activity item, and it is text.”

Federico Viticci, who at this point has likely used more third-party share extensions than anyone else on the planet, verifies that this is in fact a legitimate problem:

@irace Yup. Been talking to devs to handle exceptions when possible, but I'm getting a lot of failures in several apps.

— Federico Viticci (@viticci) September 5, 2014

@irace Yep. And, that the input passed by an app doesn't match what another app's extension expects and you get all sorts of weird stuff.

— Federico Viticci (@viticci) September 5, 2014
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

Workaround

This negatively affects both app and extension developers. It means that:

  • App developers should only configure their activity controllers with a single activity item. There are a couple of problems with this.
    1. It’s doable, but a pain if, like in Tumblr.app, you want system activities like copying and saving to the Camera Roll to support multiple different types of data.
    2. It’s a huge shame to only export one type of data and limit the number of sharing options that your users will be able to perform.
  • Extension developers should use the more complex (and unfortunately, not very thoroughly documented) predicate syntax to specifically specify an OR relationship. This would look something like:

SUBQUERY(extensionItems, $extensionItem, SUBQUERY($extensionItem.attachments, $attachment, ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.image").@count <= 10).@count >= 1 OR
SUBQUERY(extensionItems, $extensionItem, SUBQUERY($extensionItem.attachments, $attachment, ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.text").@count >= 1).@count >= 1

Some questions, specific to Tumblr:

I used to use the com.tumblr.photo UTI along with the TumblrTags and TumblrCaption metadata to send posts over to tumblr. I was hoping to be able to send a link and and image with the new extension, but it seems that in addition to being one or the other, it also doesn't have support for tags. Are you aware of any way to send metadata for custom fields over (in your case, tags)? Additionally, I noticed that your app no longer supports metadata with the UIDocumentInteractionController, so although I'm able to open your app, it doesn't initiate a post - we'd love to have that back for our iOS 7 users.

I was hoping to be able to send a link and and image with the new extension

If putting both an NSURL and a UIImage in your activity items array doesn't give you the ability to create a photo post with the URL as the caption, it should. Thanks for the feedback, we'll see if we can make this possible.

It also doesn't have support for tags. Are you aware of any way to send metadata for custom fields over (in your case, tags)?

We could certainly support this, by asking you to create an object with a custom UTI that we specifically look for, and pull whatever different types of metadata that we want out of it. However, the problem with this approach is that only the Tumblr extension will show app, for the very reasons outlined in this issue. Other share extensions won't explicitly support the custom UTI, and as such, won't be displayed in the activity controller.

That said, we'd consider if there's enough demand for specialized, Tumblr-only activity controllers. I'd just be surprised if there was, is all.

Additionally, I noticed that your app no longer supports metadata with the UIDocumentInteractionController, so although I'm able to open your app, it doesn't initiate a post - we'd love to have that back for our iOS 7 users.

This certainly sounds like a bug. I'll file a ticket and take a look, thanks for bringing this to my attention.

we'd love to have that back for our iOS 7 users

Our container app with the share extension is iOS 8 only. Was this an issue with the last iOS 7 version?

That all makes a ton of sense to me - for now I'll concat the link & tags in to the text description. I didn't think to check if the new Tumblr was iOS 8, but that should resolve the old version, I'll add the extension support and fall back to the old one on iOS 7!

Did you ever have issues with the predicate simply being parsed incorrectly? I've written a very simple predicate string, yet the Photos app is miserably failing to evaluate it. For instance, you would assume this would allow my extension to be shown whenever a video file is being shared:

SUBQUERY (
    extensionItems,
    $extensionItem,

    SUBQUERY (
        $extensionItem.attachments,
        $attachment,
        ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.movie"
    ).@count >= 1

).@count >= 1

If you select a video in the photos app, and also select a photo that was taken after that video, the extension doesn't appear. However, here's the WTF part: if you select a video, and also a photo taken before that video, it does show up. I cannot come up with any reason why this would be the case, and unfortunately there doesn't seem to be a way to debug how the predicate is being parsed.

I've tried with screenshots, photos from the back camera and the front camera, it doesn't seem to make a difference. The only difference is whether they were taken before or after the shared video.

irace commented

This is fixed in iOS 9, as per the App Extension Best Practices session 🎉.

To opt your extension into OR instead of AND behavior, simply add the following key/value pair to your Info.plist:

NSExtensionActivationDictionaryVersion: 2

The user long-presses on a photo
We put the image data, the posts’s URL, and maybe a text summary of the post, all in the activity items array

Anyone know if the above is the actual behavior of the Tumblr app? I have an action extension that accepts an image, but cannot get it to show up when you long-press a photo. I’ve got NSExtensionActivationSupportsImageWithMaxCount = 1 and NSExtensionActivationDictionaryVersion = 2 in my Info.plist and my extension does appear when doing a similar share from Photos, Twitter, Flickr, etc. But Tumblr only seems to provide a public.url attachment type and nothing matching public.image. (And the link it provides is to the post, not a specific image.) So I’m just wondering if the above idea of providing an image, link, and text together is really being done or if that was just an example/idea and not actually how Tumblr provides activity items.

Incidentally, the standard system “Save Photo” and “Copy” actions do appear from Tumblr, so somehow they know there is image data?

You're right; that's not the behavior of the Tumblr app.

In order to avoid having photo posts be unable to be shared by share extensions that do not explicitly support images, we opted for what we assumed to be the lowest common denominator for all share extensions: URLs.

The system copy activity simply acts on the URL of the photo post; if you choose Copy and then paste in a text area, you'll see it's a URL.

The "Save Photo" activity is trickier. Because the system save image activity will not show up based on a URL, we wrote our own custom save image activity that we use when long pressing photos.

@irace It looks like this behaviour again changed in iOS 11. In Firefox for iOS we now have half baked sharing support because our original predicate stopped working. Have you seen anything prblematic with iOS 11?

irace commented