dragology-swift

An unofficial Swift port of Dragology. Dragology and declarative dragging were created by Joshua Horowitz, Andrew Blinn, and Jeffrey Heer, who aren't involved in this port.

Unofficial port

Tell SwiftUI what an element can turn into, and let the drag work itself out.

Good drag interactions usually take a lot of careful code: hit regions, offsets, snapping, animation. Declarative dragging is a different way to build them, from a UIST 2026 paper. You draw your state, and list the states each element can be dragged to. The library does the geometry.

This package brings that idea to iOS. It's a from-scratch Swift port of the web library, with every drag operator, a SwiftUI host, and a gallery of more than thirty demos, most of them adapted from the original.

Permutation Lab, driven by real touches on an iPhone simulator.

A whole switch

This is all the code for a toggle. Nothing in it mentions touches, offsets, or animation.

func toggle(_ context: RenderContext<ToggleState>) -> Node<ToggleState> {
    group {
        rect(width: 120, height: 60, cornerRadius: 30)
            .fill(context.state.isOn ? RGBA(hex: 0x22C55E) : RGBA(hex: 0xD1D5DB))

        circle(radius: 26)
            .id("knob")
            .fill(.white)
            .transform(.translate(context.state.isOn ? 90 : 30, 30))
            .onDrag {
                context.d.between([ToggleState(isOn: false), ToggleState(isOn: true)])
            }
    }
}

DraggableCanvas(state: $state, draggable: toggle)

How it works

When a drag starts, the library calls your own render function on each state you listed. Where the element lands in each of those renders tells it where the targets are. Those few positions are enough to build the whole interaction.

isOn: false isOn: true the states you listed rendered, to find positions blended into a live drag your render function does the measuring
The drag comes from the same code that draws the scene, so it keeps working when the layout changes.

Drags are also composable. Small behaviors combine into bigger ones, so d.closest(spots).withFloating().whenFar(free) reads as what it does: float over the nearest spot, and move freely when far from all of them.

SwiftUI views can't be rendered speculatively or blended, so scenes are built from Node, a plain-data tree that plays the part of SVG, and drawn by a SwiftUI Canvas. The docs have the details.

Demos

These clips are from the demo app, which you can open in Xcode and run.

algebra twisted trees bézier curve braid card piles face trominoes sprouting tree permutation lab

Get started

Add the package to your app. It needs iOS 17 or macOS 14.

.package(url: "https://github.com/jeslyvarghese/dragology-swift", from: "0.2.0")

Then follow Getting Started. The operator reference has the full vocabulary.

Status

This is an early release. A few things to know before you depend on it:

The docs list these, along with how it differs from the web version.

Credits

The ideas here belong to Joshua Horowitz, Andrew Blinn, and Jeffrey Heer. If you use this in research, please cite their paper:

@inproceedings{horowitz2026declarative,
  title     = {Declarative Dragging},
  author    = {Horowitz, Joshua and Blinn, Andrew and Heer, Jeffrey},
  booktitle = {Proceedings of the 39th Annual ACM Symposium on
               User Interface Software and Technology (UIST '26)},
  year      = {2026}
}