概要
対象読者:
- シェアシートの基本的な実装がわからない
- 適切なプレビューを表示できない
- 画像とテキストを同時に送信できないアプリがある
- 特に Meta 系のアプリで上手くいかない
実装方法:
- UIActivityViewController を使ったシェアシートの実装
- UIActivityItemSource と LPLinkMetadata を使ったプレビューの実装
- UIActivityItemSource のデリゲートメソッドを使ったアプリごとの挙動分岐
- Meta 系アプリで画像とテキストを同時送信できない件の今後
UIActivityViewController を使ったシェアシートの実装
func shareSheet(shareImage: UIImage, shareText: String) {
let controller = UIActivityViewController(
activityItems: [shareImage, shareText],
applicationActivities: nil
)
viewController.present(controller, animated: true, completion: nil)
}

init(activityItems: [Any], applicationActivities: [UIActivity]?)
- activityItems: [Any]
シェアしたいオブジェクトの配列を指定。
サンプルコードでは shareImage: UIImage, shareText: String
で画像とテキストを渡した。
- applicationActivities: [UIActivity]?
シェアアクティビティの配列を指定。サンプルコードでは nil
とした。
今回は扱わないが、UIActivity
をオーバーライドして画像のようなシェアアクティビティを自分で定義することができる。

(cf. UIActivity
を自分で定義する時に参考になる記事 ⤵︎)
UIActivityItemSource と LPLinkMetadata を使ったプレビューの実装
プレビューとは以下の枠線部分のことを指す。
普通に画像とテキストだけを渡すと、シェアされるオブジェクトの優先順位の影響でテキストだけがプレビューされてしまう。
ここにメタデータも一緒に渡すことで、適切なプレビューを表示するように実装する。
シェアされる要素が欠落しているプレビュー

(cf. シェアされるオブジェクトの優先順位について参考になる記事 ⤵︎)
func shareSheet(shareImage: UIImage, shareText: String, shareURL: URL) {
let sharePreview = SharePreviewItemSource(image: shareImage, title: shareText, url: shareURL)
let controller = UIActivityViewController(
activityItems: [sharePreview, shareImage, shareText],
applicationActivities: nil
)
viewController.present(controller, animated: true, completion: nil)
}
class SharePreviewItemSource: NSObject, UIActivityItemSource {
private let linkMetadata = LPLinkMetadata()
init(image: UIImage, title: String, url: URL) {
super.init()
linkMetadata.imageProvider = NSItemProvider(object: image)
linkMetadata.title = title
linkMetadata.url = url
}
func activityViewControllerLinkMetadata(_ activityViewController: UIActivityViewController) -> LPLinkMetadata? {
linkMetadata
}
// for protocol compliance
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
[]
}
// for protocol compliance
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
nil
}
}
UIActivityItemSource :
activityViewControllerLinkMetadata(UIActivityViewController) -> LPLinkMetadata?
シェアするオブジェクトを作成できるプロトコル。サンプルコードでは、メタデータを取り出すデリゲートメソッドに、作成した linkMetadata: LPLinkMetadata
を渡している。
LPLinkMetadata:
var imageProvider: NSItemProvider?,
var title: String,
var url: URL
リンクが持つタイトルや画像などのメタデータを作成できるクラス。サンプルコードでは、imageProvider, title, url
でシェアしたい画像、タイトル、URL を埋め込んだ。
シェアされる要素を全て備えたプレビュー

UIActivityItemSource のデリゲートメソッドを使ったアプリごとの挙動分岐
上記の実装で概ねどのアプリも画像とテキストをシェアできるが、Meta 系のアプリでは画像とテキストの片方のみしかシェアできないことが分かった。
アプリごとにシェアするオブジェクトを分岐することで、どちらも正常に送れるアプリの挙動は変更せずに、Meta 系アプリでは暫定的に画像だけをシェアするように実装する。
アプリごとの挙動分岐前



func shareSheet(shareImage: UIImage, shareText: String, shareURL: URL) {
let sharePreview = SharePreviewItemSource(image: shareImage, title: shareText, url: shareURL)
let _shareText = ShareTextItemSource(shareText: shareText)
let controller = UIActivityViewController(
activityItems: [sharePreview, shareImage, _shareText],
applicationActivities: nil
)
viewController.present(controller, animated: true, completion: nil)
}
class ShareTextItemSource: NSObject, UIActivityItemSource {
private let shareText: String
init(shareText: String) {
self.shareText = shareText
super.init()
}
func activityViewController(
_ activityViewController: UIActivityViewController,
itemForActivityType activityType: UIActivity.ActivityType?
) -> Any? {
guard let activityType = activityType else {
return nil
}
switch activityType {
/// Set nil. To ensure that only image is shared on SNSs
/// where images and text cannot be sent together.
case .postToFacebook, .postToInstagram, .postToThreads:
return nil
default:
return shareText
}
}
// for protocol compliance
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
[]
}
}
extension UIActivity.ActivityType {
static let postToInstagram: UIActivity.ActivityType = .init("com.burbn.instagram.shareextension")
static let postToThreads: UIActivity.ActivityType = .init("com.burbn.barcelona.ShareExtension")
}
UIActivityItemSource:
activityViewController(
_ activityViewController: UIActivityViewController,
itemForActivityType activityType: UIActivity.ActivityType?
) -> Any?
選択されたシェアアクティビティに応じた操作を行うことができるデリゲートメソッド。サンプルコードでは、Meta 系のアプリの場合は nil
を返すことでテキストを空にしている。
UIActivity.ActivityType:
init(_ rawValue: String)
シェアアクティビティを格納している型。プロパティは任意に追加することができ、サンプルコードでは、システム定義にない Instagram と Threads を加えた。
アプリごとの挙動分岐後



Meta 系アプリで画像とテキストを同時送信できない件の今後
- 現在 Facebook SDK にて 画像 + ハッシュタグの組み合わせは可能
- Threads API が 2024年6月に発表らしいので、何か方針転換があるかもしれない
現在 Facebook SDK にて 画像 + ハッシュタグの組み合わせは可能
ネットサーフィンしていて「Facebook はテキスト送信を認めていない」という噂がちらほらあったので調べてみると実際に公式の Developer Policy に記載があった。
キャプション、コメント、メッセージ、または投稿のユーザーメッセージパラメータにコンテンツをプリフィルしないでください。
ただし、共有ダイアログを通じて共有された投稿のハッシュタグ1つである場合(ただし、APIを経由しない場合)、アプリを使用しているユーザーによって作成された場合、またはアプリの従業員がFacebook上の企業の存在を管理するためにアプリを使用している企業によって作成された場合は除きます。
「ハッシュタグ1つである場合」など気になる文言があったので、実際に Facebook SDK を使って実装してみた結果を簡略に記載する。
公式の開発者向けドキュメントによると、Facebook SDK では
- Link, Image, Movie, Image + Movie の単位でのみシェアが可能
- どのシェア単位からも contentURL や hashtag といったプロパティが生えているので、Image + URL + hashtag という単位で一見それらしい実装ができそう
- 実際には Image + hashtag , URL + hashtag という組み合わせのみ可能(Image と URL は共存できない)
結論:
Facebook では Developer Policy にて投稿全般にテキストを仕込むことが禁止されていて、画像とテキストを同時に送りたい場合は「画像 + ハッシュタグ」の組み合わせのみ実装できる。
僕のプロジェクトでは X でシェアできればよいということで、やむなく Meta 系アプリでは画像のみ仕込むという仕様を許容した。
Threads API が 2024年6月に発表らしいので、何か方針転換があるかもしれない
同じ Meta 系のアプリでは Threads が API の一般提供を開始する発表があった。
この公式ドキュメントを見ると画像とテキストの両方をパラメータに指定できるように見える。
curl - i - X POST \
"https://graph.instagram.com/ v19.0/<IG_USER_ID>/threads?media_type=IMAGE&image_url=https://www.example.com/images/bronz-fonz.jpg&text=#BronzFonz&access_token= <ACCESS_TOKEN>"
こちらは現在すぐには対応できないが、対応可能になったら試したい。