TextField with NumberFormatter, bound to Decimal property with Xcode > 13 beta 2.

The NumberFormatter seems to stop working with Decimal type properties since Xcode version 13 beta 3 (13A5192j).

The following code works up to Xcode version 13 beta 2 and then no longer.

Question:

What must be changed in the code so that entered numerical values are stored in the Decimal property again?

Code Example

import SwiftUI

struct Model {
  var decimalQuantity: Decimal = 2.43
  var someText: String = ""
}

struct DecimalBindingView: View {
  @Binding var model: Model
   
  private let numberFormatter: NumberFormatter = {
    let formatter = NumberFormatter()
    formatter.numberStyle = .decimal
    formatter.generatesDecimalNumbers = true
    return formatter
  }()
   
  var body: some View {
    Form {
      Section {
        Label {
          Text("This text field does not work correctly. \nIt doesn't write the value back to the property when the field loses focus or you click Enter.")
        } icon: {
          Image(systemName: "xmark.circle").foregroundColor(Color.red).scaleEffect(1.4)
        }
        .lineLimit(10)
         
        // The formatter or the two-way data binding does not seem to work.
        // The new value is not written back to the property correctly.
        // Question: What needs to be changed to make this work?
        TextField(
          "Input a number of type decimal…",
          value: $model.decimalQuantity,
          formatter: numberFormatter)
      }
      Section {
        TextField(
          "Sample field, so you can leave the other field",
          text: $model.someText)
      }
    }
  }
}

Minimal Xcode Project

https://github.com/brads-dev-dojo/SwiftUITextFieldWithDecimalValueTypeApp

Post not yet marked as solved Up vote post of -bastian- Down vote post of -bastian-
2.8k views
  • I opened a case with incident id FB9471568 via Feedback Assistant on 08 August 2021 and am now hoping for a response from Apple.

  • The problem still exists in the new Xcode version 13.0 beta 5 (13A5212g).

  • The problem still exists in the current Xcode Version 13.0 (13A233).

    Unfortunately, the issue has remained in the Feedback Assistant without an answer. Dear Apple team. Do you plan to restore this feature, or can you suggest a workaround?

Replies

An interesting example. In my opinion, this is a bug of iOS 13 beta 3+ and you should better send a bug report soon.


What must be changed in the code so that entered numerical values are stored in the Decimal property again?

As far as I tried, creating a Binding String <-> Decimal seemed nearly working:

private let decimalFormatter: NumberFormatter = {
//    let formatter = MyNumberFormatter()
    let formatter = NumberFormatter()
    formatter.numberStyle = .decimal
    formatter.generatesDecimalNumbers = true
    return formatter
}()

struct DecimalBindingView: View {
    @Binding var model: Model
    
    var stringBinding: Binding<String>
    
    init(model: Binding<Model>) {
        _model = model
        stringBinding = Binding(get: {
            decimalFormatter.string(for: model.wrappedValue.decimalQuantity) ?? ""
        }, set: {
            if let decimal = decimalFormatter.number(from: $0) as? NSDecimalNumber {
                model.wrappedValue.decimalQuantity = decimal as Decimal
            }
        })
    }
    
    var body: some View {
        Form {
            Section {
                Label {
                    Text("This text field does not work correctly. \nIt doesn't write the value back to the property when the field loses focus or you click Enter.")
                } icon: {
                    Image(systemName: "xmark.circle").foregroundColor(Color.red).scaleEffect(1.4)
                }
                .lineLimit(10)
                
                TextField("Input a number of type decimal…", text: stringBinding)
            }
            Section {
                TextField(
                    "Sample field, so you can leave the other field",
                    text: $model.someText)
            }
        }
    }
}
  • Hello OOPer, Thank you very much for your hints. I tried to develop your example code so that it works for my use case. But in the end, I failed.

    If the text field loses focus or a commit is triggered by the user, the last valid value should be restored in case of invalid data.

    I have not been able to implement this behavior. I find it challenging to find workarounds for such problems when you can't parse the library code (SwiftUI) as it is closed-source.

    Has anyone an idea for a working example?

Add a Comment

As a simple workaround, you could try this:

		struct DecimalBindingView: View {
  			@State private var txt = "" 
  			....

            TextField("Input a number of type decimal…", text: $txt)
              .onChange(of: txt) { val in
                  if let dec = Double(val) {
                      model.decimalQuantity = Decimal(dec)
                  }
              }
                ...
            }