BLACK FRIDAY: Save 50% on all my Swift books and bundles! >>

Weird value update issue with TextField

Forums > SwiftUI

I am quite new to SwiftUI but allready bumping my head on the wall with a strange issue.

I am displaying a list of items in a NavigationSplitView. And I want to update the content of my items in the associated detailed view. My datamodel is quite simple :

struct Item: Codable, Identifiable {
    var id = UUID()

    var description: String
    var quantity: Int
}

I am using 2 TextFields to display the Item content :

TextField(text: $item.description) {
    Text("Description: ")
}
.onSubmit(updateItem)

TextField(value: $item.quantity, format: .number) {
    Text("Quantity: ")
}
.onSubmit(updateItem)

the updateItem function is simply updating my item in the items list. description is updating correctly but not quantity. I thought something was going wrong with the formatter. So I change the code to be :

@State var nb = 0

[...]
TextField(text: $item.description) {
    Text("Description: ")
}
.onSubmit(updateItem)

TextField(value: $nb, format: .number) {
    Text("Quantity: ")
}
.onSubmit {
print(nb)                                   // nb is correctly updated with the value in the TextField
item.quantity = nb                   // item is not modified !!!!!!
print(item.quantity)                  // item still contains the old quantity value
updateItem()

The value from the TextField is updated correctly but item can't be updated with it.... Am I missing something here ?

2      

is the item variable marked with @state?

2      

Your data model is a struct, which means the detail view gets a copy of it.

You do not reveal how you modeled your list of items, nor the process for selecting an item within the first column of your list. We'll need more clues.

In the meantime, you may want to check this excellent tutorial from Stewart Lynch on NavigationSplitViews.

See -> NavigationSplitViews
Be sure to give him a thumbs up, if his video teaches you new SwiftUI concepts.

Please return here and tell us how you solved your issue.

2      

Ok let's add a little more detail.

my list of items is a simple array, which is stored in a global Object for now. At first I passed the item directely as a binding to the detailed view. Since it was not working I get back to the Item Id to handle a fresh and independant copy in the detailView.

@State var itemList: Array<Item> = db.getItems()
@State private var selectedItem:Item.ID? = nil

@State private var isUpdatingItem:Bool = false

var body: some View {
  NavigationSplitView() {
    List(itemList, id: \.id, selection: $selectedItem) { item in
      NavigationLink(value: item) {
        Text(item.description)
      }
    }
   } detail: {
             ItemView(selectedItem: $selectedItem, isUpdated: $isUpdatingItem)
   }
}

In ItemView, a fresh copy of the Item is first retrieved from the same global db Objects based on its id. So far everything is good. The updateItem() function is supposed to update the itemList with the new value. But the problem is that the item object is not holding the right value properties when it is called.

    @Binding var selectedItem: Item.ID?
    @Binding var isUpdated: Bool

    @State private var item: Item = Item()

    [...]
    VStack {
      TextField(text: $item.description) {
        Text("Description: ")
      }
      .onSubmit(updateItem)

      TextField(value: $nb, format: .number) {
        Text("Quantity: ")
      }
      .onSubmit {
        print(nb)                                   // nb is correctly updated with the value in the TextField
        item.quantity = nb                   // item is not modified !!!!!!
        print(item.quantity)                  // item still contains the old quantity value
        updateItem()
      }
    }
    .onAppear(perform: loadData)
    [...]

    func loadData() {
      item = db.getItem(id: selectedItem)
    }

    func updateItem() {
      db.updateItem(id: selectedItem, withValue: item)
      isUpdated.toggle()
    }

What I clearly not understand here is why the description property is updated correctly, but not the quantity property. The only difference is that one is a String while the other is an Int.

2      

I might experiment with:

.onSubmit {
        print(nb)                                   
        $item.quantity = nb        // <-- write to @State
        print(item.quantity)                 
        updateItem()
      }

2      

This doesn't help either : "Cannot assign through dynamic lookup property: '$item' is immutable"

2      

I finaly found a workaround. Instead of updating item properties value, I simply instantiated a new Item to replace the initial one on updates. It works well.

Looks like the additional overhead of formating text had some side effects when updating state variable values.

2      

Save 50% in my WWDC sale.

SAVE 50% All our books and bundles are half price for Black Friday, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.