iOSで自分のアプリから写真をInstagramでシェアする時にハマった


Instagramの公式ページにも書いてるし、ググれば出てくるのですが、いろいろハマったのでまとめておきます。

動作環境、Xcode 9.3 + Swift 4.1 + iOS 11

URLスキームを使ってシェアする方法

公式ページには書かれていません。しかし、レイアウトというアプリでは、アプリ内で加工した写真を選択状態にして、Instagramの投稿画面を開くことができるので、隠しであるのかも?

APIを使って直接投稿する方法

投稿APIはありません・・・

UIActivityViewControllerを使ってシェアする方法

簡単でよいのですが、Instagramアプリは起動せず、直接自分のアプリから投稿する形になります。つまり、ユーザーからするとInstagramアプリの写真加工が使えないので不便に感じるかも。

@IBAction private func didTapShare() {
    do {
        // UIImageをactivityItemsにわたす
        let activityViewController = UIActivityViewController(activityItems: [self.image], applicationActivities: nil)
        present(activityViewController, animated: true)
    } catch let error {
        print(error)
    }
}

表示されるシェアシート

Instagramを選択すると自分のアプリ内で投稿画面が表示される。

UIDocumentInteractionControllerを使ってシェアする方法

公式ドキュメントにも書かれているこの方法。説明にはInstagramアプリのみが選択対象になると書かれているのですが、実際はそうはなりません・・・過去はそうだったのかも知れませんが、iOS 11ではそうなりませんでした。あといくつかハマりポイントがありました。

@IBAction private func didTapShare() {
    do {
        // UIImageをDataに変換
        guard let imageData = UIImageJPEGRepresentation(image, 1.0) else {
          return
        }

        // テンポラリファイルとして保存。拡張子はigoにする
        let imageURL = URL(fileURLWithPath: (NSTemporaryDirectory() as NSString).appendingPathComponent("image.igo"))
        try imageData.write(to: imageURL, options: .atomicWrite)

        // 必ずメンバ変数として保持すること。ローカル変数だと意図しないタイミングで破棄される
        // 保存した画像ファイルのURLを渡す
        self.documentInteractionController = UIDocumentInteractionController(url: imageURL)
        self.documentInteractionController!.uti = "com.instagram.exclusivegram"
        // shareButtonは押したボタン(UIBarButtonItem)
        self.documentInteractionController!.presentOpenInMenu(from: shareButton, animated: true)
        // よくこの方法が見られるけど、たいていはBarButtonItemから起動するのでは?iPadでは、ここきちんとやらないと変なとこからポップアップするので注意
        // self.documentInteractionController!.presentOpenInMenu(from: self.view.bounds, in: self.view, animated: true)
    } catch let error {
        print(error)
    }
}

UIDocumentInteractionControllerオブジェクトをメンバ変数として保持しないと、意図せず破棄されてしまいます。一瞬シェアシートが出たと思ったら、すぐに閉じてしまう。エラーログにInvalid Operationとか出ます。

そして実際に開かれるシェアシート。

一覧に表示されるのはInstagramだけじゃないんですよね・・・他のアプリもigo拡張子に対応したからでしょうか?

ただ先程のUIActivityControllerの時と違って、「Instagram」ではなく「Instagramにコピー」になっていて、選択すると、Instagramが起動し写真が選択された状態で投稿画面が開きます。これはこれで便利な気もしますが、シェアシートで選択できるアプリが限られてしまうのが、ユーザーからすると不便かもしれません。Twitterがないのはさすがに・・・Slackやクラシルは出てくるのですがw

そこでファイル拡張子やUTIの指定を以下のように変えてみます。

// 拡張子をjpg
let imageURL = URL(fileURLWithPath: (NSTemporaryDirectory() as NSString).appendingPathComponent("image.jpg"))
// utiをpublic.jpeg
documentInteractionController!.uti = "public.jpeg"

この場合、表示されるシェアシートはこのようになります。

Twitterもあります。そしてInstagramとInstagramへコピーの両方が表示されます。ユーザーからしたら何が違うんだと混乱の元になりそうですが・・・でも個人的には、これが一番いいかなと思いました。

Instagramの公式ドキュメントには、以下のようにするように書かれているのですが、上記のケースとシェアシートに表示されるアプリ、およびInstagramへコピーを選択した時の挙動に違いはなさそうでした。ドキュメント用意した時と変わってしまったのかなあ。

// 拡張子をig
let imageURL = URL(fileURLWithPath: (NSTemporaryDirectory() as NSString).appendingPathComponent("image.ig"))
// utiをcom.instagram.photo
documentInteractionController!.uti = "com.instagram.photo"