Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Picker selection "0" is invalid and does not have an associated tag

Tags:

swift

swiftui

I have a class containing transaction categories (see example code below) from which the user may select during the setup process a subset for use in the app. Later on during transaction entry, if the first category in the class wasn't selected (during setup) and the picker state parameter is initialized to zero, I see the console message "Picker: the selection "0" is invalid and does not have an associated tag, this will give undefined results". In fact if the state parameter is initialized to say 5 and the 5th category wasn't selected during setup it will also give the same warning.

My questions is how to initialize the state parameter so I don't get this picker warning?

From the console message I would think that the problem is with the ForEach loop and not the conditional. But if the conditional if categories.catItem[item].catInUse == true { is removed I don't see the warning.

For example, in the code below if category Cat A is not selected during app setup (catInUse is false) and the transaction entry picker state parameter is @State private var entryCat: Int = 0 I will see the console warning message above. Category Cat A is still in the list--it just isn't displayed.

struct getCategory: View {

    @EnvironmentObject var categories: Categories

    @State private var entryCat: Int = 0

    var body: some View {

        Section(header: Text("Select Category")) {

            Picker(selection: $entryCat, label: Text("")) {
                ForEach(0 ..< categories.catItem.count, id: \.self) { item in
                    if categories.catItem[item].catInUse == true {
                        Text(categories.catItem[item].catName)
                            .bold()
                    }
                }
            }.pickerStyle(MenuPickerStyle())
        }
    }
}



// working categories
struct CatModel:  Codable, Identifiable, Hashable {
    var id = UUID()
    var catNum: Int         // used by setup categories
    var catName: String     // category name
    var catTotal: Double    // category total
    var catBudget: Double   // category budget
    var catPix: String      // sf symbol
    var catInUse: Bool      // catInUse: true = category in use
    var catCustom: Bool     // true = custom category (can be deleted)
}


class Categories: ObservableObject {
    @Published var catItem: [CatModel] {
        didSet {   // save categories
            if let encoded = try? JSONEncoder().encode(catItem) {
                UserDefaults.standard.set(encoded, forKey: StorageKeys.workCat.rawValue)
            }
        }
    }

    var catCount: Int = 0
    init() {

        // read in category data
        if let catItem = UserDefaults.standard.data(forKey: StorageKeys.workCat.rawValue) {
            if let decoded = try? JSONDecoder().decode([CatModel].self, from: catItem) {
                self.catItem = decoded
                return
            }
        }

        catItem = []

        let item0 = CatModel(catNum: 0, catName: "Cat A", catTotal: 0.0, catBudget: 0, catPix: "a.circle", catInUse: false, catCustom: false)
        self.catItem.append(item0)

        let item1 = CatModel(catNum: 1, catName: "Cat B", catTotal: 0.0, catBudget: 0, catPix: "b.circle", catInUse: false, catCustom: false)
        self.catItem.append(item1)

        let item2 = CatModel(catNum: 2, catName: "Cat C", catTotal: 0.0, catBudget: 0, catPix: "c.circle", catInUse: false, catCustom: false)
        self.catItem.append(item2)

        let item3 = CatModel(catNum: 3, catName: "Cat D", catTotal: 0.0, catBudget: 0, catPix: "d.circle", catInUse: false, catCustom: false)
        self.catItem.append(item3)

        let item4 = CatModel(catNum: 4, catName: "Cat E", catTotal: 0.0, catBudget: 0, catPix: "e.circle", catInUse: false, catCustom: false)
        self.catItem.append(item4)

        let item5 = CatModel(catNum: 5, catName: "Cat F", catTotal: 0.0, catBudget: 0, catPix: "f.circle", catInUse: false, catCustom: false)
        self.catItem.append(item5)
    }
}
like image 622
Galen Smith Avatar asked Oct 12 '25 11:10

Galen Smith


1 Answers

I would avoid using an index to access the array and instead use a for each loop. And since you might not have any object to preselect if not any category is in use or the one with value 0 is not in use I would recommend to use an optional selection property instead.

It is better to have the type of the selection property to be an identifier rather than the whole type and since your model conforms to Identifiable then the id is a good choice.

@State private var entryCat: CatModel.ID?

Also since you want to filter your model objects I prefer to do that in a computed property but of course doing it directly in the ForEach is also an option

var categoryList: [CatModel] {
    return categories.catItem.filter(\.catInUse)
}

Then the picker code could be changed to

Picker("", selection: $entryCat) {
    Text("<Nothing selected>").tag(nil as CatModel.ID?)
    ForEach(categoryList) { category in
        Text(category.catName)
            .tag(Optional(category.id))
    }
}.pickerStyle(MenuPickerStyle())
  • I use a Text first to represent the state when entryCat is nil, that is nothing is selected.
  • I use tag to hold the identifier used to set entryCat
  • Since entryCat is optional the tag values needs to be optional as well to match the type

A bit off topic but some suggestions regarding naming off types, type names should start with an uppercase letter so I would use GetCategory and a good name for a model is a noun and don't abbreviate it so here I would use Category instead of CatModel

like image 166
Joakim Danielson Avatar answered Oct 16 '25 08:10

Joakim Danielson



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!