第34章 Bridge Components
この章のねらい
Path Configuration(第33章)は、URL ごとの見せ方を決めるものでした。しかし、画面の中の一部分を、ネイティブの UI で表示したいこともあります。たとえば、ナビゲーションバーの右上に、ネイティブのボタンを置きたい、といった場合です。
それを実現するのが Bridge Components です。Web とネイティブの境界をつなぎ、Web の要素をきっかけにネイティブの UI を出します。この章では、その考え方を学びます。
34.1 Bridge Components とは
Bridge Components は、Web 側の宣言をきっかけに、ネイティブの UI 部品を動かす仕組みです。
たとえば、Web のフォームに「このフォームには送信ボタンが要る」と印を付けておくと、ネイティブ側がそれを受け取り、ナビゲーションバーにネイティブの送信ボタンを表示します。ボタンが押されると、ネイティブから Web へ「押された」と伝わり、Web がフォームを送信します。
Web とネイティブが、メッセージをやり取りして協調します。Web は「何が要るか」を宣言し、ネイティブは「それをネイティブの見た目で実現する」役割です。
34.2 Web 側のマークアップ
ここで効いてくるのが、第6部の Stimulus です。Bridge Components の Web 側は、Stimulus のコントローラの上に乗ります。
Web 側では、data-controller でブリッジ用のコントローラを要素に結びつけます。
<form data-controller="bridge--form">
...
</form>
この bridge--form というコントローラが、ネイティブ側へ「送信ボタンを出して」と伝えるブリッジになります。ブリッジ用のコントローラは、Stimulus のコントローラを、ネイティブと通信できるように拡張したものです(34.6 の BridgeComponent を使います)。
大切なのは、Web 側はあくまで Stimulus の延長で書ける、という点です。第6部で学んだコントローラの知識が、そのまま土台になります。
34.3 ネイティブ側の component
Web 側の宣言を受け取って、実際にネイティブの UI を出すのが、ネイティブ側の component です。
iOS なら Swift、Android なら Kotlin で書きます(第32章)。Web 側の bridge--form コントローラに対応するネイティブの component を用意し、「bridge--form が現れたら、ナビゲーションバーに送信ボタンを出す」といった処理を書きます。
対応のキーは Stimulus 識別子そのものではなく、Web 側の static component とネイティブ側の component 名です。たとえば識別子 bridge--form、static component = "form" ならネイティブ側は form に合わせます。Web の宣言と、ネイティブの実装が、ペアになっている、と考えてください。
34.4 メッセージの送受信
Web とネイティブは、メッセージをやり取りします。
- Web → ネイティブ。「送信ボタンを、このラベルで出して」といった指示を送ります。ボタンのラベルや状態など、必要な情報を渡します。
- ネイティブ → Web。「ボタンが押された」といった出来事を伝えます。Web 側はそれを受け取り、フォームを送信するなどの処理をします。
この往復で、Web の状態(フォームの内容など)と、ネイティブの操作(ボタンのタップ)が、つながります。Web 側の受け取りは、ブリッジ用コントローラのメソッドとして書きます。ここも Stimulus の延長です。
34.5 使いすぎを避ける判断
Bridge Components は強力ですが、使いすぎは禁物です。
何でもネイティブの部品に置き換えると、Web 側とネイティブ側の両方に実装が要り、二重管理になります。第32章の「すべてをネイティブ化しない」が、ここでも効きます。
判断は、こうです。Web の見た目で十分なら、ブリッジは架けない。ネイティブの部品でなければ実現できない、あるいはネイティブの方が明らかに体験が良い箇所だけ、ブリッジを使います。Relay の多くの画面は、ブリッジなしの Web のままで動きます。ナビゲーションバーのボタンのように、ネイティブの定位置に置きたいものだけを、ブリッジで橋渡しします。
もう 1 つ、忘れてはいけない前提があります。同じ Web ページは、ネイティブアプリの中だけでなく、通常のブラウザでも開かれます(Web-first)。ブラウザには対応するネイティブ component がないので、ブリッジは何も起こしません。だから、Bridge Components はプログレッシブ・エンハンスメントとして設計します。つまり、ネイティブがあれば良くなる、なくても Web だけで成立する、という形にします。先の例なら、Web 側に通常の送信ボタンを残しておき、ネイティブではそれをナビゲーションバーのボタンで置き換える、と考えます。こうすれば、ブラウザでもアプリでも壊れません。
第34章では、Web とネイティブをつなぐ Bridge Components を、Stimulus の延長として学びました。次の第35章では、Web ではなくネイティブ画面そのものが必要になる場面と、その責務分担を扱い、第9部を締めます。
34.6 名前の対応をコードで確認する
次は通信の概念を示す Web 側の断片です。通常の Stimulus Controller ではなく BridgeComponent を使います。bin/importmap pin @hotwired/hotwire-native-bridge で導入し、ネイティブ側にも form component を登録して初めてネイティブのボタンが現れます。Web の送信ボタンは残します。
// app/javascript/controllers/bridge/form_controller.js
import { BridgeComponent } from "@hotwired/hotwire-native-bridge"
export default class extends BridgeComponent {
static component = "form"
static targets = ["submit"]
submitTargetConnected(button) {
this.send("connect", { submitTitle: button.textContent }, () => button.click())
}
}
<form data-controller="bridge--form">
<button type="submit" data-bridge--form-target="submit">保存</button>
</form>
ネイティブ実装・登録方法は iOS Bridge Components と Android Bridge Components を参照します。付録Hの最小シェルは Bridge なしで Web の CRUD を開く範囲です。
参考資料
- Hotwire Native: https://native.hotwired.dev/
- Stimulus Handbook: https://stimulus.hotwired.dev/handbook/introduction