🍎

ShareExtensionのNSExtensionActivationRuleに複雑な条件を指定する

2020/10/03に公開

はじめに

Flutterでブックマークアプリのようなアプリを開発した際に、SafariやChromeの共有メニューにはアプリアイコンが表示されるのに、GoogleやYahooといったアプリの共有メニューには表示されない、といったことがあったので、ShareExtensionの設定まわりを調査しました。

※ 後述の記事を参考に自分の理解をまとめたものなので、誤りがあるかもしれません。

結論

はじめに結論を書いておくと、NSExtensionActivationRuleに複雑な条件を指定するには、NSExtensionActivationRuleSUBQUERYを使って条件を指定します。指定したSUBQUERYの条件に合う場合のみ、共有メニューにアプリアイコンが表示されるようになります。

  <key>NSExtensionActivationRule</key>
  <string>
    SUBQUERY (
      extensionItems,
      $extensionItem,
      SUBQUERY (
        $extensionItem.attachments,
        $attachment,
        (
          ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.url"
        )
      ).@count == 1
    ).@count == 1
  </string>

基本的な設定方法

受入を許可するデータタイプをNSExtensionActivationRuleに1つずつ設定します。
デフォルトでは、NSExtensionActivationRuleTRUEPREDICATEが設定されていて、すべてのデータタイプを受入可能になっています。しかし、この設定のままでは審査の際にリジェクトされてしまうそうなので、データタイプごとに1つ1つ許可する必要があります。

以下の例では、NSExtensionActivationSupportsTextNSExtensionActivationSupportsWebURLWithMaxCountが設定してあり、データタイプがテキストまたはURLの場合のみ、ShareExtensionが有効になります(アイコンが表示されるようになる)。
例えば、テキストを選択した際に表示される共有メニューではテキスト、Safariなどのブラウザの共有ボタンを押した時はURLを受け取ることが可能です。

<key>NSExtensionAttributes</key>
<dict>
  <key>NSExtensionActivationRule</key>
  <!-- すべてのデータタイプを受け入れる設定。リジェクトされる -->
  <!-- <string>TRUEPREDICATE</string> -->
  <dict>
    <!-- テキストデータを受け入れる -->
    <key>NSExtensionActivationSupportsText</key>
    <true/>
    
    <!-- URLデータを1つまで受け入れる -->
    <key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
    <integer>1</integer>
  </dict>
</dict>

その他に設定可能な項目は以下を参照してください。
App Extension Keys

複雑な条件が必要だった理由

自分が作成したブックマークアプリではURLを必要としていたため、NSExtensionActivationSupportsWebURLWithMaxCountのみを設定しており、SafariやChromeなど、自分が普段使う数種類のアプリの共有メニューにはアイコンが正しく表示されていました。
しかし、ある時GoogleやYahooといったアプリではアイコンが表示されないということがわかり、調べてみたところYahoo,EdgeなどのアプリではURLとテキストを渡そうとしていたため、URLを1つだけ許可するという条件に合致せずにShareExtensionが有効になっていないということがわかりました。(GoogleアプリはURLしか渡していなさそうだったのに何故かダメでした)
そのため、NSExtensionActivationSupportsTextを設定したところ、GoogleやYahooのアプリの共有メニューからもShareExtensionが使えるようになりました。

しかし、この状態だと適当な文章を選択したときに表示される共有メニューからもShareExtensionが使えてしまい、URLを期待しているアプリからすると、URLなのかそうでない文字列が渡されてくるのかわからないといった問題があります。そのため、データタイプがURLの時だけアクティブになり、データタイプが複数渡されてもOK、というような条件にしたく、調査をしました。

複雑な条件を指定する

いろいろな方の記事を見て、最終的には以下のように設定しました。
②のSUBQUERYのANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.url"は、attachmentのデータタイプ(registeredTypeIdentifiers)のいずれかがpublic.urlであるという条件で、②を実行すると、データタイプにpublic.urlを含むデータが得られます。残りの@count == 1はデータが1つであることを意味していますが、URLが複数ある場合もあるかもしれないので、変える必要が出てくるかもしれません。

①のSUBQUERYは、extensionItemsの中で条件②を満たすものが1つである、という条件ですが、経験上extensionItemsが1以外だったことがないので、どんなときに複数になるのかはわかっていません。

ANYとかUTI-CONFORMS-TOなどのキーワードの説明は以下に記載されています。
Predicate Format String Syntax

データタイプの一覧は下記を参照してください。
System-Declared Uniform Type Identifiers

  <key>NSExtensionActivationRule</key>
  <string>
    SUBQUERY ( ・・・ ①
      extensionItems,
      $extensionItem,
      SUBQUERY ( ・・・ ②
        $extensionItem.attachments,
        $attachment,
        (
          ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.url"
        )
      ).@count == 1
    ).@count == 1
  </string>

参考記事

Discussion