Skip to content

Commit 757eb42

Browse files
committed
Add ComposableView actions for callbacks back into Swift
1 parent a77e0b8 commit 757eb42

1 file changed

Lines changed: 36 additions & 3 deletions

File tree

AndroidSwiftUICore/Sources/AndroidSwiftUICore/Primitives/ComposableView.swift

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,31 +27,64 @@
2727
// renders a visible diagnostic rather than failing silently.
2828
//
2929

30+
/// A callback a custom composable can invoke to send an event back to Swift —
31+
/// the counterpart to a `UIViewRepresentable`'s coordinator. The associated
32+
/// value's type matches the fixed bridge dispatch surface.
33+
public enum ComposableAction {
34+
case void(() -> Void)
35+
case bool((Bool) -> Void)
36+
case double((Double) -> Void)
37+
case int((Int) -> Void)
38+
case string((String) -> Void)
39+
40+
internal func register(in callbacks: CallbackRegistry) -> Int64 {
41+
switch self {
42+
case .void(let action): return callbacks.register(.void(action))
43+
case .bool(let action): return callbacks.register(.bool(action))
44+
case .double(let action): return callbacks.register(.double(action))
45+
case .int(let action): return callbacks.register(.int(action))
46+
case .string(let action): return callbacks.register(.string(action))
47+
}
48+
}
49+
}
50+
3051
public struct ComposableView<Content: View>: View {
3152

3253
internal let name: String
3354
internal let props: [String: PropValue]
55+
internal let actions: [String: ComposableAction]
3456
internal let content: Content
3557

36-
public init(_ name: String, props: [String: PropValue] = [:], @ViewBuilder content: () -> Content) {
58+
public init(
59+
_ name: String,
60+
props: [String: PropValue] = [:],
61+
actions: [String: ComposableAction] = [:],
62+
@ViewBuilder content: () -> Content
63+
) {
3764
self.name = name
3865
self.props = props
66+
self.actions = actions
3967
self.content = content()
4068
}
4169

4270
public typealias Body = Never
4371
}
4472

4573
public extension ComposableView where Content == EmptyView {
46-
init(_ name: String, props: [String: PropValue] = [:]) {
47-
self.init(name, props: props) { EmptyView() }
74+
init(_ name: String, props: [String: PropValue] = [:], actions: [String: ComposableAction] = [:]) {
75+
self.init(name, props: props, actions: actions) { EmptyView() }
4876
}
4977
}
5078

5179
extension ComposableView: PrimitiveView {
5280
public func _render(in context: ResolveContext) -> RenderNode {
5381
var props = self.props
5482
props["name"] = .string(name) // reserved: identifies the registered factory
83+
// Each action registers a callback; its id crosses as a prop the factory
84+
// reads back as a typed lambda.
85+
for (key, action) in actions {
86+
props[key] = .int(Int(action.register(in: context.callbacks)))
87+
}
5588
return RenderNode(
5689
type: "Composable",
5790
id: context.path,

0 commit comments

Comments
 (0)