Getting your app to show up in the ShareSheet for URLs

If you’re developing a new iOS app, whether with SwiftUI / Swift or UIKit, one of the things you may want to do is create a Share Extension. If you do this, there’s a fair chance you will want it to work on URLs.

The standard way to do this, which does work often, would be to do something like this in your Share Extension’s Info.plist file:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>NSExtension</key>
    <dict>
        <key>NSExtensionAttributes</key>
        <dict>
            <key>NSExtensionActivationRule</key>
            <dict>
                <key>NSExtensionActivationSupportsWebPageWithMaxCount</key>
                <integer>1</integer>
                <key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
                <integer>1</integer>
            </dict>
            <key>NSExtensionJavaScriptPreprocessingFile</key>
            <string>WebpageProcessor</string>
        </dict>
        <key>NSExtensionPointIdentifier</key>
        <string>com.apple.share-services</string>
        <key>NSExtensionPrincipalClass</key>
        <string>MyExampleClass</string>
    </dict>
</dict>
</plist>

This says to activate my app in the ShareSheet if:

  1. The shared item is one web page
  2. Or, the shared item is one URL
  3. When I have a webpage, run the WebpageProcessor Javascript file on it.

This will work fine in most cases. However, you can find that your app won’t show up in the ShareSheet for Firefox, Apple News or the App Store. This is because these shared items have multiple attachments. You will need to run a subquery on the attachments.

If you want to know how to create an NSExtensionActivationRule subquery, luckily DuckDuckGo has a nice clean example you can find here.

Or, with my example above, you can turn that Share Extension Info.plist into this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>NSExtension</key>
    <dict>
        <key>NSExtensionAttributes</key>
        <dict>
            <key>NSExtensionActivationRule</key>
            <string>
            SUBQUERY (
            extensionItems,
            $extensionItem,
                SUBQUERY (
                    $extensionItem.attachments,
                    $attachment,
                    ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.url"
                    ).@count == 1
            ).@count == 1
            </string>
            <key>NSExtensionJavaScriptPreprocessingFile</key>
            <string>WebpageProcessor</string>
        </dict>
        <key>NSExtensionPointIdentifier</key>
        <string>com.apple.share-services</string>
        <key>NSExtensionPrincipalClass</key>
        <string>MyExampleClass</string>
    </dict>
</dict>
</plist>

Now, as long as a shared item has 1 URL attachment, it will show up. The Javascript preprocessing file can still work too. If you are given a webpage, then that should have a URL too and cause your app to activate.

Update (2023.05.10)

If you only want to get web urls and exclude file-based URLs then you may want something like this where you combine two sub queries:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>NSExtension</key>
    <dict>
        <key>NSExtensionAttributes</key>
        <dict>
            <key>NSExtensionActivationRule</key>
            <string>
            SUBQUERY (
            extensionItems,
            $extensionItem,
                SUBQUERY (
                    $extensionItem.attachments,
                    $attachment,
                    ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.url"
                ).@count == 1
                AND
                SUBQUERY (
                    $extensionItem.attachments,
                    $attachment,
                    ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.file-url"
                ).@count == 0
            ).@count == 1
            </string>
            <key>NSExtensionJavaScriptPreprocessingFile</key>
            <string>WebpageProcessor</string>
        </dict>
        <key>NSExtensionPointIdentifier</key>
        <string>com.apple.share-services</string>
        <key>NSExtensionPrincipalClass</key>
        <string>MyExampleClass</string>
    </dict>
</dict>
</plist>