@@ -76,6 +76,7 @@ extension NavigationStack: PrimitiveView {
7676 // captures its navigationTitle
7777 var screens : [ RenderNode ] = [ ]
7878 var titles : [ PropValue ] = [ ]
79+ var searches : [ PropValue ] = [ ]
7980
8081 func resolveScreen( _ view: any View , index: Int , canDismiss: Bool ) {
8182 var childContext = context. descending ( " screen \( index) " )
@@ -87,6 +88,17 @@ extension NavigationStack: PrimitiveView {
8788 childContext. titleSink = sink
8889 screens. append ( Evaluator . resolve ( view, childContext) )
8990 titles. append ( . string( sink. title ?? " " ) )
91+ // Each screen carries its search field descriptor, or an empty array
92+ // when it declared no `searchable`.
93+ if let id = sink. searchCallbackID {
94+ searches. append ( . array( [
95+ . string( sink. searchText ?? " " ) ,
96+ . int( Int ( id) ) ,
97+ . string( sink. searchPrompt ?? " " ) ,
98+ ] ) )
99+ } else {
100+ searches. append ( . array( [ ] ) )
101+ }
90102 }
91103
92104 resolveScreen ( root, index: 0 , canDismiss: false )
@@ -99,7 +111,7 @@ extension NavigationStack: PrimitiveView {
99111 return RenderNode (
100112 type: " NavStack " ,
101113 id: context. path,
102- props: [ " titles " : . array( titles) , " onPop " : . int( Int ( popID) ) ] ,
114+ props: [ " titles " : . array( titles) , " searches " : . array ( searches ) , " onPop " : . int( Int ( popID) ) ] ,
103115 children: screens
104116 )
105117 }
@@ -180,6 +192,31 @@ public extension View {
180192 }
181193}
182194
195+ /// Attaches a search field to the enclosing navigation stack, bound to `text`.
196+ public struct _SearchableView < Content: View > : View {
197+ internal let text : Binding < String >
198+ internal let prompt : String ?
199+ internal let content : Content
200+ public typealias Body = Never
201+ }
202+
203+ extension _SearchableView : _ResolutionEffectView {
204+ public func _applyEffect( _ context: inout ResolveContext ) -> any View {
205+ let binding = text
206+ let id = context. callbacks. register ( . string { binding. wrappedValue = $0 } )
207+ context. titleSink? . searchText = text. wrappedValue
208+ context. titleSink? . searchCallbackID = id
209+ context. titleSink? . searchPrompt = prompt
210+ return content
211+ }
212+ }
213+
214+ public extension View {
215+ func searchable( text: Binding < String > , prompt: String ? = nil ) -> _SearchableView < Self > {
216+ _SearchableView ( text: text, prompt: prompt, content: self )
217+ }
218+ }
219+
183220/// Registers a value-type destination builder with the enclosing stack.
184221public struct _NavigationDestinationView < Content: View , D: Hashable > : View {
185222 internal let build : ( D ) -> any View
0 commit comments