I'm trying pass optional @State
in View with non optional @Binding
for edit it there. I faced with problem Xcode is crushing with Fatal error: Unexpectedly found nil while unwrapping an Optional value
. But when I check optional value before call that view it is not nil: Editing car is set: Optional("Audi A8")
. I checked other SO advices how to solve that problem but nothing helps me to understand what is going wrong... How to pass @State
correctly for edit it in SheetView
?
import SwiftUI
struct Car: Identifiable {
let id = UUID().uuidString
var model: String
var color: String
}
class CarModelView: ObservableObject {
@Published var cars: [Car] = [
.init(model: "Audi A8", color: "Red"),
.init(model: "Honda Civic", color: "Blue"),
.init(model: "BMW M3", color: "Black"),
.init(model: "Toyota Supra", color: "Orange")
]
}
struct CarListView: View {
@StateObject var vm = CarModelView()
@State var editingCar: Car?
var body: some View {
List {
ForEach(vm.cars) { car in
VStack(alignment: .leading) {
Text(car.model)
.font(.headline)
Text(car.color)
.font(.callout)
}
.swipeActions(edge: .trailing) {
Button {
setEditing(car: car)
} label: {
Label("Edit", systemImage: "pencil.circle")
}
}
}
.sheet(item: $editingCar) {
resetEditingCar()
} content: { _ in
SheetView(editingCar: Binding($editingCar)!) // Crash!
}
}
.environmentObject(vm)
}
func setEditing(car: Car) {
editingCar = car
print("Editing car is set: \(String(describing: editingCar?.model))")
}
func resetEditingCar() {
editingCar = nil
print("Editing car should be nil: \(String(describing: editingCar?.model))")
}
}
struct SheetView: View {
@Binding var editingCar: Car
var body: some View {
VStack {
Text("Edit Car Data")
TextField("Model", text: $editingCar.model)
TextField("Color", text: $editingCar.color)
}
}
}
Actually we don't need binding to state here, because it will edit nothing (after sheet close - state will be dropped, so all changes will be lost). Instead we need to transfer a binding to view model item into sheet.
A possible solution is to iterate over view model bindings and use state of binding as activator to inject it as sheet's item into content.
Tested with Xcode 13.4 / iOS 15.5
Here is main part:
@State var editingCar: Binding<Car>? // << here !!
var body: some View {
List {
ForEach($vm.cars) { car in // << binding !!
VStack(alignment: .leading) {
Text(car.wrappedValue.model)
.font(.headline)
Text(car.wrappedValue.color)
.font(.callout)
}
.swipeActions(edge: .trailing) {
Button {
setEditing(car: car) // << binding !!
} label: {
Label("Edit", systemImage: "pencil.circle")
}
}
}
}
.sheet(item: $editingCar) { // << sheet is here !!
resetEditingCar()
} content: {
SheetView(editingCar: $0) // non-optional binding !!!
}