Search code examples
core-dataswiftuiobservedobject

Updating SwiftUI view from Core Data non scalar attribute


I have a Core Data NSManagedObject entity Person with a Bool attribute that I generate as a NSNumber. (the "Use Scalar Type" checkbox is not used, so the Bool attribute becomes an NSNumber) I am trying to observe this attribute, employed, to control the UI.

@ObservedObject var person: Person

var body: some View {
    List {
        Section {
            HStack {
                Toggle(isOn: $person.employed) { <-- 1
                    Text("Showing employed content..")
                }
            }
        }
        if person.employed.boolValue {
            Section { } etc

I get a warning at "1" saying: Cannot convert value of type 'Binding<NSNumber?>' to expected argument type 'Binding<Bool>' How can I make use of the employed attribute as a bool without changing it to a scalar?

Note: $person.employed.boolValue would not work it seems, and I would also have to account for the optional part.


Solution

  • One possible way to do it is via a custom Binding:

    Toggle("Showing employed content..", isOn: Binding<Bool>(get: {
             person.employed?.boolValue == true
          }, set: { value in
             person.employed = NSNumber(value: value)
          }))  
    

    Now, if you prefer this to be a computed property instead, you could do something like this:

    var isEmployed: Binding<Bool> {
        Binding<Bool>(get: {
            person.employed?.boolValue == true
        }, set: { value in
            person.employed = NSNumber(value: value)
        })
    }
    
    var body: some View {
        Toggle("Showing employed content..", isOn: isEmployed)
    }
    

    Also, here is a possible implementation of an extension that handles optional NSNumber backed booleans (excuse my terible naming):

    extension Binding where Value == NSNumber? {
        var boolBinding: Binding<Bool> {
            Binding<Bool>(get: {
                self.wrappedValue == true
            }, set: { value in
                self.wrappedValue = NSNumber(value: value)
            })
        }
    }
    

    which can be used like this:

    Toggle("Showing employed content..", isOn: $person.employed.boolBinding)