Share Extension で画像のシェア先に自分のアプリを出す

f:id:star__hoshi:20191230162427p:plain

これの Share 先に自分のアプリを表示させて、保存などの処理を行う。

やることは

  1. Target 追加
  2. App Groups の設定
  3. 証明書の修正
  4. Podfile 修正
  5. 新しいTargetの Info.plist 修正
  6. ShareViewController の実装
  7. データの保存先について
  8. Simulator で確認

です。

Target 追加

Xcode の File > New > Target から Share Extension を選択して作成。
こんな感じの物が出来上がる。

f:id:star__hoshi:20191230172145p:plain

App Groups

次に、App Groups の設定をする。Target を超えてファイルを共有するのに必要。

先ほど作った Target > Signing & Capabilities > + Capability から AppGroups を選択。 group.com.hoge を App Groups に追加する。メインの Target にも同様に group.com.hoge を App Groups に追加する。

証明書の更新

証明書の設定が必要なので、 https://developer.apple.com/account/resources/identifiers/list から Capabilities の App Groups にチェックを入れ、 group.com.hoge を登録する。

f:id:star__hoshi:20191230172657p:plain

証明書の更新をしておく。

Podfile 修正

Share Extension でライブラリを使う必要があるなら Podfile も修正する。
例えば Realm を共有するなら以下のようにして pod update。

# Podfile

target 'hoge' do
  use_frameworks!
  inhibit_all_warnings!

  pod 'RealmSwift'
end

target 'hogeShareExtension' do
  use_frameworks!
  inhibit_all_warnings!

  pod 'RealmSwift'
end

Info.plist 修正

今回は画像を1枚だけ受ける設定にしたいので、Share Extension の Info.plist を以下のようにする。

NSExtensionAttributes を Dictionary に, NSExtensionActivationRule を Dictionary に, NSExtensionActivationSupportsImageWithMaxCount を 1 で設定する。

f:id:star__hoshi:20191230173308p:plain

https://developer.apple.com/library/archive/documentation/General/Conceptual/ExtensibilityPG/ExtensionScenarios.html#//apple_ref/doc/uid/TP40014214-CH21-SW8 で他の設定などが確認できる。

ShareViewController の実装

これまでの手順で、もう画像のシェア先に hoge アプリが出るはず。
画像を受け取った時の実装を書いていく。

import UIKit
import Social
import MobileCoreServices
import RealmSwift

class ShareViewController: SLComposeServiceViewController {
    override func isContentValid() -> Bool {
        // テキスト入力で Validation したい場合に設定する
        return true
    }

    // Post ボタンが押された
    // データの保存処理などここで行う
    override func didSelectPost() {
        let extensionItem = self.extensionContext?.inputItems.first as? NSExtensionItem
        let itemProvider = extensionItem?.attachments?.first
        let imageType = String(kUTTypeImage)

        // 渡されたデータが画像か確認
        if itemProvider?.hasItemConformingToTypeIdentifier(imageType) == true {
            // データを読み込む
            itemProvider?.loadItem(forTypeIdentifier: imageType, options: nil) { item, error in
                // 画像の場合 URL が渡ってくる
                if let url = item as? URL {
                    let image = UIImage(contentsOfFile: url.path)
                    if let image = image {
                        // 画像をローカルに保存やAPI実行するなどやりたいことをする
                        // contentText から入力された文字が取得できる
                    }
                }
            }
        }

        // 最後に実行する。データの保存処理などが非同期な場合は注意
        self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
    }

    override func configurationItems() -> [Any]! {
        // To add configuration options via table cells at the bottom of the sheet, return an array of SLComposeSheetConfigurationItem here.
        return []
    }
}

こんな感じで画像を受け取ることができる。

データの保存先について

これで共有ディレクトリを取得できます。

let url: URL? =FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.hoge")

メインのアプリとデータを共有するためには documentDirectory などは使えません。
Realm でデータの共有をしたい場合などは以下のようにします。

        var config = Realm.Configuration()
        config.fileURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.memoCamera")?.appendingPathComponent("default.realm")
        Realm.Configuration.defaultConfiguration = config

Simulator で確認

Scheme を変えてデバッグをします。Scheme を変えないと break point など使えません。

f:id:star__hoshi:20191230174653p:plain

参考

以下の記事などが参考になります qiita.com