Learn Courses My Dashboard

Picker selector is nil at the beginning, how to handle? receiving runtime error

Hi,

I am receiving a runtime warning like this whenever I launch my application:

My picker displays an object from CoreData with a sting name and a UUID as tag
I defined the selector of the picker as optional UUID
then currently, during onAppear of the picker, I am initializing the value of selector

the code goes like this:

@State var selectedCollectionId : UUID? = nil
    @State var selectedCollection : Collections? = nil
    @State var isAllCollectionSelected = false
    
    
    var body: some View {
        
        
        ScrollView {

            
            HStack {
                
                Picker("Collections", selection: $selectedCollectionId) {
                    ForEach(dB.collections) { col in
                        Text(col.name ?? "No Name").tag(col.id)
                    }
                }.pickerStyle(.menu)
                    .onChange(of: selectedCollectionId) { newValue in
                        if let col = dB.collections.first(where: {$0.id == newValue}) {
                            selectedCollection = col
                        }
                    }
                    .onAppear() {
                        if(dB.collections.count > 0)
                        {
                            selectedCollectionId = dB.collections[0].id
                        }
                    }
                    .disabled(isAllCollectionSelected)
                
                Spacer()
                
                Text("Select All:")
                Image(systemName: isAllCollectionSelected ? "checkmark.square" : "square").onTapGesture {
                    isAllCollectionSelected.toggle()
                }
                
            }

// .....  and so on ....
}}

@mj1240

Hi Michael,

First question:

What does dB.collection contain?

Hi Chris,

dB is an instanve of my View Model which has an observable object array
collection : [Collections]

then this collection is initialize using fetch onInit of the class in the top view

@mj1240

Is the UUID in your CoreData Optional?

I think I unset the optional
image

but in the generated code from CoreData there is ? so I guess its optional.

extension Collections {

    @nonobjc public class func fetchRequest() -> NSFetchRequest<Collections> {
        return NSFetchRequest<Collections>(entityName: "Collections")
    }

    @NSManaged public var id: UUID?
    @NSManaged public var name: String?
    @NSManaged public var dateUpdated: Date?
    @NSManaged public var hasWords: NSSet?

}

Now I am thinking of 2 idea:

make selectedCollectionId instead of @State to @AppStorage
but the error for sure will still occur when AppStorage value is not yet initialize

another idea is to make it binding and set it at its parent view

but if you have idea, I would appreciate your help

thanks,

Mike

OK if you have unset the Optional then you know that there is going to be a UUID associated with the CoreData record.

That being the case you should put your Picker code inside an if statement and say:

if selectedCollectionId != nil {
    //  Picker code goes here
}

Also move the .onAppear code to the closing brace of the HStack so that it fires when the HStack is drawn in the View rather than waiting for the Picker View to be drawn. Does that makes sense?

            HStack {
                
                if selectedCollectionId != nil {
                    Picker("Collections", selection: $selectedCollectionId) {
                        ForEach(dB.collections) { col in
                            Text(col.name ?? "No Name").tag(col.id)
                        }
                    }.pickerStyle(.menu)
                        .onChange(of: selectedCollectionId) { newValue in
                            if let col = dB.collections.first(where: {$0.id == newValue}) {
                                selectedCollection = col
                            }
                        }
                        .disabled(isAllCollectionSelected)
                }

                Spacer()
                
                Text("Select All:")
                Image(systemName: isAllCollectionSelected ? "checkmark.square" : "square").onTapGesture {
                    isAllCollectionSelected.toggle()
                }
                
            }
            .onAppear() {
                if(dB.collections.count > 0)
                {
                    selectedCollectionId = dB.collections[0].id
                }
            }

SwiftUI builds the View progressively but it does it blindingly fast so that you don’t see how many times the View is redrawn as each element takes effect.

Hi Chris,

It worked. Thanks

1 Like