I have a custom UIGestureRecognizerRepresentable that causes a crash. Only happens about once a week, and that’s with about a thousand times it gets invoked. But it’s persistent and annoying when it happens. The code looks like
func handleUIGestureRecognizerAction(_ recognizer: Recognizer, context: Context) {
let startLocation = context.converter.convert(globalPoint: recognizer.startLocation, to: coorinateSpace)
coorinateSpace is set in init and has crashed on .global, .local, and .named("foo")
The convert function gets caught in a AGGraphGetValue loop before finally failing. It has happened while attached to the debugger, but there is not any more information than is in the logs. I don’t even know why it would invoke AttributeGraph in the first place.
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libsystem_kernel.dylib 0x23aedd1d0 __pthread_kill + 8
1 libsystem_pthread.dylib 0x1eb0cf7dc pthread_kill + 268
2 libsystem_c.dylib 0x197a89c98 abort + 148
3 AttributeGraph 0x1befffef0 AG::precondition_failure(char const*, ...) + 216
4 AttributeGraph 0x1bf000f68 AG::Graph::input_value_ref_slow(AG::data::ptr<AG::Node>, AG::AttributeID, unsigned int, unsigned int, AGSwiftMetadata const*, unsigned char&, long) + 936
5 AttributeGraph 0x1beffb2a8 AGGraphGetValue + 232
6 SwiftUICore 0x19626eee8 specialized UnaryLayoutComputer.updateValue() + 92
7 SwiftUICore 0x196bdc9cc specialized implicit closure #1 in closure #1 in closure #1 in Attribute.init<A>(_:) + 24
8 AttributeGraph 0x1bf001914 AG::Graph::UpdateStack::update() + 496
9 AttributeGraph 0x1bf00152c AG::Graph::update_attribute(AG::data::ptr<AG::Node>, unsigned int) + 352
10 AttributeGraph 0x1bf000e68 AG::Graph::input_value_ref_slow(AG::data::ptr<AG::Node>, AG::AttributeID, unsigned int, unsigned int, AGSwiftMetadata const*, unsigned char&, long) + 680
11 AttributeGraph 0x1beffb2a8 AGGraphGetValue + 232
12 SwiftUICore 0x196287604 specialized UnaryChildGeometry.value.getter + 92
13 SwiftUICore 0x196a21bc0 specialized implicit closure #1 in closure #1 in closure #1 in Attribute.init<A>(_:) + 52
14 AttributeGraph 0x1bf001914 AG::Graph::UpdateStack::update() + 496
15 AttributeGraph 0x1bf00152c AG::Graph::update_attribute(AG::data::ptr<AG::Node>, unsigned int) + 352
16 AttributeGraph 0x1bf000e68 AG::Graph::input_value_ref_slow(AG::data::ptr<AG::Node>, AG::AttributeID, unsigned int, unsigned int, AGSwiftMetadata const*, unsigned char&, long) + 680
17 AttributeGraph 0x1beffb2a8 AGGraphGetValue + 232
18 SwiftUICore 0x196a21b28 specialized implicit closure #1 in closure #1 in closure #1 in Attribute.init<A>(_:) + 72
19 AttributeGraph 0x1bf001914 AG::Graph::UpdateStack::update() + 496
20 AttributeGraph 0x1bf00152c AG::Graph::update_attribute(AG::data::ptr<AG::Node>, unsigned int) + 352
21 AttributeGraph 0x1beffb560 AG::Graph::value_ref(AG::AttributeID, unsigned int, AGSwiftMetadata const*, unsigned char&) + 296
22 AttributeGraph 0x1beffb2f8 AGGraphGetValue + 312
23 AttributeGraph 0x1bf00206c AGGraphGetInputValue + 60
24 SwiftUICore 0x19626d0a8 GeometryProxy.transform.getter + 220
25 SwiftUICore 0x19699ce80 GeometryProxy.convert<A>(globalPoint:to:) + 76
26 SwiftUI 0x195ddf71c UIGestureRecognizerRepresentableCoordinateSpaceConverter.convert<A>(globalPoint:to:) + 76
27 myapp.debug.dylib 0x1033b3a30 MyAppDragGesture.handleUIGestureRecognizerAction(_:context:) + 1560
Hi @rahmcoff,
This is not a bug in the convert call itself — it's a re-entrancy / lifetime problem with the Context's coordinate-space converter. The crash happens because context.converter.convert(globalPoint:to:) ends up reading a SwiftUI GeometryProxy/UnaryLayoutComputer value out of the AttributeGraph, and the attribute it tries to read is being evaluated at a moment when its dependencies are not in a valid, resolved state.
One thing to keep in mind is that AG::precondition_failure → abort() is AttributeGraph's way of saying "you asked the graph for a value while the graph was mid-update or the node has been invalidated."
The reason this abort() is intermittent (~1 in 1000) is that it only triggers when the gesture action fires while a layout pass affecting that representable is in flight, or after the view providing that coordinate space has been removed/recycled.
I'd suggest you consider one of the following options:
-
The code captures
coordinateSpace(or the converter) and are using it at the wrong time. You mentioncoordinateSpaceis set ininit. If you stored a.named("foo")space, that name has to resolve to a view that is currently installed in the hierarchy. During a transition, push/pop, or list-cell recycling, that named space can briefly not exist while the gesture is still firing → graph precondition failure..globaland.localcrashing as well points to the same timing issue rather than the space identity. -
The action is firing slightly off the main run loop's layout state. For example, a gesture continuing to deliver
.changed/.endedwhile the representable is being torn down or re-laid-out. The converter's backing geometry node is invalidated, but the recognizer still has a reference to a staleContext. -
The code is reading the converter from a retained
Context.Context(and its converter) are only valid for the duration of the callback. Holding onto context and calling convert later is undefined and matches this failure signature exactly.
If you have any further questions, please let me know.
Cheers,
Paris X Pinkney | WWDR | DTS Engineer