Skip to content

Commit e31abe6

Browse files
committed
Add searchable attaching a search field to the navigation stack
1 parent 725f09d commit e31abe6

1 file changed

Lines changed: 38 additions & 1 deletion

File tree

AndroidSwiftUICore/Sources/AndroidSwiftUICore/Navigation.swift

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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.
184221
public struct _NavigationDestinationView<Content: View, D: Hashable>: View {
185222
internal let build: (D) -> any View

0 commit comments

Comments
 (0)