Skip to content

Commit 23c7718

Browse files
committed
Use NodeRef as element id with ForEach instead of TupleView
1 parent ff6180b commit 23c7718

File tree

2 files changed

+23
-57
lines changed

2 files changed

+23
-57
lines changed

Sources/LiveViewNative/Property Wrappers/LiveContext.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public struct LiveContext<R: RootRegistry>: DynamicProperty {
5050
public func buildElement(_ element: ElementNode) -> some View {
5151
// can't use coordinator.builder.fromElement here, as it causes an infinitely recursive type when used with custom attributes
5252
// so use ElementView to break the cycle
53-
return ElementView(element: element, context: storage)
53+
return NodeView(node: element.node, context: storage)
5454
}
5555

5656
/// Builds a view representing the children of the current element in the current context.

Sources/LiveViewNative/ViewTree.swift

Lines changed: 22 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -20,34 +20,7 @@ struct ViewTreeBuilder<R: RootRegistry> {
2020
func fromNodes<Nodes>(_ nodes: Nodes, context: LiveContextStorage<R>) -> some View
2121
where Nodes: RandomAccessCollection, Nodes.Index == Int, Nodes.Element == Node
2222
{
23-
let e = nodes
24-
let c = context
25-
switch e.count {
26-
case 0:
27-
EmptyView()
28-
case 1:
29-
f(e[0], c)
30-
case 2:
31-
TupleView((f(e[0], c), f(e[1], c)))
32-
case 3:
33-
TupleView((f(e[0], c), f(e[1], c), f(e[2], c)))
34-
case 4:
35-
TupleView((f(e[0], c), f(e[1], c), f(e[2], c), f(e[3], c)))
36-
case 5:
37-
TupleView((f(e[0], c), f(e[1], c), f(e[2], c), f(e[3], c), f(e[4], c)))
38-
case 6:
39-
TupleView((f(e[0], c), f(e[1], c), f(e[2], c), f(e[3], c), f(e[4], c), f(e[5], c)))
40-
case 7:
41-
TupleView((f(e[0], c), f(e[1], c), f(e[2], c), f(e[3], c), f(e[4], c), f(e[5], c), f(e[6], c)))
42-
case 8:
43-
TupleView((f(e[0], c), f(e[1], c), f(e[2], c), f(e[3], c), f(e[4], c), f(e[5], c), f(e[6], c), f(e[7], c)))
44-
case 9:
45-
TupleView((f(e[0], c), f(e[1], c), f(e[2], c), f(e[3], c), f(e[4], c), f(e[5], c), f(e[6], c), f(e[7], c), f(e[8], c)))
46-
case 10:
47-
TupleView((f(e[0], c), f(e[1], c), f(e[2], c), f(e[3], c), f(e[4], c), f(e[5], c), f(e[6], c), f(e[7], c), f(e[8], c), f(e[9], c)))
48-
default:
49-
forEach(nodes: nodes, context: c)
50-
}
23+
forEach(nodes: nodes, context: context)
5124
}
5225

5326
// alias for typing
@@ -289,52 +262,45 @@ extension View {
289262
}
290263
}
291264

292-
// not fileprivate because it's used by LiveContext
293-
// this cannot be "NodeView" because it's used by forEach which requires element ids, which leaf nodes can't have
294-
internal struct ElementView<R: RootRegistry>: View {
295-
let element: ElementNode
265+
// not fileprivate because it's used by LiveContext.
266+
internal struct NodeView<R: RootRegistry>: View {
267+
let node: Node
296268
let context: LiveContextStorage<R>
297269

298270
var body: some View {
299-
context.coordinator.builder.fromElement(element, context: context)
271+
context.coordinator.builder.fromNode(node, context: context)
300272
}
301273
}
302274

303275
private enum ForEachElement: Identifiable {
304-
case element(ElementNode, String)
305-
case error(Error)
276+
case keyed(Node, id: String)
277+
case unkeyed(Node)
306278

307-
var id: String {
279+
var id: AnyHashable {
308280
switch self {
309-
case .element(_, let id):
310-
return id
311-
case .error(let error):
312-
return error.localizedDescription
281+
case let .keyed(_, id):
282+
return AnyHashable(id)
283+
case let .unkeyed(node):
284+
return AnyHashable(node.id)
313285
}
314286
}
315287
}
316288
// not fileprivate because List needs to use it so it has access to ForEach modifiers
317289
func forEach<R: CustomRegistry>(nodes: some Collection<Node>, context: LiveContextStorage<R>) -> some DynamicViewContent {
318-
let elements: [ForEachElement]
319-
do {
320-
elements = try nodes.map { (node) -> ForEachElement in
321-
guard let element = node.asElement() else {
322-
throw ForEachViewError.invalidNode
323-
}
324-
guard let id = element.attributeValue(for: "id") else {
325-
throw ForEachViewError.missingID
326-
}
327-
return .element(element, id)
290+
let elements = nodes.map { (node) -> ForEachElement in
291+
if let element = node.asElement(),
292+
let id = element.attributeValue(for: "id")
293+
{
294+
return .keyed(node, id: id)
295+
} else {
296+
return .unkeyed(node)
328297
}
329-
} catch {
330-
elements = [.error(error)]
331298
}
332299
return ForEach(elements) {
333300
switch $0 {
334-
case .element(let element, _):
335-
ElementView<R>(element: element, context: context)
336-
case let .error(error):
337-
ErrorView<R>(error)
301+
case let .keyed(node, _),
302+
let .unkeyed(node):
303+
NodeView(node: node, context: context)
338304
}
339305
}
340306
}

0 commit comments

Comments
 (0)