【iOS】ShareExtension-を導入してみたよ

作成日
更新日

どうも。Reo です。

個人アプリに ShareExtension を使うついでに、手順をメモしながら導入しようと思います。

 

環境

  • Xcode11.3
  • Swift 5.1.3
  • iOS 13.3

今回はサンプルプロジェクトを用意しません。余裕があるときに追記するかもしれませんが。

 

ターゲットを追加する

まずはターゲットを追加します。

ShareExtension は別ターゲットで開発していくことになります。なのでライブラリ等を使う場合は注意が必要です。

File > New > Target より ShareExtension を追加します。

2020-01-【iOS】ShareExtension-を導入してみたよ-b5cec6174e8b4962a0dddcbed57d2a25

名前はそのまま ShareExtension にしました。

作成するとポップアップが出てきました。Activate を選択しましょう。

2020-01-【iOS】ShareExtension-を導入してみたよ-eca6c00e774d46f3bfc60b3a920d6ed3

 

これで ShareExtension フォルダができていればおkです。

2020-01-【iOS】ShareExtension-を導入してみたよ-cd0a330d0f324b65b97f0aba0794d056

簡単!

とりあえずビルドをしてみましょう。

ビルドターゲットを ShareExtension に設定をして実行します。

2020-01-【iOS】ShareExtension-を導入してみたよ-924f91f109674fe8809b1b2b89f500ca

すると、どのアプリで実行するかのポップアップが表示されます。とりあえず Safari で見てみましょう。

2020-01-【iOS】ShareExtension-を導入してみたよ-0335395b5ea44d5694856149731f0611

Safari が開かれるので 下部タブバーのシェアボタンを押してみます。

2020-01-【iOS】ShareExtension-を導入してみたよ-c28ac8e9157d4257ba56933b40d48c24

はい!プロジェクト名のアイコン(設定してないので白ペッペ)を見つけられたらオッケーです。

押してみると、デフォルトの画面が表示されます。

2020-01-【iOS】ShareExtension-を導入してみたよ-95dd852cc641440185836b6e891e8b1c

ここの画面をカスタマイズして、アプリに情報を送信することができます。今は Post に何もアクションを設定してないので、画面を閉じる処理のみが行われます。

また、Cancel, Post の部分はアプリをローカライズしている場合には反映がされるはずです。ちょっと試してないんですが、されるはず...

 

こんな風に画面をカスタマイズすることもできますよ。

2020-01-【iOS】ShareExtension-を導入してみたよ-86aaa68a0595483eac199ece950fc050 AppAppより

全く手入れができていないんですが、AppApp のコードは GitHub に公開してあります。

 

AppGroups の設定をする

さて。ShareExtension とアプリを繋げるために AppGroups の設定をしていきます。

プロジェクトを選択して、TARGETS > ShareExtension の 上部タブの Sigining & Capabilities を開きます。+アイコンを押して Capability を追加します。(Capabilityは能力という意味。能力の追加...!カコイイ!)

2020-01-【iOS】ShareExtension-を導入してみたよ-570af985a76b4b57a4f5f5eeb0d4f371

 

AppGroups をダブルクリックで追加します。

2020-01-【iOS】ShareExtension-を導入してみたよ-a0f94b8db0c74aca8da1426350a06869

そうすると以下のような項目が追加されます。

2020-01-【iOS】ShareExtension-を導入してみたよ-7aafc5be04614b5cb3dcb0978bed3414

 

これね、すごいね。AppAppが出てきちゃった!!

App Groups を利用すると、FileManager や UserDefaults を使ってアプリ間でファイルの共有をすることができます。でも今回は使わないのでここには特にチェックを入れず、新規に Group を作ります。

+を押して、Group を追加します。group. プロジェクトのBundle ID にすると良いです。

間違えた名前で作ってしまった奴が消せない...消せないのかこいつ...ちょっとわかったら追記します。

 

同じ作業を Targets > メインアプリの方でも行ってください!

 

Keychain Sharingを追加する

AppGroups を追加する手順と同様に Keychain Sharing を追加します。

2020-01-【iOS】ShareExtension-を導入してみたよ-87f989d488c54be6abd294e2ae83a01e

こちらは+ボタンを押すだけで追加されます。

2020-01-【iOS】ShareExtension-を導入してみたよ-18e51c63935f4e46865af516589dcba2

 

これでとりあえずおkのはず。

 

UserDefaults で保存してみる

最後に実際にデータを UserDefaults で保存して、アプリで受け取ることができるかを試してみます。

ShareViewController.swift の didSelectPost() 内で URL を取得します。Safari で検証する予定なので、URL が取得できる前提です。

didSelectPost() は投稿ボタンが押されたときの処理を書きます。

 


override func didSelectPost() {
    guard let extensionItem: NSExtensionItem = extensionContext?.inputItems.first as? NSExtensionItem, let itemProviders = extensionItem.attachments else { return }
    // URL を取得する
    let identifier = "public.url"
    let urlProvider = itemProviders.first(where: { $0.hasItemConformingToTypeIdentifier(identifier)})
    urlProvider?.loadItem(forTypeIdentifier: identifier, options: nil, completionHandler: { [weak self] (item, error) in
        // item に格納されている
        guard let url = item as? URL else { return }
        self?.save(url: url)
        // 最後にこれを呼ばないとフリーズする
        self?.extensionContext?.completeRequest(returningItems: [], completionHandler: nil)
    })
}

private func save(url: URL) {
    // UserDefaults に保存
}

この辺の説明はちょっと割愛。いずれAppAppのリファクタリングブログを書くつもりなのでその時に余裕があれば...(書く書く詐欺なう...)

 

save(url:)の中で、UserDefaultsに保存します。

private func save(url: URL) {
    // AppGroups に設定したもの
    let suiteName = "group.hoge.hoge.hogehoge"
    let key = "shareSampleURLKey"
    let userDefaults = UserDefaults(suiteName: suiteName)
    userDefaults?.set(url, forKey: key)
}

 

これで ShareExtension を実行して Post まで実行します。

アプリ側の適当な ViewController で取得してみます。

final class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // AppGroups に設定したもの
        let suiteName = "group.hoge.hoge.hogehoge"
        let key = "shareSampleURLKey"
        let userDefaults = UserDefaults(suiteName: suiteName)
        let value = userDefaults?.url(forKey: key)
        print(value)
    }
}

 

これで取得ができていればおkです。

...

nil だと ...

 

AppGroups の設定を Targets > メインアプリの方 でも同様に設定が必要でした!!!!

AppGroupsの項でも追記しておきました。

 

無事取得できたぞ...

 

extensionContext.completeRequest を呼ばなかったら...

以前、AppAppを作っていた時は extensionContext.completeRequest を呼び忘れるとフリーズしてしまいます。

フリーズというよりかは SLComposeViewController が閉じてくれません。

iOS13.3で確認すると、デフォルトでモーダル表示になるので、下にスワイプすると画面を閉じることができます。そうじゃない場合には呼び出すのを忘れると、閉じることができなくなります。

 

Info.plist で取得コンテンツを制限する

今のままだと以下のような Warning が表示されます。

Embedded binary's NSExtensionActivationRule is TRUEPREDICATE. Before you submit your containing app to the App Store, be sure to replace all uses of TRUEPREDICATE with specific predicate statements or NSExtensionActivationRule keys. If any extensions in your containing app include TRUEPREDICATE, the app will be rejected.

 

ShareExtension の Info.plist で修正します。

2020-01-【iOS】ShareExtension-を導入してみたよ-0dbab5ed59454c438fe7380492935b20 変更前

 

NSExtensionActivationRule の型を Dictionary に変更します。

その中に必要な情報のみを書きましょう。

今回はURLのみを使いたいので

NSExtensionActivationSupportsWebURLWithMaxCount | Number | 1

を設定します。

2020-01-【iOS】ShareExtension-を導入してみたよ-c4987bbaba064266b4c9ea9ecb998b8c 変更後

 

これでURLのみが取得できるようになります。

 

おわりに

少しぐだってしまった。メモがてら書くつもりが、記事を書く方がメインになってしまっていました。

そして、ほとんど「Share Extensionでデータを共有する - Qiita」の記事を見てやってたようなものなので、実質書く意味もなかった気がしますね...

Xcodeのバージョンが変わって、AppGroups の追加方法が若干変わっているので、その辺ぐらいですかねー。

 

とりあえず今回はこの辺で。画像ばっかりで重くなるのでブログを軽くすることも考えないと...

ではでは〜

サイトアイコン
公開日
更新日