質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

1回答

1147閲覧

share extensionのdidSelectPostにおいて,画像の取得ができません

kj8

総合スコア16

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2019/01/07 09:09

編集2019/01/08 09:40

前提・実現したいこと

share extensionを利用して画像を共有したいと思っています.
webページで開いている画像等ではなく,iphoneの写真アプリ内の画像やスクリーンショットの画像を共有することを想定しております.
didSelectPostにおいて,postがクリックされた際にアタッチメントの画像をシェアしたいのですがうまくいきません.

発生している問題・エラーメッセージ

エラーは起こっていませんが,共有したい画像の取得ができません.

該当のソースコード

Swift

1 override func didSelectPost() { 2 3 let inputItem: NSExtensionItem = self.extensionContext?.inputItems[0] as! NSExtensionItem 4 let itemProvider = inputItem.attachments![0] 5 6 7 // 画像を取得 8 if (itemProvider.hasItemConformingToTypeIdentifier("public.url")) { 9 itemProvider.loadPreviewImage(options: nil, completionHandler: { (item, error) in 10 if let image = item as? UIImage { 11 //キーチェーンで紐付いている名前を記入 12 let sharedDefaults: UserDefaults = UserDefaults(suiteName: self.suiteName)! 13 //画像を保存 ※ここで画像の保存がうまくいきません 14 sharedDefaults.setValue(image.pngData(), forKey: "img") 15 //画像の名前を保存 16 var imgName = self.textView.text 17 imgName = ShareViewController.valueOfCell + "_" + imgName! 18 sharedDefaults.set(imgName, forKey: "name") 19 sharedDefaults.synchronize() 20 } 21 }) 22 } 23 24 self.extensionContext?.completeRequest(returningItems: [], completionHandler:nil) 25 } 26

保存がうまくいったか確認するコード

Swift

1 class ImageViewController: UIViewController { 2 3 override func viewDidLoad() { 4 super.viewDidLoad() 5 6 //確認 7 let suiteName: String = "group..." //group名は適当です 8 let imgkey: String = "img" 9 let namekey: String = "name" 10 let sharedDefaults: UserDefaults = UserDefaults(suiteName: suiteName)! 11 let img = sharedDefaults.object(forKey: imgkey) 12 let name = sharedDefaults.object(forKey: namekey) 13 print(name as! String) 14 print(img) 15 16 17 } 18 19 override func didReceiveMemoryWarning() { 20 super.didReceiveMemoryWarning() 21 } 22 23} 24

試したこと

didSelectPostを以下のように変えたところ,画像の名前(文字列)の保存のみできました.
以下のコードで保存ができてるかImageViewControllerで確認したところ,print(img)でOptional(teklra;)と出力されました.
ちなみに,「//画像を保存」と「画像の名前を保存」に該当するコードの順序を逆にして実行すると(画像を保存してから画像の名前を保存しようとすると),画像の名前すら保存されませんでした.

override func didSelectPost() { let inputItem: NSExtensionItem = self.extensionContext?.inputItems[0] as! NSExtensionItem let itemProvider = inputItem.attachments![0] //キーチェーンで紐付いている名前を記入 let sharedDefaults: UserDefaults = UserDefaults(suiteName: self.suiteName)! //画像の名前を保存 var imgName = self.textView.text sharedDefaults.set(imgName, forKey: self.namekey) //画像を保存 sharedDefaults.setValue(inputItem, forKey: "img") sharedDefaults.synchronize() self.extensionContext?.completeRequest(returningItems: [], completionHandler:nil) }

どうぞよろしくお願い致します.

補足情報(FW/ツールのバージョンなど)

Swift4
Xcode10.1

ここにより詳細な情報を記載してください。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

takabosoft

2019/01/08 05:26

share extensionやったことがないのですが、「//キーチェーンで紐付いている名前を記入」あたりのコードにまで制御が到達しているのは確認できているのでしょうか? 例えば画像の保存はできていないが、ファイル名の方は取れていたるするのでしょうか? また、うまく保存できたかどうかはどのようなコードで確認されているのでしょうか?
kj8

2019/01/08 05:41

説明不足で申し訳ございません. 「//キーチェーンで紐づいている名前を記入」のところまで制御が到達しておりません. 試したことに詳細を追記いたしますのでご確認お願い致します. うまく保存できたかどうかを確認するコードもソースコードに追加いたします.
guest

回答1

0

ベストアンサー

いくつかググってみて気になった点ですが、

以下の記事にimageを取得するサンプルコードが載っていました。
https://qiita.com/KosukeQiita/items/994693da551a7101cc9c#imageを取得する

で、self.extensionContext?.completeRequest(returningItems: [], completionHandler:nil)
の位置がサンプルと違うようです。

たぶんですが、何かしらの処理が成功したか失敗したかをどこかのタイミングで通知する必要があり、
本来であればすべて画像なりなんなりを保存した上でself.extensionContext?.completeRequestを呼び出さないといけないはずが、
何も保存できていない段階でself.extensionContext?.completeRequestを呼び出していて処理が正常に動作していないのかな?と感じました。

失敗した時にはself.extensionContext!.cancelRequestを呼び出した方が良いのかなと思いましたが、そのへんは調べてみてください。


追記:
自分でプロジェクト作ってみましたがシミュレーター上では保存コードまで到達しています。
(googleのトップページをpostしているだけです)

イメージ説明

ちゃんとブレークしていますし、OKもprintされています。

swift

1class ShareViewController: SLComposeServiceViewController { 2 3 let suiteName = "group..." 4 5 override func isContentValid() -> Bool { 6 // Do validation of contentText and/or NSExtensionContext attachments here 7 return true 8 } 9 10 override func didSelectPost() { 11 let inputItem: NSExtensionItem = self.extensionContext?.inputItems[0] as! NSExtensionItem 12 let itemProvider = inputItem.attachments![0] 13 14 15 // 画像を取得 16 if (itemProvider.hasItemConformingToTypeIdentifier("public.url")) { 17 itemProvider.loadPreviewImage(options: nil, completionHandler: { (item, error) in 18 if let image = item as? UIImage { 19 print("OK") 20 //キーチェーンで紐付いている名前を記入 21 let sharedDefaults: UserDefaults = UserDefaults(suiteName: self.suiteName)! 22 //画像を保存 ※ここで画像の保存がうまくいきません 23 sharedDefaults.setValue(image.pngData(), forKey: "img") 24 //画像の名前を保存 25 var imgName = self.textView.text 26 imgName = "_" + imgName! 27 sharedDefaults.set(imgName, forKey: "name") 28 sharedDefaults.synchronize() 29 } 30 self.extensionContext?.completeRequest(returningItems: [], completionHandler:nil) 31 }) 32 } 33 34 35 } 36 37 override func configurationItems() -> [Any]! { 38 // To add configuration options via table cells at the bottom of the sheet, return an array of SLComposeSheetConfigurationItem here. 39 return [] 40 } 41 42}

更に追記:

どうもXcode10とシミュレーターの組み合わせだと実行できないバグがあるようでして(ドメインのエラーが出る)
https://stackoverflow.com/questions/52462456/sharing-pdf-file-from-safari-with-action-extension-failing-in-swift-4-2-xcode-10?rq=1

It looks like it is a bug in Xcode 10 iOS 12 Simulator only.

実機で試したところ、うまく動きましたので、一度報告します。

swift

1 override func didSelectPost() { 2 3 let inputItem: NSExtensionItem = self.extensionContext?.inputItems[0] as! NSExtensionItem 4 let itemProvider = inputItem.attachments![0] 5 6 if itemProvider.canLoadObject(ofClass: UIImage.self) { 7 itemProvider.loadObject(ofClass: UIImage.self) { image, error in 8 if let image = image as? UIImage { 9 let sharedDefaults = UserDefaults(suiteName: self.suiteName)! 10 sharedDefaults.setValue(image.pngData(), forKey: "img") 11 } 12 self.extensionContext?.completeRequest(returningItems: [], completionHandler: nil) 13 } 14 } else { 15 self.extensionContext?.completeRequest(returningItems: [], completionHandler: nil) 16 } 17 }

更に追記:

一応他の方法も試しました。
loadItemを使うと画像のURLが取れましたが、どうも一時的なもので、このURLには確認用アプリの方からはアクセスできなさそうでした(結局そのファイルの内容をバイナリのままUserDefaultsに保存する方法で試作)。

一個上のサンプルコードは一旦UIImageに変換がかかっているので、それに比べればこちらのサンプルコードは元のファイルデータ(のコピー)をそのまま保存するので、多少無駄が減ったかなと思います。

※本当は写真のアドレスだけ他のアプリに渡して、読み取れれば容量的にもスマートなんですが、ちょっとやり方がわからなかったです。

swift

1 override func didSelectPost() { 2 3 let inputItem: NSExtensionItem = self.extensionContext?.inputItems[0] as! NSExtensionItem 4 let itemProvider = inputItem.attachments![0] 5 6 let itemIdentifier = kUTTypeImage as String 7 if itemProvider.hasItemConformingToTypeIdentifier(itemIdentifier) { 8 itemProvider.loadItem(forTypeIdentifier: itemIdentifier, options: nil) { item, error in 9 if let url = item as? URL { 10 print(url) 11 if let data = try? Data(contentsOf: url) { 12 let sharedDefaults = UserDefaults(suiteName: self.suiteName)! 13 sharedDefaults.set(data, forKey: "img") 14 sharedDefaults.synchronize() 15 } 16 } 17 self.extensionContext?.completeRequest(returningItems: [], completionHandler: nil) 18 } 19 } else { 20 self.extensionContext?.completeRequest(returningItems: [], completionHandler: nil) 21 } 22 }

なお、itemProvider.loadDataRepresentationではDataが取れなかったので諦めました。

投稿2019/01/08 06:09

編集2019/01/09 03:00
takabosoft

総合スコア8356

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kj8

2019/01/08 06:36

回答いただきありがとうございます. サンプルコードを参考に,ほぼ全く同じコードで実行しましたが,未だに保存がうまくいきません. また,self.extensionContext?.completeRequestの位置を変えたことにより,共有画面が閉じれなくなってしまったのでself.extensionContext?.completeRequestまで制御が到達していないように思えます. ご教授の方どうぞよろしくお願い致します.
takabosoft

2019/01/08 08:03

自分で検証した結果、普通に動いてしまったので原因不明です。info.plistのNSExtensionActivationRuleはどうなっていますか?
kj8

2019/01/08 08:21

わざわざ検証いただきありがとうございます. googleのトップページで試されたということなので,私も同様にgoogleのトップページでpostしたらうまくいきました. 私の意図としては,iphoneの写真アプリの写真や,スクショを取った際の共有ボタンからシェアすることを想定しております. そのため,スクショを撮り,その際の共有ボタンから同様にpostするとやはり画像の保存はできませんでした. もしかしたら私が勘違いしており,教えていただいたサンプルコードの「imageを取得する」というのはwebページの画像を取得ということで,私がやりたい写真や画像の共有とは違うものなのでしょうか?
takabosoft

2019/01/08 08:34

「webページの画像を取得ということで,私がやりたい写真や画像の共有とは違うものなのでしょうか?」→違うと思います。最初にpublic.urlでふるいに掛けている通り、おそらくURLをPOSTする用のサンプルコードかと思います。質問欄にもろに書いてありましたが見落としていました、すみません(^_^;)
kj8

2019/01/08 09:37

わざわざ確認していただいたのにこちらこそすみませんでした. 改めて,私がやりたい写真や画像の共有方法について何かわかることがありましたらご指摘の方よろしくお願い致します.
takabosoft

2019/01/09 01:41

試してはいるのですが、loadPreviewImageで写真のサムネイルは取れても、オリジナル画像がどうにも取れないんですよねぇ。。。
takabosoft

2019/01/09 03:00

いろいろ解答欄に追記しました。私の方は一旦これで調査を終わります。
kj8

2019/01/09 05:56

いろいろ試していただきありがとうございました!無事画像を保存することができました. 大変感謝しております.
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問