SwiftUI & SwiftData: Select a Table Row and Show Details
Getting a SwiftData object by its ID isn't as straightforward as you'd think.
Intro
I was building a little macOS prototype app for (yet another) bookmarks manager recently. I thought implementing a common UX would be straightforward:
- See a table of data
- Select a row
- See details in an inspector panel
There are some modern clean SwiftUI components that should make this easy. Table (not to be confused with the older UITableView) shows a table and allows for selecting a row. There’s an inspector() modifier for a clean looking inspector panel.
And I wanted the data in the app to be powered by SwiftData, which is similarly simple and awesome. Apple says it’s “designed to integrate seamlessly with SwiftUI”, but as we’ll see in a sec, it’s not always so straightforward.
If you’re not familiar with SwiftData, the esteemed Paul Hudson has a fantastic crash course.
So, SwiftUI Table + Inspector + SwiftData. Let’s go.
Building the Table
If our SwiftData model is this:
We can list the bookmarks in a table with a simple view like this:
Remember for SwiftData, you also have to add a modelContainer()
modifier on your main view, so if you’re following along be sure to have something like this:
Let’s add a button to populate some sample data. Here’s a simple function we’ll put in our view:
We can add the button to the toolbar with this modifier on the Table
:
Cool! So far so good.
Adding the Inspector
Now let’s get that inspector showing, with this modifier on the Table
as well…
We’ll replace that .constant(true)
binding in a minute, but at least now we can see the inspector showing up correctly.
Here’s what we’ve got so far:
Selection on a Table
I assumed I could just bind to a Bookmark itself for the selection, but Apple says that
Binding to a single instance of the table data’s
id
type creates a single-selection table.
So we need to bind to an ID. Looks like we can do something like this, because SwiftData objects come with an identifier fo’ free.
And now a row highlights when we click on it!
But how do we load the details for a bookmark in the inspector panel?
Loading Details in the Inspector
This is where working with SwiftData isn’t as straightforward as I’d like. You’d think you’d use some kind of modelContext.fetch()
method with a #Predicate
comparing IDs, but nope. Thankfully, our hero Paul helped me figure out how to look up a SwiftData object by its identifier. We need our Inspector to look like this:
After using SwiftData for a year now, I’ve never used that kind of lookup. But there ya have it, learned something new. I assume it has something to do with the ID being a different underlying data type, but that’s above my pay grade.
Now we can click on a row, and the details correctly populate in the Inspector. Woohoo!
Editable Details
If you wanted to make the details editable, you could do something like this with SwiftData’s @Bindable
property wrapper and a subview.
We did it! Now we can move on to other more exciting things.
Full Code
Here’s all the code for reference: