0

I have some code that I am using, the idea is that the user selects a card they like and a sheet is presented to show more information about that card. The problem is, if I don't have the Text(selectedCard?.name ?? "Not selected") line, it crashes. I was just wondering what I don't understand about Swift/SwiftUI that is making this happen and how I could fix it (as I don't want it to say what card they have selected).

It is where the sheet comes to be presented that it crashes as it finds a nil value for selectedCard but selectedCard should not be nil when that runs. If I add the seemingly unrelated line to display which card has been selected, the value is set fine and it does not crash.

Full code:

import SwiftUI

struct CardsView: View {
    
    @State private var cardlist = [Cards]()
    @State private var showingSheet = false
    @State private var selectedCard: Cards?
    
    let columns = [
        GridItem(.adaptive(minimum: 120))
    ]
    
    var body: some View {
        NavigationView() {
            ScrollView (.vertical, showsIndicators: false) {
                Text("View our full collection of cards below:")
                    .padding(.leading, 20)
                    .frame(maxWidth: .infinity, alignment: .leading)
                
                Text(selectedCard?.name ?? "Not selected") // This line stops it from crashing
                
                    LazyVGrid(columns: columns, spacing: 10) {
                        ForEach(cardlist, id: \.name) { thecard in
                            CardItem(card: thecard)
                                .onTapGesture {
                                    selectedCard = thecard
                                    showingSheet.toggle()
                                }
                        }
                        
                    }
                    .padding([.leading, .trailing])
                
                Spacer()
            }
            
            .navigationTitle("Cards")
        }
        
        .sheet(isPresented: $showingSheet) {
            SpecificCardView(card: selectedCard!)
        }
        
        .onAppear() {
            loadCards()
        }
        
    }
    
    func loadCards() {
        ...
    }
    
}
  • `selectedCard` is an optional. Its value could be `nil`. If you try to use it with a `nil` value, you will get the crash. In the line you have commented, you are giving `Text` an alternate string to display, as it can't display a `nil`. What you have done IS a standard fix. See [this question](https://stackoverflow.com/questions/71010176/handling-optional-values-in-swiftui-text-view-best-practices) from a few days ago. – Yrb Feb 09 '22 at 13:47
  • @Yrb but that doesn't explain why the sheet suddenly starts to work with that `Text` line added. I know I have force unwrapped `selectedCard` when I present the sheet with the `SpecificCardView` but why does adding the `Text` above suddenly stop it from crashing? It is almost like without the `Text` above the variable is not updating. – Danny Franklin Feb 09 '22 at 14:10
  • So, you're saying the crash is on the .sheet modifier, and that crash goes away when you add the text line above? Is the crash on presenting or dismissing the sheet? – jrturton Feb 09 '22 at 14:34
  • Actually, it shouldn't prevent the crash. I don't know what your `SpecificCardView` looks like, but you should handle the optional inside the `SpecificCardView`. The only way for us to test this is with a [Minimal, Reproducible Example (MRE)](https://stackoverflow.com/help/minimal-reproducible-example). – Yrb Feb 09 '22 at 14:36

1 Answers1

1

Your problem is likely due to the view diffing algorithm having a different outcome if you have the text value present, since that would be a difference in the root view. Since you have two pieces of state that represent the sheet, and you are force unwrapping one of them, you're leaving yourself open to danger.

You should get rid of showingSheet, then use the item binding of sheet instead. Make Cards conform to Identifiable, then rewrite the sheet modifier as:

.sheet(item: $selectedCard) { SpecificCardView(card: $0) } 

This will also reset your selected card to nil when the sheet is dismissed.

jrturton
  • 118,105
  • 32
  • 252
  • 268