Issue with .sheet() Passing String to a View in SwiftUI 2.0

I wrote an app in SwiftUI last year and it's on the App Store. I am working on it in XCode 12 for iOS 14. When I pass a string to a View in .sheet, the String won't get passed. It worked in SwiftUI 1.0, but now doesn't in SwiftUI 2.0. I tested this by printing the variable right before passing to the view and it shows but doesn't print when the new view is opened in .sheet.

Anyone having this issue?

Running XCode 12 Beta 6 & iOS 14 Beta 7

Accepted Reply

Ok, here's the code.

Thanks for showing your code.

As far as tried, @State variables used in a closure for sheet method do not work in some occasions which are hard to predict.

I'm wondering if it's a bug with SwiftUI.

In my opinion, it definitely is a bug, but not sure if Apple will fix it soon.
Anyway, you should better send a feedback to Apple.


One workaround, pass a Binding instead of a value of @State variables.
Code Block
.sheet(isPresented: self.$showingShipEditor) {
ShipEditingView(shipName: self.$shipSelected) //<- Please do not miss `$`
}


Code Block
struct ShipEditingView: View {
@Binding var shipName: String //<- Put `@Binding` here
//...
}



Replies

Show some code
Ok, here's the code. Part of the first view. You can see that I pass the ship name to a view in .sheet

Code Block
struct ShipEditorView: View {
   
  @Environment(\.managedObjectContext) var managedObjectContext
  @FetchRequest(fetchRequest: Ship.getAllShips()) var ships:FetchedResults<Ship>
   
  @State private var newShip = ""
  @State var shipSelected = ""
   
  @State var showingAddShipAlert = false
  @State var showingShipEditor = false
  @State var edit = false
   
  var body: some View {
    ZStack(alignment: .bottomTrailing){
      VStack{
        List{
          ForEach(self.ships) { ship in
            if self.edit == true {
              Button(action: {
                print("Opens Ship Editor")
                self.shipSelected = ship.shipName!
                self.showingShipEditor = true
              }) {
                Text(ship.shipName!)
              }
              .sheet(isPresented: self.$showingShipEditor) { ShipEditingView(shipName: self.shipSelected) }
            } else {
              NavigationLink(destination: ProductEditorView(shipName: ship.shipName!)) {
                Text(ship.shipName!)
              }
            }
             
          } .onDelete {indexSet in ...


Then the second view. When this view shows up, the ship textfield is blank. I even tried printing the shipName and shipText and nothing shows up.

Code Block
struct ShipEditingView: View {
  let shipName: String
   
  @Environment(\.presentationMode) var mode: Binding<PresentationMode>
  @State var context: NSManagedObjectContext!
   
  @State var shipText = ""
   
  var body: some View {
    NavigationView{
      VStack{
        Form{
          Section(header: Text("Rename Ship")) {
            TextField("Titanic", text: $shipText)
              .font(.title)
              .textFieldStyle(RoundedBorderTextFieldStyle())
          }
        }
        Button(action: {
          self.saveEdits()
          self.mode.wrappedValue.dismiss()
        }) {
          Text("Done")
            .font(.title)
        }
        Spacer()
      }
      .navigationBarTitle(Text("Rename Ship"))
      .onAppear() {
        self.shipText = self.shipName
      }
    }.navigationViewStyle(StackNavigationViewStyle())
  }
   
  func saveEdits() { ...


I'm wondering if it's a bug with SwiftUI. Running Beta 8 and I've noticed that if I tap on the ship I want to edit and dismiss the sheet a few times, then the ship name will show up in the textfield and never fails with different ships after that.

Ok, here's the code.

Thanks for showing your code.

As far as tried, @State variables used in a closure for sheet method do not work in some occasions which are hard to predict.

I'm wondering if it's a bug with SwiftUI.

In my opinion, it definitely is a bug, but not sure if Apple will fix it soon.
Anyway, you should better send a feedback to Apple.


One workaround, pass a Binding instead of a value of @State variables.
Code Block
.sheet(isPresented: self.$showingShipEditor) {
ShipEditingView(shipName: self.$shipSelected) //<- Please do not miss `$`
}


Code Block
struct ShipEditingView: View {
@Binding var shipName: String //<- Put `@Binding` here
//...
}



Hi,

also stumbled across this. Although the workaround with using a binding works. It seems that there is a special function that you can use in this case.

sheet(item:onDismiss:content:)

You can pass a binding to the item parameter and then use that value inside of the content closure e.g.


.sheet(item: $data, onDismiss: nil, content: { data in

                CustomView(withCustomData: data)

            })

This basically also uses a binding. I don't know if it has more benefits but it looks like this function was especially created for passing in dynamic data. The sheet in this case will be displayed if data becomes non-nil so there is no need for an extra variable to toggle showing the sheet.