Callbacks
SwiftFlow emits events through closure callbacks. All callbacks are optional — only set the ones you need.
Change Callbacks
These are the core callbacks for state mutation. They emit change descriptors that you apply to your state.
onNodesChange
onNodesChange: (([NodeChange<NodeData>]) -> Void)?Called when node positions, selections, or dimensions change. Apply changes with applyNodeChanges(_:nodes:):
SwiftFlow(
nodes: nodes, edges: edges,
onNodesChange: { changes in
nodes = applyNodeChanges(changes, nodes: nodes)
}
) { ... }onEdgesChange
onEdgesChange: (([EdgeChange<EdgeData>]) -> Void)?Called when edge selections change or edges are removed. Apply with applyEdgeChanges(_:edges:):
onEdgesChange: { changes in
edges = applyEdgeChanges(changes, edges: edges)
}onConnect
onConnect: ((Connection) -> Void)?Called when a user completes a valid connection between two handles. Create an edge with addEdge(_:edges:defaults:):
onConnect: { connection in
edges = addEdge(connection, edges: edges)
}
// With default edge options
onConnect: { connection in
edges = addEdge(connection, edges: edges,
defaults: DefaultEdgeOptions(type: .smoothstep, markerEnd: .arrowClosed))
}onReconnect
onReconnect: ((Edge<EdgeData>, Connection) -> Void)?Called when a user drops a dragged edge endpoint on a valid handle. Endpoint anchors only appear when onReconnect is set, the edge is reconnectable and selected, and nodesConnectable is on. The callback receives the original edge; the edge is not removed when the drag starts, dropping elsewhere leaves it unchanged, and onConnect is not used as a fallback:
onReconnect: { edge, connection in
edges = reconnectEdge(edge, connection, edges)
}Viewport Callbacks
onViewportChange
onViewportChange: ((Viewport) -> Void)?Fires on every viewport change (pan, zoom). Use to persist viewport state:
onViewportChange: { viewport in
saveViewportToDisk(viewport)
}onMoveStart / onMove / onMoveEnd
onMoveStart: ((Viewport) -> Void)?
onMove: ((Viewport) -> Void)?
onMoveEnd: ((Viewport) -> Void)?Fine-grained viewport movement tracking:
onMoveStart: { _ in showGrid = true },
onMove: { vp in updateCursorPosition(vp) },
onMoveEnd: { _ in showGrid = false }Selection Callbacks
onSelectionChange
onSelectionChange: (([Node<NodeData>], [Edge<EdgeData>]) -> Void)?Fires when the set of selected nodes or edges changes:
onSelectionChange: { selectedNodes, selectedEdges in
showProperties = !selectedNodes.isEmpty
}onPaneClick
onPaneClick: (() -> Void)?Called when the user taps empty canvas space (also when selection is disabled):
onPaneClick: {
selectedNodeId = nil
}Node Interaction Callbacks
onNodeClick / onNodeDoubleClick
onNodeClick: ((Node<NodeData>) -> Void)?
onNodeDoubleClick: ((Node<NodeData>) -> Void)?Called when a node is tapped or double-tapped. onNodeClick fires even when selection is disabled:
onNodeClick: { node in
openNodeEditor(for: node.id)
},
onNodeDoubleClick: { node in
toggleNodeExpansion(node)
}onNodeDragStart / onNodeDrag / onNodeDragStop
onNodeDragStart: ((Node<NodeData>) -> Void)?
onNodeDrag: ((Node<NodeData>) -> Void)?
onNodeDragStop: ((Node<NodeData>) -> Void)?Track individual node drag lifecycle:
onNodeDragStart: { _ in isDragging = true },
onNodeDrag: { node in updatePreview(node.position) },
onNodeDragStop: { _ in isDragging = false }onNodeMouseEnter / onNodeMouseLeave
onNodeMouseEnter: ((Node<NodeData>) -> Void)?
onNodeMouseLeave: ((Node<NodeData>) -> Void)?macOS only. Fires when the mouse enters or leaves a node:
onNodeMouseEnter: { node in hoveredNodeId = node.id },
onNodeMouseLeave: { _ in hoveredNodeId = nil }onNodeContextMenu
onNodeContextMenu: ((Node<NodeData>) -> Void)?Fires on right-click (macOS) or long-press (iOS) on a node:
onNodeContextMenu: { node in
showContextMenu = true
contextMenuNodeId = node.id
}Edge Interaction Callbacks
onEdgeClick / onEdgeDoubleClick
onEdgeClick: ((Edge<EdgeData>) -> Void)?
onEdgeDoubleClick: ((Edge<EdgeData>) -> Void)?Double-clicking an edge only calls onEdgeDoubleClick; it does not delete the edge. onEdgeClick fires even when elementsSelectable is false.
onEdgeMouseEnter / onEdgeMouseLeave
macOS only. Same semantics as node hover events.
onEdgeContextMenu
onEdgeContextMenu: ((Edge<EdgeData>) -> Void)?Fires on right-click (macOS) or long-press (iOS) on an edge.
Connection Drawing Callbacks
onConnectStart / onConnectEnd
onConnectStart: ((OnConnectStartParams) -> Void)?
onConnectEnd: (() -> Void)?Track the connection drawing lifecycle:
onConnectStart: { params in
// params.nodeId, params.handleId, params.handleType
showConnectHint = true
},
onConnectEnd: {
showConnectHint = false
}isValidConnection
isValidConnection: ((Connection) -> Bool)?Validate connections before they are created:
isValidConnection: { connection in
// Prevent self-connections
guard connection.source != connection.target else { return false }
// Prevent duplicate edges
let exists = edges.contains { existing in
existing.source == connection.source
&& existing.target == connection.target
}
return !exists
}Deletion Callbacks
onBeforeDelete
onBeforeDelete: (([Node<NodeData>], [Edge<EdgeData>]) async -> BeforeDeleteResult<NodeData, EdgeData>?)?Async validation before deletion. Return .cancel to abort, or .delete(nodes:edges:) with a (possibly filtered) subset:
onBeforeDelete: { nodesToDelete, edgesToDelete in
// Show confirmation dialog
let confirmed = await showDeleteConfirmation(nodesToDelete)
if confirmed {
return .delete(nodes: nodesToDelete, edges: edgesToDelete)
} else {
return .cancel
}
}onNodesDelete / onEdgesDelete
onNodesDelete: (([Node<NodeData>]) -> Void)?
onEdgesDelete: (([Edge<EdgeData>]) -> Void)?Fires when nodes or edges are deleted:
onNodesDelete: { deletedNodes in
undoManager.pushUndo(deletedNodes)
}onDelete
onDelete: (([Node<NodeData>], [Edge<EdgeData>]) -> Void)?Fires after deletion completes, receiving both deleted nodes and edges:
onDelete: { deletedNodes, deletedEdges in
saveToHistory(deletedNodes, deletedEdges)
}Selection Drag Callbacks
Fires during multi-node drag operations:
onSelectionDragStart: (([Node<NodeData>]) -> Void)?
onSelectionDrag: (([Node<NodeData>]) -> Void)?
onSelectionDragStop: (([Node<NodeData>]) -> Void)?Other Callbacks
onInit
onInit: (() -> Void)?Fires once when the canvas finishes initializing:
onInit: {
loadSavedGraph()
}onError
onError: ((String, String) -> Void)?Receives error code and message for internal errors:
onError: { code, message in
print("[SwiftFlow Error \(code)] \(message)")
}onPaneContextMenu
onPaneContextMenu: (() -> Void)?Fires on right-click (macOS) or long-press (iOS) on empty canvas space.