Search code examples
swiftswiftdata

How to persist an OptionSet with SwiftData?


Edit: Fixed with iOS 18.1 Beta 5 (Build: 22B5054e).

Let's create a simple SwiftData model with a codable OptionSet.

@Model
final class Item {
    var options: SomeOptions
    
    struct SomeOptions: OptionSet, Codable {
        let rawValue: Int
    }
    
    init(options: SomeOptions = .init(rawValue: .random(in: 0..<10))) {
        self.options = options
    }
}

When killing then relaunching the app, all items options rawValues equal 0.

struct ContentView: View {
    @Environment(\.modelContext) private var modelContext
    @Query private var items: [Item]

    var body: some View {
        NavigationStack {
            List(items) { item in
                Text(item.options.rawValue.formatted()) // FIXME: this always diplays "0"
            }
            .toolbar {
                ToolbarItem {
                    Button("Add item", systemImage: "plus", action: addItem)
                }
            }
        }
    }

    private func addItem() {
        withAnimation {
            let newItem = Item()
            modelContext.insert(newItem)
            try! modelContext.save() // FIXME: if not saved, the OptionSet always equals 0 too
        }
    }
}

Any idea of what's going on please?

Tested with Xcode 15.0.


Solution

  • This looks like a SwiftData bug to me. One workaround for now is to store the raw value of the OptionSet instead and use a transient property of the custom type to access it.

    private var optionsStorage: SomeOptions.RawValue
    
    @Transient
    var options: SomeOptions {
        get { SomeOptions(rawValue: optionsStorage) }
        set { optionsStorage = newValue.rawValue }
    }
    

    We also need to adjust the initialisation

    init(options: SomeOptions) {
        self.optionsStorage = options.rawValue
    }