18 July 2026

Reorderable and Swipeable SwiftUI Collections

Layout4 min read

List has long been the shortest path to common collection interactions in SwiftUI. That convenience can become a constraint when a product needs a dense grid, a custom row style, or a scroll layout that does not behave like a list.

New collection APIs separate interaction from the container. A ForEach marks its items as reorderable, the surrounding container coordinates the gesture, and the data model applies the resulting difference. The same idea extends swipe actions beyond List.

Let the container manage the drag gesture

Add reorderable() to the ForEach that owns the identifiable items. Then add reorderContainer(for:isEnabled:move:) to the container that should coordinate movement.

List {
    ForEach(bookmarks) { bookmark in
        BookmarkRow(bookmark: bookmark)
    }
    .reorderable()
}
.reorderContainer(for: Bookmark.self) { difference in
    applyReorder(difference)
}

The closure is where the visual interaction becomes a data change. Apply the provided difference to the source of truth, then persist the new order if the collection survives app launches. Do not rebuild an order from transient row positions or use the drag gesture as the model update itself. The difference represents the outcome SwiftUI has already animated.

Because the interaction is no longer tied to List, the same pattern can move to a LazyVGrid, a stack, or a custom layout. The data update stays the same while the visual browsing mode changes. That is the real advantage over hand-built drag-and-drop code.

Keep the data model responsible for persistence

Reordering in memory is only half of a user feature. Decide how the order is represented before adding the modifier:

  • an ordered array for a local collection;
  • a sortable position field for a persistent store;
  • a stable ordering operation for a server-backed collection.

For a simple array, applying the difference can be enough. A large or synchronized collection needs an atomic persistence operation, conflict handling, and an order that remains stable after a refresh. The UI API does not remove those requirements. It gives the app a reliable user gesture and a structured change to commit.

Add swipe actions to a custom row layout

Swipe actions now work in any supported container, not only List. Put the action on each item and apply swipeActionsContainer() to the common scroll container.

ScrollView {
    LazyVStack(spacing: 0) {
        ForEach(bookmarks) { bookmark in
            BookmarkRow(bookmark: bookmark)
                .swipeActions {
                    Button("Delete", role: .destructive) {
                        delete(bookmark)
                    }
                }
        }
    }
}
.swipeActionsContainer()

This preserves a custom row's layout while allowing the container to coordinate which item is currently open. Use it for genuinely direct row actions such as archive, pin, or delete. If the action needs extra explanation or affects several records, a context menu or a detail screen is usually clearer.

Present destructive confirmation with the selected item

Alerts and confirmation dialogs can now use the same optional-item binding pattern as sheets. This removes a separate Boolean and avoids racing a selected row against a different value stored elsewhere.

@State private var bookmarkToDelete: Bookmark?
 
.confirmationDialog("Delete bookmark?", item: $bookmarkToDelete) { bookmark in
    Button("Delete", role: .destructive) {
        delete(bookmark)
    }
}

Set bookmarkToDelete when the person chooses the destructive action. SwiftUI presents the dialog while a concrete item exists and provides that item to the action closure. Resetting the binding dismisses the presentation naturally.

The best use of these APIs is not to add more hidden gestures. It is to let a collection take the visual form that suits the content while retaining the familiar interactions people already expect. Keep the data update explicit, make destructive work confirmable, and reserve drag and swipe for actions that remain discoverable in the surrounding design.

References