← すべての記事

canOpenURLが非推奨に:代わりに何を呼ぶべきか

iOS 3.0から提供されてきたメソッドを、Appleはわずか3文で退けました。「canOpenURL:は非推奨です。事前に検証するのではなく、URLを開くことを試み、失敗した場合に対処してください。カスタムURLスキームの代わりにユニバーサルリンクを使えば、この検証自体が不要になります」1

この項目にはradar 179874781が付き、UIKitのDeprecations(非推奨)欄で、アプリを起動不能にするsceneベースのライフサイクル義務化のすぐ下に置かれています。1 隣の項目は、具体的な結果まで書き添えています。一方のcanOpenURLの項目は、結果を何ひとつ示していません。

3つの文はそれぞれ別々の指示であり、開発者が本当に知りたいこと、つまり「では代わりに何を書けばいいのか」に答えているのは2番目だけです。そしてAppleの答えは、名前だけでなく呼び出しの形そのものを変えるものでした。

要点

  • AppleはcanOpenURL(_:)をiOS、iPadOS、Mac Catalyst、tvOS、visionOSの27.0で非推奨とし、「URLを開くことを試み、失敗に対処する方法を推奨します」というメッセージを添えました。2 削除バージョンの明示はなく、実行時の影響もありません。
  • 本当に効いてくる変更は数値のほうで、しかもリリースノートではなく、非推奨となったメソッド自身の解説(Discussion)に埋もれています。「iOS 27以降にリンクされたアプリは、LSApplicationQueriesSchemesキーに最大25エントリまでという制限を受けます」——従来の50から半減です。2 引き金となるのは、リンクするSDKのほうです。
  • 機械的な置き換え先はopen(_:options:completionHandler:)とその真偽値で、許可リストへの登録は要りません。「open(_:options:completionHandler:)メソッドはLSApplicationQueriesSchemesの要件に縛られません」2
  • 代替を失うパターンが1つあります。canOpenURLは何も描画しないうちに答えを返しますが、「試す」方式は結果によって答えるからです。成功すれば、別のアプリが前面に出てきます。2 生き残るのはuniversalLinksOnlyです。iOS 10から存在するユニバーサルリンク(Universal Links)向けのopenオプションで、Appleは非推奨にしていません。これは「URLが有効なユニバーサルリンクであり、それを開けるアプリがインストールされている場合にのみURLを開く」ものです。3
  • SwiftUIにはそもそもこのメソッドが存在せず、完了ハンドラの真偽値は最初から「開いた(did open)」ではなく「開ける(can open)」を意味しています。415 筆者が出荷している7本のアプリ、自前のSwiftファイル463本を調べたところ、canOpenURLLSApplicationQueriesSchemesも出現回数はゼロでした。6

影響のない非推奨と、影響のある数値

Appleが書いたのは非推奨であって、削除ではありません。シンボルページにはUIApplicationが存在する5つのプラットフォームすべてにdeprecatedAt 27.0が記載され、概要も解説も戻り値の契約も宣言も、そのまま残っています。2 このメソッドが動かなくなるバージョンを名指ししたページはAppleのどこにもなく、最も近い前例はむしろ逆を示しています。iOS 10.0で非推奨となったopenURL(_:)は今もページが残り、注記は「このメソッドを呼び出しても効果はありません」となっています。7 10年越しの非推奨が生んだのは、消えたメソッドではなく、無効化されたメソッドでした。これは前例であって、スケジュールではありません。

告知の範囲も、非推奨の範囲より狭くなっています。リリースノートはAppleが「iOS & iPadOS 27 Beta 4 Release Notes」と題したページにあるだけで、他のどこにも出てきません。一方、シンボルのメタデータはtvOSとvisionOSでも同じように非推奨としており、2026年6月のUIKit更新ページにもXcode 27のリリースノートにも一切言及がないのです。1289 記録として持続性があるのは、シンボルページのほうでしょう。

だからこそ、埋もれた数値のほうが本題になります。非推奨となったメソッド自身の解説、しかも宣言を要求している当の補足のなかに、こう書かれています。「iOS 15以降にリンクされたアプリは、LSApplicationQueriesSchemesキーに最大50エントリまでという制限を受けます。iOS 27以降にリンクされたアプリは、LSApplicationQueriesSchemesキーに最大25エントリまでという制限を受けます」2

許可リストは、それが仕える唯一のメソッドの非推奨を生き延び、しかも新しいSDKにリンクしたアプリでは上限が半分になります。Appleは上限を示すだけで、そこから先には触れていません。26番目以降がどうなるかはどこにも書かれておらず、補足の前半と後半を突き合わせて読んで得られるのは、文書化された答えではなく「おそらくこうだろう」という推測です。宣言されていないスキームは常にfalseを返すのだから、40個のスキームを宣言しているアプリが再リンクすれば、おそらくそのうち15個は存在しないものとして扱われ始めます。どの15個なのか、そもそも切り捨てが配列順に従うのかどうか、Appleは何も述べていません。仕組みは推測、リスクは現実——そう扱ってください。アプリが無いときも宣言が無いときも、返ってくるfalseはまったく同じ顔をしているからです。

同じ解説の次の段落には2つ目の制限があり、両者は加算ではなく択一の関係にあります。Appleはこれを、リンクしたSDKによって切り分けています。「アプリを以前のバージョンのiOSに対してリンクしていて、それがiOS 9.0以降で動作している場合、このメソッドは50回まで呼び出せます。その上限に達すると、以降の呼び出しは常にfalseを返します。ユーザーがアプリを再インストールまたはアップグレードすると、iOSは上限をリセットします」2 条件をよく読んでください。この回数制限はiOS 9より前にリンクされたアプリのもので、今日出荷できるものではありません。現行のアプリはすべてもう一方の枝、つまり宣言数の上限の側にあり、Appleが50エントリから25エントリへ削ったのはこちらです。どちらも使い切れば同じ無言のfalseに行き着き、どちらもAppleがたった今非推奨にしたメソッドの中にしか記載がありません。

プライバシーの観点からの読み解きも、あくまで推測です。canOpenURLは、その人がどのアプリをインストールしているかを検出する標準的な手段でした。リンクの検証というより、デバイスのシグナルです。そしてAppleによるフィンガープリンティングの定義は、「デバイスやユーザーを識別しようとしてデバイスのシグナルにアクセスするために悪用される」APIを対象にしています。10 しかしApple自身がこの2つを結びつけたことはありません。このメソッドは必須理由API(required reason API)の一覧に載っておらず、リリースノートも非推奨メッセージもシンボルページも、純粋に機構的な理由しか挙げていないのです。1210 問い合わせ上限の半減はプライバシーの筋書きに合致しますが、Appleはそれを書き残していません。

canOpenURLが実際に約束していたこと

trueはURLについての説明ではなく、次の呼び出しについての保証でした。「このメソッドがtrueを返した場合、同じURLでopen(_:options:completionHandler:)メソッドを続けて呼び出せば、そのURLを処理できるアプリの起動に成功することをiOSが保証します」2 一方のfalseは設計上あいまいで、原因が2つあり、どちらが起きたのかを知る術はありませんでした。「URLのスキームを処理するアプリがデバイスにインストールされていない場合、またはInfo.plistファイルでそのURLのスキームを宣言していない場合はfalse2

答えているように思われがちで、実際には一度も答えていないことが3つあります。「戻り値は、URLの妥当性、指定されたリソースの存在、そしてユニバーサルリンクの場合はそのリンクに応答するよう登録されたアプリがデバイスにインストールされているかどうかを示すものではありません」2 3つ目の節は、Appleが勧める移行先にとって重要なので、この後もう一度取り上げます。

このメソッドを構造的に有用にしていた性質こそ、何も代わりを持たないものです。canOpenURLは宣言にnonisolatedキーワードを持ち、Appleは「このメソッドはメインスレッド以外のスレッドから安全に呼び出せます」と明言しています。2 main actorの外でも使える同期的な真偽値なら、何かが描画される前にレイアウトの判断を分岐させられるわけです。

試して対処する——変わるのは呼び出しの形

Appleが示す代替の指示はたった一節で、ビフォー・アフターも一見ささいなものに見えます。

// Before: validate, then open. Requires an Info.plist declaration.
let url = URL(string: "someapp://profile/42")!
if UIApplication.shared.canOpenURL(url) {
    UIApplication.shared.open(url)
} else {
    presentWebFallback()
}
<key>LSApplicationQueriesSchemes</key>
<array>
    <string>someapp</string>
</array>
// After: attempt, then handle. No declaration needed.
let url = URL(string: "someapp://profile/42")!
let opened = await UIApplication.shared.open(url)
if !opened {
    presentWebFallback()
}

Info.plistのエントリは消えます。Appleもはっきりこう述べています。「このメソッドとは異なり、open(_:options:completionHandler:)メソッドはLSApplicationQueriesSchemesの要件に縛られません。URLを処理できるアプリがあれば、スキームを宣言していなくてもシステムがそれを起動します」2 完全に移行したコードベースはこのキーを削除でき、25エントリ問題ごと消えてなくなります。

書き換えても残る違いが2つあり、どちらも構造的なものです。

1つ目は分離(isolation)とタイミングです。UIApplicationの宣言は@MainActor class UIApplicationなのでopenはmain actor上で動き、概要にもそのとおり「指定されたURLのリソースを非同期に開こうと試みます」と書かれています。1112 メインスレッド外で使える同期的な真偽値は失われました。したがって、モデル層やバックグラウンドキュー、あるいは非分離のヘルパーに置かれた「検証してから開く」ロジックは、移動するかasyncになるかを迫られます。

2つ目は、もはや「こっそり尋ねる」ことができない点です。試行が成功すれば、別のアプリが前面に出てきます。「iOSはそのアプリを起動し、URLを渡します(アプリの起動によって、そのアプリが前面に出ます)」12 答えは、行動した結果の副作用として届くのです。失敗した場合についてAppleは「完了ハンドラがsuccessパラメータfalseで呼ばれる」以外に目に見える挙動を文書化していませんが、行動より先に答えを必要としていたパターンはすべて失われます。相手のアプリがあるときだけ「別のアプリで開く」行を表示する、インストール済みのアプリ順に共有シートを並べ替える、競合する候補のなかから既定を選ぶ——いずれもです。12

Apple自身のドキュメントも追いついていません。open(_:options:completionHandler:)のページには、今もこう書かれています。「URLを処理できるアプリがインストールされているかどうかを判断するには、このメソッドを呼ぶ前にcanOpenURL(_:)メソッドを呼び出してください」12 代替手段のページが、非推奨のメソッドを勧めているわけです。

生き残る存在確認

canOpenURLの問いに「試す」やり方で答えられる道が、1つだけ文書化されています。しかも答えが「いいえ」のとき、ユーザーには何も見えません。UIApplication.OpenExternalURLOptionsKey.universalLinksOnlyはiOS 10から存在し、Appleは非推奨にしておらず、その挙動はまさに存在確認そのものです。「このメソッドは、URLが有効なユニバーサルリンクであり、かつそれを開けるアプリがインストールされている場合にのみURLを開きます」3

// Presence check with no side effect when the app is absent.
let url = URL(string: "https://myphotoapp.example.com/albums?albumname=vacation")!
let installed = await UIApplication.shared.open(
    url,
    options: [.universalLinksOnly: true]
)
if !installed {
    presentWebFallback()   // nothing opened, nothing switched
}

価値があるのはfalseの分岐です。ブラウザは起動せず、アプリも出てこず、それでも呼び出し側はcanOpenURLがかつて教えてくれていたことを知ります。引き換えになるものは、オプション名のなかに隠れています。これを付けなければ、httpsへの試行はブラウザが受け取れる限り必ず成功してしまうのです。「ユニバーサルリンクを処理できるアプリがない場合、iOSはそれをその人の既定のブラウザに回し、関連付けられたWebサイトに応答させます」2 つまりこのオプションは、ユニバーサルリンクを心地よいものにしているフォールバックを封じることで、意味のある真偽値を手に入れています。値の型についてAppleは「真偽値を格納したNSNumberオブジェクト」としており、SwiftのtrueはObjective-Cのブリッジを通じてこれを満たします。3

代償は設計上のもので、これがAppleの3文目にあたります。ユニバーサルリンクには双方向の関連付けが必要です。「誰かがあなたのアプリをインストールすると、システムはあなたのWebサーバー上に置かれたファイルを確認し、あなたのWebサイトがそのアプリに代わってURLを開くことを許可しているかを検証します。このファイルをサーバーに置けるのはあなただけであり、それによってWebサイトとアプリの関連付けが保護されます」13 自分が管理するサーバー上のファイル——これこそ、カスタムスキームが一度も要求しなかったものです。だからこの移行は自分のアプリ群には有効でも、theirapp://しか公開していない第三者に対しては何の役にも立ちません。文書化された意外な挙動も2つ付いてきます。自分のアプリが自分のユニバーサルリンクを開いても自アプリには回らないこと、そしてSafariで自サイトを閲覧中に同一ドメインのリンクをタップしてもSafariに留まることです。13

ここで、先ほどの3つ目の節が効いてきます。canOpenURLもまた、ユニバーサルリンクについては存在の問いに答えていませんでした。2 移行とは存在検出を移植することではなく、取り除くことなのです。素のhttpsへの試行は、応答したのがアプリでもWebサイトだけでも、どのみち成功してしまうからです。カスタムスキームがインストール状態を漏らしていたからこそ成立していたパターンは、スキームとともに去ります。そしてAppleのリリースノートは、その喪失こそ移行すべき理由だと位置づけています。

SwiftUIにはそもそもこのメソッドがなかった

SwiftUI側の話は短く、そして朗報です。EnvironmentValues.openURLOpenURLActionLinkはいずれもiOS 14.0からの提供で非推奨の記載はなく、3つとも妥当性チェックの手段を持っていません。4514

代わりにSwiftUIが提供しているのは、Appleが今やあらゆる場所で求めている「試して対処する」の意味論そのものです。しかも完了パラメータのドキュメントによれば、その真偽値は古い問いに答えています。「URLを開けるかどうかを判断した後、ただしURLを完全に開き終わる前に呼ばれる可能性のあるクロージャ。クロージャは、メソッドがURLを開けるかどうかを示す真偽値を受け取ります」15 「開いた」ではなく「開ける」です。Apple自身の例も、それを出力しています。

openURL(url) { accepted in
    print(accepted ? "Success" : "Failure")
}

Linkは真偽値を一切公開せず、環境(environment)に委ねます。その既定の動作は、すでにユニバーサルリンクの筋書きを実装済みです。「既定のアクションは、可能であればユニバーサルリンクを関連付けられたアプリで開き、そうでなければユーザーの既定のWebブラウザで開きます」5 .handled.discarded.systemActionのいずれかを返すカスタムのOpenURLActionは、その環境からアクションを読み取るすべてのLinkと、Text内のすべてのマークダウンリンクを横取りします。5

SwiftUIだけで移行を計画する前に、1つの欠落が問題になります。OpenURLActionにはオプションの辞書がありません。呼び出しシグネチャはcallAsFunction(_:)callAsFunction(_:completion:)、そしてiOS 26で追加されたcallAsFunction(_:prefersInApp:)の3つで、どれもuniversalLinksOnlyを受け取らないのです。1518 副作用のない存在確認は、結局UIApplication.shared.openを呼ぶことを意味します。

7本のアプリ、463ファイル、呼び出しはゼロ

他人のコードについて書く前に自分のポートフォリオを監査し、移行対象の一覧が出てくるものと思っていました。移行すべきものは、何もありませんでした。6

アプリ Swiftファイル canOpenURL LSApplicationQueriesSchemes URLを開く呼び出し箇所
Reps 77 0 0 5
Return 57 0 0 2
Banana List 55 0 0 2
Ace Citizenship 26 0 0 2
Water 34 0 0 0
ResumeGeni 71 0 0 12
Yawara 143 0 0 0
合計 463 0 0 23

このゼロは、範囲を広げた3回の走査を生き延びました。ベンダリングされたSwiftパッケージすべて、全リポジトリの全ファイル種別まで広げてもゼロです。6 192本の.plist.pbxproj.entitlements.xcconfigファイルのどれも問い合わせスキームを宣言していませんが、これは当然の帰結でしょう。canOpenURLの呼び出しがないアプリに、宣言する理由はありません。

理由は平凡で、おそらくよくある話です。23か所の呼び出しのうち、9つは法務文書を開き、4つはアプリ自身のWeb製品へ引き渡し、2つはユーザーが自分の選出議員を調べられるようアラートからcensus.govを開き、2つはAPI経由で届く求人URLを開き、2つはmacOS専用のfile:エクスポート、1つは「Health Access Required」アラートの背後にあるUIApplication.openSettingsURLStringです。この20か所はどれも他のアプリに問い合わせていません。行き先が、Safariが必ず応答するhttpsか、システムURLのどちらかだからです。残る3か所に、面白い挙動がすべて詰まっています。

開く前に検証している唯一の箇所は、今回の非推奨が問題にしているものとは別物でした。ResumeGeniの課金フローは/api/me/portalPOSTし、サーバーが返したURLについてurl.scheme == "https"を確認しています。侵害された、あるいは不具合のあるサーバーがアプリにfile:やカスタムスキームのURLを渡してくる場合に備えるためです。6 Appleの注記が対象としているのは相手のアプリが存在するかどうかの検証であり、リモート入力を信用してよいかについては何も述べていません。したがってこのガードを削除するのは移行ではなく、セキュリティの後退になります。

残る2つはBanana ListのmacOS向け引き渡しで、そのうち1つがポートフォリオ内で唯一の本物の存在確認です。このアプリは、同梱の.mcpb拡張をLaunch Servicesに渡す前にNSWorkspace.shared.urlForApplication(withBundleIdentifier: "com.anthropic.claudefordesktop") != nilを尋ねており、すぐ隣には、まずアプリが必要な人のためにclaude.com/downloadを開くボタンが置かれています。コード内のコメントが動機を明かしています。この確認がないと、macOSが独自に「no application set to open the document」というダイアログを出してしまうのです。6 プラットフォームは違いますが、監査で最も有用なデータ点でした。「試して対処する」方式の失敗モードを、実地で捉えているからです。失敗した試行は常に静かとは限らず、システムが代わりに口を開いたとき、ユーザーが読むのはあなたのフォールバックではなくシステムのエラーになります。

applinks:を宣言しているリポジトリは1つもなく、つまりここにあるどのアプリもユニバーサルリンクに対応していません。Appleが示す設計上の解決策の代金は、筆者の側でもまだ支払われていないわけです。6 カスタムスキームの代金はInfo.plistのエントリ1つ。ユニバーサルリンクの代金は、Webサーバー上のファイルと、エンタイトルメントと、自分が管理するドメインです。

ここから、本当の作業がどこにあるかが見えてきます。自前のアプリコードではなく、インストール済みアプリに応じて並び替わる共有シート、「〜で開く」の選択画面、そしてアトリビューション用のSDKです。リポジトリがきれいでも、これらの存在までは否定できません。

監査そのものは、27サイクルの他の項目より簡単です。ビルド設定から流れ込んでテキスト検索を完全に無力化する起動画面のキーとは違い、LSApplicationQueriesSchemesにはAppleのビルド設定リファレンス上にINFOPLIST_KEY_版が存在しません。だからgrepが正しい第一手になりますし、その配列に誰のスキームが並んでいるかを見れば、どの依存関係が答えを欲しがっているのかも分かります。16

よくある質問

iOS 27でcanOpenURLは動かなくなりますか?

いいえ。そしてAppleは、いつそうなるかも述べていません。シンボルにはdeprecatedAt 27.0が付いていますが、それ以外の部分——戻り値の契約、続くopen呼び出しについての保証、宣言数の上限、旧来の呼び出し回数制限——はすべて文書として残っています。2 削除バージョンは、リリースノートにもシンボルページにもXcode 27のリリースノートにも登場しません。129 突然の断絶ではなく、警告とゆるやかな衰退を前提に計画してください。

アプリがインストールされているか知る必要がある場合、代わりに何を書けばいいですか?

相手先を自分で管理しているかどうかで変わります。管理しているなら、ユニバーサルリンクを公開し、openuniversalLinksOnlyを渡してください。Appleはこれを、有効なユニバーサルリンクであり、かつ受け取れるアプリがインストールされている場合にのみURLを開くものとして文書化しています。したがってfalseは、何も開かず何も切り替わらなかったことを意味します。3 代金は、Webサーバー上の双方向の関連付けファイルです。13 第三者がカスタムスキームしか公開していない場合、代替は存在しません。canOpenURLは依然として答えを返し、依然としてInfo.plistの宣言を必要とし、その宣言の上限はiOS 27以降にリンクされたアプリでは25エントリに半減します。2 この点についてSwiftUIのコードに変更は不要です。openURLLinkも、もともとそのチェックを提供していなかったからです。414

LSApplicationQueriesSchemesはまだ必要ですか?

必要なのはcanOpenURLのためだけです。AppleのLaunch Servicesのドキュメントは、このキーを完全に非推奨メソッドの言葉で定義しています。「UIApplicationクラスのcanOpenURL:メソッドでアプリが使えるようにしたいURLスキームを指定します」17 このキーは現行のInformation Property Listリファレンスにページを一切持たず、非推奨メソッドの解説からのリンク先も旧アーカイブです。217 代替手段には何も要りません。Appleがopenを宣言要件からきっぱり除外しているからです。2 移行を完了すれば配列は消えます。呼び出しを1つでも残せば、配列も宣言要件も、そして小さくなった上限も残ります。

25エントリを超えるビルドはApp Storeでリジェクトされますか?

そう述べたAppleのページはありません。上限が登場するのはcanOpenURL(_:)の解説という1か所だけで、Appleはそれを提出時のルールではなく、キーが保持できる内容の制限として書いています。2 iOS 27のリリースノートにも、UIKitの更新ページにも、Xcode 27のリリースノートにも、審査上の結果を結びつける記述はなく、システムが未宣言として扱うスキームについて文書化されている症状は、実行時に素のfalseが返ることだけです。1289 App Store ReviewガイドラインにはLSApplicationQueriesSchemescanOpenURLも25エントリという数値も一度も出てきません。提出時のルールがあるとすれば、そこに書かれるはずのものです。19 つまり備えるべき失敗は、リジェクトされるビルドではなく、自分のコードが返す誤った答えのほうです。

要点まとめ

iOS開発者へ - if canOpenURL(url) { open(url) }let opened = await open(url)!opened時のフォールバックに置き換え、呼び出しがなくなった時点で対応するLSApplicationQueriesSchemesのエントリを削除しましょう。2 - サーバーから届くURLに対して行っているスキームの検査は残してください。Appleの注記が対象としているのはアプリの存在確認であってリモート入力ではありませんが、grepの上では両者はまったく同じに見えます。

アプリ群のディープリンクやSDKを出荷しているチームへ - 存在の有無を知りたいときはcanOpenURLではなくuniversalLinksOnlyを使い、それが要求するAssociated Domainsの関連付けファイルも見込んでおきましょう。313 アプリが無いときにユーザーへ何も見せない、文書化された唯一のチェックです。 - iOS 27のSDKにリンクする前に、LSApplicationQueriesSchemes配列の要素数を数えてください。25エントリを超えた分は文書化された上限の外に出ます。未宣言のスキームについてAppleが示す症状は素のfalseであり、それはインストールされていないアプリとまったく同じに見えます。2

リリース管理者へ - この非推奨をリリースのブロッカーとして計画に載せないでください。削除日も実行時の影響もない以上、優先度はリジェクトを招く起動画面のキー、起動そのものを止めるsceneの義務化、ビルドを止める@Stateマクロより後になります。 - 本当の引き金を持つ項目は25エントリの上限だけだと考えてください。これはユーザーが動かすOSではなく、こちらがリンクするSDKによって決まるからです。2


27サイクルは、さまざまな重さで次々に届きます。その重さを読み分けることが、このサイクルをうまく使い切る方法です。起動画面のキーは提出を阻み、sceneの義務化はアプリの起動を止め、@Stateマクロはビルドを止め、On Demand Resourcesは移行の時計を動かし始め、そしてcanOpenURLは警告するだけです。警告のまわりに緊急性をこしらえるのは、サイクルの浪費でしかありません。注視すべき一行はリリースノートではなく解説の段落に潜んでいて、しかもそれは数値なのです。シリーズ全体のハブはApple Ecosystemシリーズにあります。

参考文献


  1. Apple, iOS & iPadOS 27 Release Notes、UIKitセクションのDeprecations(radar 179874781)。確認時点でこのページ自身のタイトルは「iOS & iPadOS 27 Beta 4 Release Notes」であり、本記事で引用した他のリリースノートページもすべて同様でした。したがってリリースノートの文言はすべて暫定的なものとして扱ってください。より持続性のある記録は、シンボルページの提供状況メタデータのほうです。ここで引用した項目全文の出典:「canOpenURL:は非推奨です。事前に検証するのではなく、URLを開くことを試み、失敗した場合に対処してください。カスタムURLスキームの代わりにユニバーサルリンクを使えば、この検証自体が不要になります」。この項目は、sceneベースのライフサイクルの項目(radar 141837548)「最新のSDKでビルドされたアプリはsceneベースのライフサイクルを採用しなければならず、さもなければ起動に失敗します」の直後に置かれています。2026年7月26日にAppleのドキュメントJSONに対して検証しました。HTMLのページはJavaScriptを通じて描画されるためです。同じJSONにはcanOpenURLが1回、radar 179874781が1回だけ出現します。tvOS 27、visionOS 27、watchOS 27、macOS 27のリリースノートも同日に取得し、それぞれ「tvOS 27 Beta 4」「visionOS 27 Beta 4」「watchOS 27 Beta 4」「macOS 27 Golden Gate Beta 4」というタイトルでしたが、いずれの文字列も出現回数はゼロでした。 

  2. Apple, canOpenURL(_:)、UIKitインスタンスメソッドリファレンス。宣言はnonisolated func canOpenURL(_ url: URL) -> Bool。iOS 3.0(Mac Catalyst 13.1、tvOS 9.0、visionOS 1.0)で導入され、iOS、iPadOS、Mac Catalyst、tvOS、visionOSでdeprecatedAt 27.0が付され、いずれにも「URLを開くことを試み、失敗に対処する方法を推奨します」という提供状況メッセージが添えられています。ページの非推奨サマリーは、リリースノートの3文のうち2文をそのまま繰り返しています。「事前に検証するのではなく、URLを開くことを試み、失敗した場合に対処してください。カスタムURLスキームの代わりにユニバーサルリンクを使えば、この検証自体が不要になります」。以下の出典でもあります。戻り値の説明(「URLのスキームを処理するアプリがデバイスにインストールされていない場合、またはInfo.plistファイルでそのURLのスキームを宣言していない場合はfalse、それ以外はtrue」)、保証(「このメソッドがtrueを返した場合、同じURLでopen(_:options:completionHandler:)メソッドを続けて呼び出せば、そのURLを処理できるアプリの起動に成功することをiOSが保証します。戻り値は、URLの妥当性、指定されたリソースの存在、そしてユニバーサルリンクの場合はそのリンクに応答するよう登録されたアプリがデバイスにインストールされているかどうかを示すものではありません」)、スレッドに関する注記(「このメソッドはメインスレッド以外のスレッドから安全に呼び出せます」)、両方の上限を含む本記事引用の許可リストに関する補足(「iOS 15以降にリンクされたアプリは、LSApplicationQueriesSchemesキーに最大50エントリまでという制限を受けます。iOS 27以降にリンクされたアプリは、LSApplicationQueriesSchemesキーに最大25エントリまでという制限を受けます」)、本記事引用の実行時呼び出し回数制限(「アプリを以前のバージョンのiOSに対してリンクしていて、それがiOS 9.0以降で動作している場合、このメソッドは50回まで呼び出せます。その上限に達すると、以降の呼び出しは常にfalseを返します。ユーザーがアプリを再インストールまたはアップグレードすると、iOSは上限をリセットします」)、適用除外(「このメソッドとは異なり、open(_:options:completionHandler:)メソッドはLSApplicationQueriesSchemesの要件に縛られません。URLを処理できるアプリがあれば、スキームを宣言していなくてもシステムがそれを起動します」)、そしてユニバーサルリンクのフォールバック(「ユニバーサルリンクを処理できるアプリがない場合、iOSはそれをその人の既定のブラウザに回し、関連付けられたWebサイトに応答させます」)。なお補足内の1文は、公開されている文面のままではやや奇妙に読めます。「このメソッドは、デバイスに登録済みのアプリがインストールされていない場合でも、宣言されていないスキームに対しては常にfalseを返します」。本記事におけるfalseの曖昧さに関する主張は、この文ではなく戻り値の節に依拠しています。2026年7月26日にAppleのドキュメントJSONに対して検証しました。 

  3. Apple, UIApplication.OpenExternalURLOptionsKey.universalLinksOnly、UIKitタイププロパティリファレンス。iOS 10.0(Mac Catalyst 13.1、tvOS 10.0、visionOS 1.0)から利用可能で、2026年7月26日時点で非推奨メタデータは付いていません。概要(「URLはユニバーサルリンクであり、それを開くよう構成されたアプリが存在しなければなりません」)および本記事で引用した解説の出典:「このキーをopen(_:options:completionHandler:)メソッドのオプション辞書に含めると、このメソッドは、URLが有効なユニバーサルリンクであり、かつそれを開けるアプリがインストールされている場合にのみURLを開きます。このキーの値は、真偽値を格納したNSNumberオブジェクトです」。 

  4. Apple, EnvironmentValues.openURL、SwiftUIインスタンスプロパティリファレンス。宣言は@MainActor @preconcurrency var openURL: OpenURLActionで、iOS 14.0、iPadOS 14.0、Mac Catalyst 14.0、macOS 11.0、tvOS 14.0、visionOS 1.0、watchOS 7.0から利用可能。2026年7月26日時点で非推奨メタデータはありません。本記事に再掲したopenURL(url) { accepted in ... }の例、および本記事で引用した既定アクションの説明の出典です。 

  5. Apple, OpenURLAction、SwiftUI構造体リファレンス。宣言は@MainActor @preconcurrency struct OpenURLActionで、iOS 14.0から利用可能、非推奨メタデータはありません。「システムは、URLの内容に応じた挙動を持つ既定のURLオープンアクションを提供します。たとえば既定のアクションは、可能であればユニバーサルリンクを関連付けられたアプリで開き、そうでなければユーザーの既定のWebブラウザで開きます」、およびカスタムアクションが「組み込みのLinkビューとマークダウンリンクを含むTextビュー、あるいは属性付き文字列内のリンク」に適用されるという記述の出典。本記事で挙げたResultのメンバーは子ページであるApple, OpenURLAction.Resultによるもので、そこにはhandleddiscardedsystemActionsystemAction(_:)、およびiOS 26の型メソッドsystemAction(_:prefersInApp:)が列挙されています。 

  6. 筆者による、出荷済みのApple向けプロジェクト7本(Reps、Return、Banana List、Ace Citizenship、Water、ResumeGeni、Yawara)の調査。2026年7月26日、macOS 26.5.2上でripgrepを用い、build/DerivedData/.build/Pods/Carthage/.swiftpm/SourcePackages/、およびパッケージのcheckouts/を除外した自前のSwiftを対象としました。ファイルの網羅性はリポジトリごとにfindrgを突き合わせて確認し(77、57、55、26、34、71、143ファイル)、gitignoreされた自前のSwiftを取りこぼしていないことを確かめています。canOpenURLのゼロは3通りの方法で検証しました。自前のSwift、--no-ignore --hidden付きのSwift(ビルド生成物とベンダリングされたパッケージのcheckoutをすべて含み、ResumeGeniのツリーで3,946本、共有の941Kitツリーで15,760本のSwiftファイル)、そして--no-ignoreでの全ファイル種別です。第三者製・ベンダリング済みのコードを含め、どの走査でもゼロでした。LSApplicationQueriesSchemesINFOPLIST_KEY_LSApplicationQueriesSchemesは、192本の.plist.pbxproj.entitlements.xcconfigファイルを通じてゼロ件です。23か所の呼び出しの内訳は、SwiftUIのLinkビューが6つ、UIApplication.shared.openが3つ、openURL(...)が12(すべてResumeGeni、6つの@Environment(\.openURL)宣言に由来)、Banana ListのmacOSコード内のNSWorkspace.shared.openが2つ。なおLink(の計数には単語境界が必要でした。素のパターンではNavigationLink(やプロジェクト固有の各種...Link(型にも一致してしまうためです。SFSafariViewControllerWKWebViewの使用はどのプロジェクトにもゼロ、applinks:を宣言しているリポジトリもゼロでした。ResumeGeniの課金ガードはProfile/ProfileView.swift:1746、Banana Listの存在確認とその説明コメントはBanana List/SettingsView.swift:321:329にあり、本記事で引用した「no application set to open the document」という文言はmacOSのダイアログの書き起こしではなく、そのソースコメントの表現です。Returnの設定へのディープリンクはReturn/ContentView.swift:222。RepsのメインアプリターゲットはSUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx"を宣言しています。本記事のプラットフォームに関する記述はいずれも*_DEPLOYMENT_TARGETキーに由来せず、また各アプリの対応範囲全体をSUPPORTED_PLATFORMSから推測してもいません。このキーは、これらのプロジェクトの80あるビルド構成のうち40でしか設定されていないからです。 

  7. Apple, openURL(_:)、UIKitインスタンスメソッドリファレンス。宣言はfunc openURL(_ url: URL) -> Boolで、iOS 2.0で導入され、iOS 10.0(Mac Catalyst 13.1)で非推奨。現在の非推奨注記の出典:「このメソッドを呼び出しても効果はありません。代わりにopen(_:options:completionHandler:)メソッドを使用してください」。2026年7月26日確認。 

  8. Apple, UIKit updates、Apple Developer Documentation。2026年6月のセクションは4つの小見出し(General、App life cycle、Drag and drop、Text views)を持ち、App life cycleの下にsceneベースのライフサイクル義務化が含まれています。「iOS 27以降、最新のSDKでビルドされたアプリはsceneベースのライフサイクルを使わなければならず、さもなければ起動に失敗します」。2026年7月26日にcanOpenURLLSApplicationQueriesSchemesを検索しましたが、ページ上のどこにも見当たりません。 

  9. Apple, Xcode 27 Release Notes。2026年7月26日にcanOpenURLLSApplicationQueriesSchemes、radar 179874781を検索しましたが、いずれも見当たりません。 

  10. Apple, Describing use of required reason API、Bundle Resourcesドキュメント。本記事で引用したAppleのフィンガープリンティング定義の出典:「アプリが中核的な機能を提供するために使うAPIのなかには……デバイスやユーザーを識別しようとしてデバイスのシグナルにアクセスするために悪用されうるものがあり、これはフィンガープリンティングと呼ばれます。ユーザーがトラッキングを許可したかどうかにかかわらず、フィンガープリンティングは認められません」。2026年7月26日に検索したところ、canOpenURLLSApplicationQueriesSchemesはこのページに登場しません。Appleがこのメソッドをフィンガープリンティングと結びつけていないという本記事の記述は、これを根拠としています。同ページは報告要件を説明し、カテゴリ一覧はNSPrivacyAccessedAPITypeのドキュメントに委ねています。本記事における非推奨のプライバシー的解釈は筆者の推測であり、Appleが表明した理由ではありません。 

  11. Apple, UIApplication、UIKitクラスリファレンス。宣言は@MainActor class UIApplicationで、iOS 2.0から利用可能、非推奨メタデータはありません。open(_:options:completionHandler:)が継承し、canOpenURL(_:)nonisolatedで抜け出しているmain actor分離の出典です。 

  12. Apple, open(_:options:completionHandler:)、UIKitインスタンスメソッドリファレンス。iOS 10.0から利用可能で非推奨メタデータはなく、完了ハンドラ形式とasync形式の両方で宣言されています。func open(_ url: URL, options: [UIApplication.OpenExternalURLOptionsKey : Any] = [:], completionHandler completion: (@MainActor @Sendable (Bool) -> Void)? = nil)およびfunc open(_ url: URL, options: [UIApplication.OpenExternalURLOptionsKey : Any] = [:]) async -> Bool。概要(「指定されたURLのリソースを非同期に開こうと試みます」)、本記事で引用した起動時の挙動(「指定されたURLスキームを別のアプリが処理する場合、iOSはそのアプリを起動し、URLを渡します(アプリの起動によって、そのアプリが前面に出ます)。指定されたスキームを処理できるアプリがない場合、完了ハンドラはsuccessパラメータにfalseが設定された状態で呼ばれます」)、そして今なお非推奨メソッドを勧めている記述(「URLを処理できるアプリがインストールされているかどうかを判断するには、このメソッドを呼ぶ前にcanOpenURL(_:)メソッドを呼び出してください。使用したいスキームの登録に関する重要な注意があるため、そのメソッドの説明を必ずお読みください」)の出典。2026年7月26日確認。 

  13. Apple, Allowing apps and websites to link to your content、Xcodeドキュメント。サーバー側の関連付け要件(「誰かがあなたのアプリをインストールすると、システムはあなたのWebサーバー上に置かれたファイルを確認し、あなたのWebサイトがそのアプリに代わってURLを開くことを許可しているかを検証します。このファイルをサーバーに置けるのはあなただけであり、それによってWebサイトとアプリの関連付けが保護されます」)、ブラウザへのフォールバック(「その人がアプリをインストールしていない場合、システムはそのURLを既定のWebブラウザで開き、Webサイト側に処理させます」)、アプリが自分自身のユニバーサルリンクを開いても自アプリには回らないという注記(「上記のいずれかの方法であなたのアプリが自サイトのユニバーサルリンクを開いた場合、そのリンクはアプリでは開きません」)、および本記事で述べた同一ドメインでのSafariの挙動の出典。同ページは、ユニバーサルリンクを振り分ける呼び出しとして、SwiftUIのEnvironmentValues.openURLとUIKitのopen(_:options:completionHandler:)を挙げています。 

  14. Apple, Link、SwiftUI構造体リファレンス。宣言は@MainActor @preconcurrency struct Link<Label> where Label : Viewで、iOS 14.0、macOS 11.0、watchOS 7.0から利用可能、非推奨メタデータはありません。本記事で引用した既定の挙動の出典:「ユーザーがLinkをタップまたはクリックしたときの既定の挙動は、URLの内容によって決まります。たとえばSwiftUIは、可能であればユニバーサルリンクを関連付けられたアプリで開き、そうでなければユーザーの既定のWebブラウザで開きます」。 

  15. Apple, OpenURLAction.callAsFunction(_:completion:)、SwiftUIインスタンスメソッドリファレンス。宣言は@MainActor @preconcurrency func callAsFunction(_ url: URL, completion: @escaping (Bool) -> Void)。本記事で引用した完了時の意味論の出典:「URLを開けるかどうかを判断した後、ただしURLを完全に開き終わる前に呼ばれる可能性のあるクロージャ。クロージャは、メソッドがURLを開けるかどうかを示す真偽値を受け取ります」。兄弟メソッドのcallAsFunction(_:)はURLのみを受け取ります。2026年7月26日確認。 

  16. Apple, Build settings reference、Xcodeドキュメント。2026年7月26日にINFOPLIST_KEY_LSApplicationQueriesSchemesを検索したところ、この設定は登場せず、一方でINFOPLIST_KEY_LSApplicationCategoryTypeINFOPLIST_KEY_LSBackgroundOnlyINFOPLIST_KEY_LSSupportsOpeningDocumentsInPlaceINFOPLIST_KEY_LSUIElementは存在します。独自のプロパティリストをマージするビルドステップがこのキーを注入することは依然として可能なので、この不在が意味するのは、リポジトリ検索が完全な監査になることではなく、正しい第一手になることです。 

  17. Apple, Launch Services Keys、Information Property List Key Reference(Appleアーカイブ)。キーの定義の出典:「LSApplicationQueriesSchemes(配列 - iOS)UIApplicationクラスのcanOpenURL:メソッドでアプリが使えるようにしたいURLスキームを指定します。canOpenURL:メソッドで使いたいURLスキームごとに、この配列へ文字列として追加してください」。同ページはこのキーが「iOS 9.0以降でサポートされている」と述べ、エントリ数の上限には触れていません。このアーカイブは、canOpenURL(_:)自身の解説内にあるリンクの遷移先です。このキーは現行のInformation Property Listリファレンスにページを持たず、2026年7月26日に確認したところ、documentation/bundleresources/information-property-list/lsapplicationqueriesschemes.jsonはHTTP 404を返す一方、lsapplicationcategorytypelsbackgroundonlyといった兄弟のLS*キーのページは200を返しました。リファレンス自身のインデックスJSONも判定材料にはなりません。そこには最上位のキーグループが7つ列挙されているだけで個別のLS*キーは1つも名指しされておらず、実在するページを持つlsapplicationcategorytypeも同様に載っていないからです。 

  18. Apple, OpenURLAction.callAsFunction(_:prefersInApp:)、SwiftUIインスタンスメソッドリファレンス。宣言は@MainActor @preconcurrency func callAsFunction(_ url: URL, prefersInApp: Bool)で、iOS 26.0、iPadOS 26.0、Mac Catalyst 26.0、macOS 26.0、tvOS 26.0、visionOS 26.0、watchOS 26.0から利用可能。OpenURLActionのページでは「Calling the action」ではなくInstance Methodsの下に掲載されています。オプション辞書ではなく単一の真偽値を取るため、これが3つ目にして最後の呼び出しシグネチャであり、3つのいずれもuniversalLinksOnlyを受け取りません。2026年7月26日確認。 

  19. Apple, App Store Review Guidelines。2026年7月26日にLSApplicationQueriesSchemescanOpenURL、「25 entries」を検索したところ、いずれも出現回数はゼロでした。積極的な主張のためではなく、提出時のルールが存在しないことを裏づけるために引用しています。 

関連記事

iOS 27の起動画面ルール——4つのキーがなければリジェクト

iOS 27のSDKでビルドしたアプリは、起動画面(launch screen)を宣言していなければApp Storeにリジェクトされます。4つのキーの使い分けと、自動生成されるplistを前提にしたターゲット監査の手順を解説します。

2 分で読める

On Demand Resourcesは非推奨に——Background Assetsが求める代償

Appleは13語でODRを非推奨にしました。代替手段は3つに分岐し、iOS 26という下限を設け、これまでシステムが担っていたディスク管理をあなたに返してきます。

5 分で読める