Filtering on Enum Values in SwiftData Queries

While working on my current app I ran across an issue with SwiftData and filtering. SwiftData has support for enums in your models but you can’t directly sort or filter on those enum values. Hopefully this year will bring some improvements but I will outline my hack to work around this issue with the current versions of Swift/SwiftData. This post is current as of Swift 5.10/Xcode 15.3. I found very limited information on how to deal with this in my quick internet searches so hopefully this is helpful to others. As always, I’m interested in any feedback or suggestion how to do this better.

Let’s say you have a model for some orders you are taking to sell some shirts. That might look something like this:

Notice that two of the fields in our model are enum types. SwiftData will store these values and you can refer to the data stored within them. What you can not do currently is use those values to sort or filter when creating your queries.

Your app might contain something like this to display all your orders in a List view:

That would render as:

All pretty standard stuff.

You may want to be able to filter the list to only show orders with certain values, for example all orders with a given color. So you add the following to your view as an initializer:

This looks pretty good and should result in the orders array only having items that match the specified color when a color is provided. Unfortunately that won’t work and the view will now show nothing. Xcode will not present an error or warning. Everything will build but nothing will be displayed. Your order array will be empty.

You can experiment with many ways to define the predicate but all to no avail. My research indicated that SwiftData is storing the enum as binary data and it can not be used in this manner (hopefully only for now and this improves soon?)

In order to be able to use the values within your enum for sort/filter actions you will need another way. Here is what I have come up with.

Updated Model.

The order model now stores a mirrored value of the enum for color. This value is generated when you specify the color on order creation. 

Using this mirror value you can now filter the orders on the value stored in the color enum as follows:

Updated Init.

Here we are using the mirrored colorString value in our predicate and we’ve defined that value as a string so everything works as expected.

Note that for my purposes I never need to update the color after initialization or I would need to update the mirrored value. It may appear that the mirrored value could be defined as a dynamic property and simply return the rawValue of our color enum but I was unable to get that to work and it crashed the simulator any way I attempted this.

As always when I post one of these hacks I sincerely hope someone comes along and tells me “no dummy just do this..” If you have a better solution I’d love to know. I spent hours on this before I RTFM and found the problem and formulated this hack. It works. I wish it wasn’t necessary.

If you want to play with the example app I used to generate this post you can grab the source code it here 

Patrick McConnell @pmcconnell
🐘 Reply on Mastodon