I know this is a few months old, but I've been looking at this too. (On a side note, I think it's abysmal that Apple didn't include a per character filter and a way to reject the value.)
I ended up writing a floating point parser (simple state machine) to validate text. It returns true if the text is a valid starting part of the number and has a computed property that tells me it's a complete number of not. This is required because "-" is a valid beginning of a floating point number, so is ".", etc. Of course ".." is not so you have to have something to validate these strings.
Because there is no per character event that I could find, I store the previous value (keep in mind that it can be shorter or longer - they user may enter a character and delete it). If the new value is not valid, I set the old value.
The examples I've seen here, may not work when you have an observable object set as an environment variable. I ran into infinite loops - so I only assign the new value if it's different than the current.
Here's my onReceive for the TextField.
.onReceive(Just(data.text))
{ newValue in
/* Filter the new value */
let filtered = newValue.filter { Set(Self.allowedChars).contains($0) }
/* If valid or empty, save the old value and set the new */
if (filtered.count == 0) || (validator.check(value: filtered))
{
oldValue = data.text
if self.data.text != filtered
{
self.data.text = filtered
}
}
/* Otherwise, set the old value */
else
{
self.data.text = oldValue
}
}
Validator checks the string to ensure it is part of, or a complete floating point number. Mine allows numbers like -0.355e-05.
Now if I only knew what keyboard I was going to use. There doesn't appear to be a standard keyboard (except for the full one) that can be used for floating point.