
-
SwiftUI向けWebKitの紹介
WebKitを使って、WebコンテンツをSwiftUIアプリに簡単に統合する方法を学びましょう。Webコンテンツをロードして表示する方法や、Webページと通信する方法などを紹介します。
関連する章
- 0:00 - イントロダクション
- 1:54 - Webコンテンツのロードと表示
- 9:37 - ページとの通信
- 15:44 - コンテンツとのインタラクションのカスタマイズ
リソース
関連ビデオ
WWDC25
-
このビデオを検索
こんにちは Richardです WebKitチームのエンジニアです WebKitは中核的な ブラウザエンジンとして Safari メールなど無数のアプリの中核となり iOS iPadOS visionOS macOSに対応します 長年にわたり WebKitは デベロッパの皆さんにリッチで ダイナミックな体験を構築してもらえるよう パワフルで柔軟な Webの機能をアプリに直接もたらし Appleのプラットフォームの強みを 提供してきました 今年は SwiftUI向けWebKitを 紹介できることを嬉しく思います まったく新しいSwiftUI APIにより Webコンテンツをアプリに 簡単に統合できます SwiftUI向けのWebKitのメリットとして WebKitがサポートする パワフルなWeb体験と SwiftUIの使いやすさがあります 新しいAPIを使えば 美しいWebコンテンツの表示は Webビューを作成して URLを提供するのと同じくらい簡単で WebKitがサポートする すべてのプラットフォームで動作します WebKitとSwiftUIをとても簡単に 使い始められるようになりました では このAPIを使ってアプリを構築する 新しい方法をいくつか見ていきましょう まず Webコンテンツを読み込んで表示する 様々な方法を学びます ローカルリソースの読み込みなどです 続いて Webコンテンツの変更に対応し コンテンツに変更を加える 様々な方法を説明します 最後に 既存/新規の ビューモディファイアを使って Webコンテンツの操作方法を カスタマイズする方法に触れます ページ内検索の有効化や スクロール位置の設定などを 説明します
内容は多岐にわたりますが 順に見ていきましょう 新しいAPIが持つ 素晴らしい機能をお見せするために 世界中の様々な湖の情報を閲覧する アプリを構築します アプリ全体に美しいWebコンテンツを表示し 記事の内容を読み込む方法や ナビゲーションを確認する方法 JavaScriptと通信して アプリに命を吹き込む方法を紹介します また Webコンテンツの ブラウジングを思い通りに カスタマイズする方法を紹介します 最後に 仕上げとして 新規/既存の ビューモディファイアを組み合わせて アプリの外観と操作性を高めます アプリの開発を始めましょう SwiftUIのコードの一部は設定済みなので 新しいWebKit APIの統合に集中できます まず Webコンテンツの基本である 表示から始めましょう 新しいWebView APIで 驚くほどシンプルになります これはまったく新しいSwiftUIビューで どんなWebコンテンツでも 簡単に表示できるよう設計されています 先ほど見たように WebViewを使用するには URLを指定するだけです URLが自動的に読み込まれ コンテンツが表示されます もちろん 1つのURLを表示するためだけに WebViewを使用したくない場合もあります この例では 2つのURLを切り替えるボタンを用意し WebViewに再びURLを渡します これで トグルが変更されるたびに Webビューが自動的に読み込まれ 新しいURLに移動します とてもシンプルです では Webコンテンツで もう少し面白いことをやってみましょう プロパティの変更に反応させます このような場合は WebPageを作成してから WebViewに接続するだけです これで Webコンテンツに加えられた変更は ページのタイトルのように Webページ全体に適用されます
WebPageは まったく新しいObservableクラスで Webコンテンツを表します SwiftやSwiftUIと完璧に連携するよう 一から設計されています
Webコンテンツの読み込み コントロール 通信を行えるのがWebPageです WebPageは完全に単独で使用できます しかし WebViewと組み合わせると Webコンテンツの 魅力的な体験を構築できます では 様々な方法をお見せしながら WebPageで Webコンテンツを読み込んでみましょう 一般的な方法の1つとしてご紹介したいのが リモートURLをWebページに読み込む方法です これを行うには URL要求でAPI読み込みを使用するだけです しかし WebPageは URLの読み込みだけに限定されません HTML文字列とベースURLを APIに指定することで HTMLコンテンツを 直接読み込むこともできます ベースURLは システムがHTML内の 相対URLを解決するときに使用されます WebPageはまた Webアーカイブデータなど 様々な種類のデータの 直接読み込みもサポートしています 読み込むデータ そのMIMEタイプ 文字エンコーディング ベースURLを 指定するだけです このアプリではまず 特定の記事を表す ArticleViewModelクラスを作ります その中に 関連するWebPageと Lakeの記事を配置します SwiftUIがページと記事の変更に 簡単に反応できるようにするため クラスをObservableにします これで クラス設定の基礎ができたので 湖のURLをWebページに実際に読み込むための 関数を追加します 次はビューの方に話を移しましょう モデルのWebPageを使用して WebViewを作成します そして ビューが最初に表示されたときに モデルの loadArticle関数を呼び出します もっといい感じにするために ignoreSafeAreaも使って ページがデバイスの下まで 流れるようにします これで 新しい記事のURLを アプリに追加して開くと コンテンツが読み込まれ 詳細ビューに表示されます ここまでは実に順調ですが 体験をさらに良いものにするために アプリで事前に読み込まれた 記事をいくつか提供します WebKitはリモートURLと データの読み込みを想定通りに すぐに処理します アプリにバンドルされている コンテンツを読み込み ローカルファイルにアクセスする 機能も備えています これは まったく新しいURLSchemeHandler プロトコルを使って実行できます 使用方法を説明しましょう 独自のスキームハンドラを実装すると そのスキームを持つ URLへのナビゲーションは すべてハンドラを経由し 指定されたデータを使用します その前に まずスキームの 概要について説明しましょう スキームはURLのコロンより前の部分で この場合はHTTPSスキームです WebKitは https file aboutなどの 一般的なスキームを処理します しかし 例えばこのlakesスキームのような カスタムスキームの場合 URLSchemeHandlerプロトコルは アプリのコードに lakesスキームURLのリソースの 読み込みを処理させます 独自のカスタムスキーム ハンドラを実装するには URLSchemeHandlerプロトコルを 確認する型を作成し プロトコルの応答関数を実装して URLSchemeTaskの結果の 非同期シーケンスを返します この関数は 応答を決定するための URLRequestパラメータも提供します 一連のイベントを作成する場合は 最初に URLResponseを含む URLSchemeTaskResultを 生成する必要があります URLResponseを生成したら Dataを指定するだけです これはほとんどの場合に 適していますが 時には 非同期にデータを ストリーミングする場合もあります AsyncSequence戻り値の型のおかげで これも簡単に行うことができます ここでは 非同期で 1つの応答が返されます また データ値のAsyncSequenceも受け取り 返されたシーケンスに追加します URLスキームタスクが 何らかの時点で取り消された場合 関数内のタスクも 自動的にキャンセルされます アプリでは いくつかの湖を あらかじめ入力して読み込ませておき ユーザーがすぐに始められるように したいと思います この機能を実装するには アプリ内に HTMLアセットとCSSアセットを配置し 先ほど作った カスタムスキームハンドラを使います あとは スキームハンドラをWebPageに 登録するだけです これを行うには 処理したいスキームを使って URLSchemeを作成します この場合もlakesを使います WebKitがすでに処理している スキームを指定すると イニシャライザはnilを返します 次にWebPage Configurationを作成し urlSchemeHandlers辞書に スキームハンドラを追加します また WebPageを作成するときに 設定を渡すようにします これで デフォルトのLakeArticleの値を いくつか作成し カスタムスキームでURLを指定することで Webコンテンツがバンドルに保存された リソースから読み込まれ Webからは取得されません これで 新しいページを読み込んで 移動する方法がわかりました ページの読み込みが完了したら サイドバーに目次を表示したいと思います Webコンテンツに起こる ナビゲーションイベントを観察することで 実行できます 非常に簡単です ナビゲーションの状態に 簡単にアクセスするには WebPageの新しいObservable currentNavigationEvent プロパティを使います そのイベントを取得したら ナビゲーションIDを取得し イベントの種類に基づいて 何らかのアクションを実行できます さて ナビゲーションはたくさんあります 様々なナビゲーションイベントの タイプを見ていきましょう ナビゲーションは 複数の連続した イベントで構成されます ナビゲーションは常に startedProvisionalNavigationで始まります サーバがリクエストをリダイレクトすると receivedServerRedirectを受け取ります committedが発生し ページでメインフレームのコンテンツの 受信が開始されます finishはナビゲーションが 完了したときに届きますが 保証されているわけではありません ナビゲーションがいずれかの時点で 失敗すると failedや failedProvisionalNavigationが 発行されます 新しいナビゲーションが始まると currentNavigationEventプロパティに そのナビゲーションが反映されます 普段から 発生した ナビゲーションには継続的に 反応できるようにしたいですよね リンクをクリックしたときや load関数を呼び出したときなどです
これには新しいObservations APIを使います Swift 6.2で利用可能です Observationsを使って currentNavigationEventから 非同期シーケンスを作成し for-awaitループを使って 現在のイベントの各変更を確認します こうすることで エラーが発生した場合に対処したり 読み込みが完了した時点で 記事のセクションを更新したりできます これにより Webコンテンツの ナビゲーションの変更に対応することが これまで以上に簡単になります currentNavigationEventの他にも WebPageには変更を確認できる プロパティがたくさんあり それらはすべてSwiftUIと完璧に連携します 例えば 詳細ビューのナビゲーションバーに 記事のタイトルを 表示したいとします これを行うには navigationTitleを WebPageのtitleプロパティに設定します 実に簡単です ページのタイトル以外にも 役立つプロパティが多数用意されており 現在のURL 推定読み込み状況 ページのテーマカラーなどがあります これらのプロパティは非常に便利です ただし 標準のプロパティではない 特定の情報を ページから取得する 必要がある場合もあります そのため もう少し柔軟なものが必要です それもとても簡単にできます 新しいcallJavaScript APIを使えば JavaScriptを直接評価して ページと通信できます 通常のように JavaScript関数を書いて callJavaScriptを使うときに渡すだけです callJavaScriptの戻り値の型は 省略可能なAny値です そのため 使いやすいように 適切なSwift型にキャストしてください parseSections関数を簡単に実装するには ページのHTMLを使って 各セクションの すべてのIDとタイトルを取得します また 結果をセクション 構造体の配列に変換して 扱いやすくします
これで アプリは読み込みと同時に サイドバーに記事のセクションを 表示します WebPageはナビゲーションポリシーを 思い通りにカスタマイズする オプションを提供し 新しいWebPage. NavigationDecidingプロトコルを使用します このアプリでは カスタム ナビゲーションポリシーを実装して Webコンテンツ内のナビゲーションが 思い通りに動作するようにします ページ内に外部サイトへのリンクがある場合 アプリ内で読み込むのではなく デフォルトのブラウザで 開くようにしたいと思います これを行うには 新しいNavigationDeciderタイプを作り WebPage.NavigationDeciding. NavigationDeciderを確認します このプロトコルを使えば ナビゲーションのステップごとに 異なるポリシーを指定できます 例えば ナビゲーションの発生前や レスポンスを受け取けるとき 認証が必要なときなどです ナビゲーションが許可されるべきかどうかを URLに基づいて 判断するため ナビゲーションアクションの ポリシーを指定します この関数が提供する NavigationActionは ナビゲーションで使用され NavigationPreferencesは リクエストに基づいて調整できます urlスキームがlakesか ホストがlakes.apple.comの場合 リンクがアプリの一部であり 外部ではないことを意味するので ナビゲーションを許可します そうでない場合は アプリ内のWebPageが ナビゲーションを続けないようにします これをキャンセルし ArticleViewModelの urlToOpenプロパティを更新します カスタムのNavigationDeciderができたので それを使うように Webページを設定する必要があります ArticleViewModelに戻り 新しいnavigationDeciderと 新しいurlToOpenプロパティを作成します WebPageを更新して navigationDeciderを取り込み 先ほど指定した ポリシーを使用するようにします WebPageを設定したら 最後のステップで この機能をすべて連動させます
ArticleViewでは モデルの urlToOpenプロパティの変更を検知します プロパティがnilでない場合 ナビゲーションがキャンセル されたということです そこで openURL SwiftUI環境値を使って デフォルトのブラウザでURLを開きます これで ナビゲーションは 期待通りに機能します それでは 様々な ビューモディファイアを使用して ユーザーがWebコンテンツを操作する 方法をカスタマイズする さまざまな方法を説明します まず スクロールの動作を設定し Webビューが垂直にのみ バウンスするようにします デフォルトでは 水平の端を越えて スクロールされたときもバウンスします コンテンツがWebビューより小さくてもです 標準のscrollBounceBehavior モディファイアでは この動作をカスタマイズでき WebViewでうまく機能します コンテンツがビューより大きい場合にのみ バウンスするように 水平軸を設定するには .basedOnSize値を水平軸に設定します この他にも多くのパワフルな スクロールカスタマイズ機能が WebViewでサポートされます それだけではありません visionOSの今年の新機能である Look to Scrollは 1つのビューモディファイアを使って 簡単に設定できます
visionOSでは WebViewは 新しいlook ScrollInputKindをサポートし 新しいwebViewScrollInputBehavior モディファイアを使用できます デフォルトでは Look to Scrollは WebViewで無効になっています アプリでは有効にしたいので モディファイアを使用し lookの種類に対して 動作を有効に設定します これらの記事にはかなり長いものもあるので ユーザーが必要な記事を見つけられるように 便利にしたいと思います ユーザーが簡単に記事を 検索できるようにするために ページ内検索のサポートを追加しましょう 既存のfindNavigator モディファイアがすでに WebViewで完璧に動作するので ページ内検索のサポートは 本当に簡単です 検索ナビゲータを表示するために アプリのツールバーにボタンを追加します iOSとiPadOSでは 検索ナビゲータは オンスクリーンキーボードの一部として またはWebビューの下部に表示されます macOSとvisionOSでは Webビューの上部に表示されます アプリがようやくまとまってきました 優れたスクロール体験を構築して さらにアクセシビリティを高めましょう 記事を簡単にナビゲートするもう1つの 方法は サイドバーでセクションを選択し Webビューをそのセクションまで スクロールさせることです Webビューのスクロール位置を セクションと同期し 自分がどこにいるのか わかるようにする必要があります まず 追加したいのは ユーザーがサイドバーの セクションをタップすると Webビューがそこまで スクロールする機能です セクションにスクロールするには その場所を判断する必要がありますが これは先ほど設定した JavaScript関数で実行できます スクリプトを実行するには もう1度callJavaScriptを使用します ただし今回は 引数の辞書も指定します この辞書のキーはJavaScriptに表現される ローカル変数となり 値はJavaScriptの値に変換されます このようにパワフルかつ使いやすい方法で 再利用可能な 汎用JavaScript関数を作成できます ArticleViewでは状態を追加し スクロール位置とセクションを コントロールします スクロール位置を WebViewに関連付けるために 新しいwebViewScrollPosition モディファイアを使用します あとは セクションが変更されるたびに スクロール位置を設定するために スクロール位置でscrollToを使用して セクションの計算位置を指定します サイドバーでセクションに スクロールできるようになったので スクロールに合わせて サイドバーも更新する必要があります 新しいWebViewonScrollGeometryChange モディファイアでは それ以上のことができます 例えば コンテンツのオフセットやサイズなど WebViewのスクロールジオメトリの 一部が変更されるたびに モディファイアは指定された変換を使用し 変換された値が変更された場合 そのクロージャを呼び出します クロージャでは 新しいスクロール オフセットに最も近いセクションを計算し 選択されたセクションを更新します そうすることで 選択されたセクションと スクロール位置が 完全に同期します これで Lakesアプリが完成しました うまくできたと思います WebPageとWebViewを組み合わせて 記事を読み込んで表示し アプリにデフォルトの記事を あらかじめ入れておくことも できました また サイドバーに目次を表示し スクロール位置を同期させることで ユーザー体験を向上させました 今日は多くのことを取り上げたので 学んだことを要約しましょう SwiftUI用のWebKitは シンプルでありながらパワフルなAPIで Webコンテンツをアプリに シームレスに統合できます Webコンテンツを読み込み 見た目をカスタマイズする 機能を提供します URLSchemeHandlingにより ローカル Webリソースを効果的に管理できます その上 パワフルな ビューモディファイアである webViewScrollPositionと findNavigatorにより WebViewのユーザー体験を調整できます これらはSwiftUI用WebKitの 機能のほんの一部です そのため より包括的な情報については デベロッパ向けドキュメントを 参照することを強くお勧めします そしてもちろん これはSwiftUIなので Webコンテンツはすべての プラットフォームで見栄えが良くなります
すでにSwiftUIアプリで UIKitやAppKit WebKit APIを 使用している場合や 一からアプリを作成する場合は 新しいAPIへの移行を試す絶好の機会です 新しいObservations APIのように SwiftとSwiftUIの 他の新機能もご確認ください 最後に この新しいAPIを試したら 変更すべき点や 不足していると思う機能について お知らせください 素晴らしい新時代の到来を告げる WebKitをぜひご活用ください
-