Tables, Pickers and Bindings, woe my...

I have a Core Data entity with a few properties, the ones of interest here are a UUID column named "id" and an Int16 column containing values between 0 and 3.

I have a Table in SwiftUI which is correctly arranging the items stored by Core Data into its rows. The "selection:" binds a Set of the "id" values.

I have another view to which I have sent the Set of "id" values (Set<Bin.ID>) (Bin is the name of my entity) using @Binding var ... - so far so good.

Within that view, I can determine that I am correctly getting the set of interest, and most of what I am trying to accomplish I can get to work.

What is driving me up the wall, however, is that I am trying to get a Picker in the child view to let me change the value of the Int16 property (called playMode) of the first selected object from the table.

I have tried numerous things to create that binding but I can't seem to find the magic combination that works in any sensible way.

Here is what I am currently doing, which seems to come closest.

I set up a @State variable within the child view that has the same type as the property of the entity and created an initializer that calls a function to find the first selected item and set that state variable, plus a function to update the entity when the state changes:

    @Binding var whichBins: Set<Bin.ID>
    @State var playMode: Int16 = 0

    init(whichBins: Binding<Set<Bin.ID>>)
    {
        self._whichBins = whichBins
        playMode = getPlayMode(whichBins.wrappedValue)
    }

    private func getPlayMode(_ val: Set<Bin.ID>) -> Int16
    {
        if val.count > 0
        {
            do
            {
                let fr = try PersistenceController.shared.container.viewContext.fetch(Bin.fetchRequest(forID: val.first!))

                for bin in fr
                {
                    if (bin.typ == 1)
                    {
                        print("getPlayMode: actual return \(bin.playMode)")
                        return bin.playMode
                    }
                }
            } catch {
                let nsError = error as NSError
                print("getPlayMode fetch error \(nsError), \(nsError.userInfo)")
            }

        }

        print("getPlayMode: default return existing \(playMode)")
        return playMode
    }


    private func setPlayMode(value:Int16)
    {
        print("setPlayMode: value=\(value)")

        if whichBins.count > 0
        {
            do
            {
                let fr = try PersistenceController.shared.container.viewContext.fetch(Bin.fetchRequest(forID: whichBins.first!))

                for bin in fr
                {
                    print("setPlayMode: GO GO GO")
                    bin.playMode = value
                    try? PersistenceController.shared.container.viewContext.save()
                }
            } catch {
                let nsError = error as NSError
                print("setPlayMode fetch error \(nsError), \(nsError.userInfo)")
            }
        }
    }

Within the body of the view, I set up the picker with onChange handlers to update the state variable when the table selection changes and to call the function to set the value in the entity when the picker makes a change:

            Picker("Play Mode:", selection: $playMode)
            {
                Text("Single").tag(Int16(0))
                Text("Repeat One").tag(Int16(1))
                Divider()
                Text("Through End").tag(Int16(2))
                Text("Repeat All").tag(Int16(3))
            }

            .onChange(of: playMode)
            { val in
                setPlayMode(value: val)
            }

            .onChange(of: whichBins)
            { val in
                playMode = getPlayMode(val)
            }

What is happening is that when I switch the selection in the Table, I can see (from the logging output being produced by "print") that the getPlayMode function is returning the intended value - the one stored in the object - and that when I change the value in the Picker, the setPlayMode function is being called with the intended value (which then gets returned by getPlayMode).

However, when I change the table selection, while getPlayMode is returning the value that it should (as evidenced by "actual return"), the Picker is consistently showing the first item in the list - it never comes up with the intended selected item.

What am I missing?

For the benefit of anyone else who runs into this:

To get this working, I had to push the @State var playMode up to the parent view and pass it into this view as a binding.

I then had the parent view detect when a single Bin was selected and use a custom get/set binding to access its value directly rather than using the @State var (but still used the @State var when multiple Bin objects were in the selection list), and upon change to the playMode, had the onChange(of: playMode) toggle a boolean in an environment object in order to cause the view hierarchy to refresh.

I was then able to eliminate the getPlayMode function completely.

This seems to have given me the behavior I wanted, though it does seem a bit convoluted.

SwiftUI as a framework clearly has much room for improvement.

Tables, Pickers and Bindings, woe my...
 
 
Q