YouTubeの「共有」から動画を追加できるようにした話(Flutter / Android・iOS)

自作アプリ「学びピン」に、
「YouTubeアプリ → 共有 → 学びピン」で動画を2タップ追加できる機能を入れました。

これまではURLをコピーしてアプリを開いて貼り付け…という導線でしたが、
共有シートから直接放り込めるようになりました。

使ったのは receive_sharing_intent

Androidはあっさり、iOSは沼だったので、詰まったポイントと設定を実装記録として残します。

同じことをやる個人開発者の方の時短になれば。

目次

仕組みはシンプル(Dart側はほぼ共通)

やっていることは、共有で飛んできたテキストから YouTube の URL を拾って、
既存の「動画を追加」フローに流すだけです。

受信部分の Dart コードは Android / iOS 共通で、プラットフォーム分岐は書いていません。

// アプリ起動中の共有
ReceiveSharingIntent.instance.getMediaStream().listen(_handleShared);
// 共有から起動された場合(コールド起動)
ReceiveSharingIntent.instance.getInitialMedia().then((v) {
  _handleShared(v);
  ReceiveSharingIntent.instance.reset(); // 二重処理防止に必須
});

YouTubeの共有本文は「タイトル+改行+URL」で飛んでくることが多いので、正規表現でURL部分だけ抜き出すのがコツです(watch / youtu.be / shorts 対応)。

Android:ほぼ詰まらない

pubspec.yaml に追加して、AndroidManifest.xmlMainActivity にテキスト共有のフィルタを足すだけ。

<intent-filter>
    <action android:name="android.intent.action.SEND" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:mimeType="text/*" />
</intent-filter>

唯一ハマったのは、プラグインの JVMターゲット不一致(Java 1.8 と Kotlin 17)で assembleDebug が落ちたこと。ルートの android/build.gradle.kts で、このプラグインだけ Kotlin を 1.8 に揃えて解消しました。

subprojects {
    if (project.name == "receive_sharing_intent") {
        afterEvaluate {
            tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>()
                .configureEach { kotlinOptions { jvmTarget = "1.8" } }
        }
    }
}

豆知識として、共有シートの1画面目(おすすめ枠)に出すかどうかはOS側が決めるので、最初は「その他」から選ぶ形になります。使っていくうちに前面へ昇格します。設定ミスではありません。

iOS:ここからが本番(Share Extension の沼)

iOSはマニフェスト1行では出せず、Share Extensionというミニターゲットを追加する必要があります。
詰まった順に。

1. CocoaPods の ffi がビルドできない
新しいmacOSだと、OS標準の古いRuby(2.6)で ffi のネイティブビルドが失敗します。素直に Homebrew版のCocoaPodsbrew install cocoapods)に切り替えるのが早いです。

2. Podfile は「入れ子」にする
共有拡張のターゲットは、Runnerと並列(トップレベル)に置くと Unable to integrate the following embedded targets で落ちます。Runnerの中に入れ子にするのが正解でした。

target 'Runner' do
  use_frameworks!
  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
  target 'Share Extension' do
    inherit! :search_paths
  end
end

3. Build Phases の順序
Runnerのビルドフェーズで「Embed Foundation Extensions」を「Thin Binary」よりへ。

4. 最大の沼:'Flutter/Flutter.h' file not found
Clang dependency scanning failure とセットで出るこれは、Xcode 26 の Explicitly Built Modules(明示的モジュールビルド)が原因の既知バグでした(Flutter issue #185210)。この方式をオフにすると通ります。プロジェクトと各Podに以下を設定。

SWIFT_ENABLE_EXPLICIT_MODULES = NO
CLANG_ENABLE_EXPLICIT_MODULES = NO

5. 対応iOSバージョン
拡張の Deployment Target を 14.0 に(プラグインの要件。13.0 だと minimum deployment target of iOS 14.0 で落ちます)。

6. App Group のスペース混入
CUSTOM_GROUP_ID に末尾スペースが紛れていて、App Group名が一致せず共有データが渡らない、という地味なハマりもありました。目に見えないので要注意。

7. スプラッシュで止まる
起動はするのにスプラッシュから進まない時、原因がデバッグ接続(Dart compiler exited unexpectedly)なら、flutter run --release で切り分け・回避できます。

まとめ

  • 共有を受け取るロジック自体はFlutterで完結し、Android/iOSで共通に書けた。
  • 沼はほぼiOSのネイティブ設定側。特に Xcode 26 の新しいモジュールビルド方式は、既存の解説記事にまだ載っていないことが多く、ここに一番時間を取られた。
  • 一度通してしまえば快適そのもの。ユーザーの「動画を追加する手間」が、コピペ往復から共有2タップに激減した。

同じ構成(Flutter + Firebase + Share Extension + Xcode 26)で詰まっている方は、まず Explicitly Built Modules をオフにするところから試してみてください。

目次