VisitProposalを用いたルート決定ハンドラへの情報拡充
このPRは、ルート判定ハンドラが受け取る情報を VisitProposal に拡張し、location だけでなく訪問オプションやパス設定、バンドルまで利用できるようにした点が中心です。これにより、カスタムハンドラはより包括的な判断が可能になります。
背景
既存の RouteDecisionHandler は location 文字列のみを受け取り、ルーティングロジックの自由度が制限されていました。 例えば、訪問オプションやパス設定プロパティを考慮したいケースで、ハンドラは追加情報を取得できず、アプリ側で余計な処理を挟む必要がありました。この制約は、カスタムハンドラ実装や customRouteDecision のオーバーライドで顕在化していました。結果として、統一的な拡張ポイントが欠如し、コードの重複や保守コストが増大していました。
そこで、訪問に関わる全情報をひとまとめにしたデータクラスを導入し、ハンドラへ渡す方針が取られました。 この方針は、既存の location 文字列は proposal.location として引き続き参照可能にしつつ、将来的な拡張を見据えた設計です。実装側では API 変更に伴う破壊的変更が発生しますが、明示的なシグネチャ変更により移行が容易になるよう整理されています。
技術的な変更
VisitProposal データクラスが新規追加され、location、options、properties、bundle を保持します。
package dev.hotwire.core.turbo.visit
import android.os.Bundle
import dev.hotwire.core.turbo.config.PathConfigurationProperties
data class VisitProposal(
val location: String,
val options: VisitOptions,
val properties: PathConfigurationProperties,
val bundle: Bundle?
)
このクラスは訪問に必要な全情報を一元化し、ハンドラの引数として提供されます。
複数のシグネチャが VisitProposal に置き換えられました。 HotwireDestination.customRouteDecision、Router.RouteDecisionHandler.matches/handle、Navigator.getRouteDecision などがすべて proposal: VisitProposal を受け取る形に変更され、旧来の location: String は削除されています。たとえば Router のインターフェースは次のように更新されています。
interface RouteDecisionHandler {
fun matches(proposal: VisitProposal, configuration: NavigatorConfiguration): Boolean
fun handle(proposal: VisitProposal, configuration: NavigatorConfiguration, activity: HotwireActivity): Decision
}
この変更により、ハンドラは提案全体を元に判定・処理を行えるようになります。
Navigator の内部ロジックが VisitProposal の生成と伝搬に合わせて改修されました。 ルート決定前に VisitProposal インスタンスを組み立て、currentDestination?.customRouteDecision(proposal) へ渡すことで、既存のカスタムロジックが新データに適応します。
private fun getRouteDecision(
location: String,
options: VisitOptions,
bundle: Bundle?
): Router.Decision {
val proposal = VisitProposal(
location = location,
options = options,
properties = Hotwire.config.pathConfiguration.properties(location),
bundle = bundle
)
val customDecision = currentDestination?.customRouteDecision(proposal)
// ...
}
この流れは全ハンドラ実装に統一的に適用されます。
各 RouteDecisionHandler 実装が proposal を使用するよう修正されました。 例えば AppNavigationRouteDecisionHandler は proposal.location を基にドメイン比較を行い、BrowserTabRouteDecisionHandler と SystemNavigationRouteDecisionHandler も同様に proposal.location を参照しています。テストコードも同様に VisitProposal を生成して検証しています。
設計判断
VisitProposal を中心に据える設計は、情報の一元化と拡張性確保を目的としています。 ハンドラが個別に VisitOptions や PathConfigurationProperties を取得しに行く必要がなくなり、インターフェースがシンプルかつ表現力豊かになりました。さらに、location は依然として proposal.location で取得できるため、後方互換性はコード側で明示的に保たれています。
API 変更は破壊的ですが、意図的にシグネチャを統一したことで移行コストを明確化しています。 変更が必要な箇所は RouteDecisionHandler 実装と HotwireDestination のオーバーライドだけで、他のコンポーネントは VisitProposal の生成ロジックを介して自動的に対応します。この設計は将来的に更なるプロパティ追加が容易になる点でも評価できます。
まとめ
VisitProposal の導入により、ルート決定ハンドラは訪問全体のコンテキストを取得でき、より高度なルーティングロジックが実装可能になりました。API が変更されたことはありますが、既存の location 参照はそのまま残されているため、移行は明確です。今後、ハンドラ側での拡張やカスタマイズがシンプルになることが期待されます。