Binding<String>, set, get?

I have three sets of Text and TextField. And I need to filter each TextField entry. I have gotten a function to filter the TextField entry from this website (https://zenn.dev/yorifuji/articles/swiftui-textfield-filter). Finally, I have the following lines of code.

import SwiftUI

struct ContentView: View {
	@State var username = ""
	@State var password = ""
	@State var tenantID = ""
	
	var body: some View {
		VStack {
			makeForm(label: "Username: ", placeHolder: "123456", text: $username)
			
			makeForm(label: "Password: ", placeHolder: "abcdefg", text: $password)
			
			makeForm(label: "Shop ID: ", placeHolder: "123456", text: $tenantID)
		}.padding(.horizontal, 40.0)
	}
	
	@ViewBuilder
	private func makeForm(label: String, placeHolder: String, text: Binding<String>) -> some View {
		HStack {
			let newText = Binding<String>(
				get: { text.wrappedValue },
				set: { filter(value: $0) }
			)
			Text(label)
			TextField(placeHolder, text: newText)
		}
	}
	
	func filter(value: String) -> String {
		let validCodes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
		let sets = CharacterSet(charactersIn: validCodes)
		return String(value.unicodeScalars.filter(sets.contains).map(Character.init))
	}
}

Well, I don't know how to use the Binding with get and set, which I believe is what I need. Yet, I get a warning at the following line.

set: { filter(value: $0) }

What I need to do is set the filtered value to each TextField. What am I doing wrong? Thanks.

Currently, what I type is not filtered.

Currently, the warning says "Expression of type 'String' is unused."

The warning will disappear with the following, but the text entry won't be filtered. set: { text.wrappedValue = filter(value: $0) }

In fact it does filter, but only when you type a new char after the unvalid one.

For instance, if I type in Username

  • A@

it shows

  • if I type now B, then @ disappears and I get AB

-If I typed A@&, all show

  • but as soon as I type a valid char, @& disappear.

I instrumented code to try understand:

            let newText = Binding<String>(
                get: {  print("get", text.wrappedValue) ; return filter(value: text.wrappedValue) },
                set: { text.wrappedValue = filter(value: $0) ; print("set", text.wrappedValue) }
            )

When you type invalid char, after a valid A, one get:

  • get A
  • get A
  • get A
  • get A
  • set A
  • set A

which shows invalid @ is not handled in newText.

I think I cannot use Binding. Instead, I think I need to use TextField with .onChange.

The following would work except that I'm required to use Binding.

struct ContentView: View {
	@State var username = ""
	@State var password = ""
	@State var tenantID = ""
	
	var body: some View {
		VStack {
			makeForm(label: "Username: ", placeHolder: "123456", text: $username)
			
			makeForm(label: "Password: ", placeHolder: "abcdefg", text: $password)
			
			//makeForm(label: "Shop ID: ", placeHolder: "Amazon River Branch", text: $tenantID)
			HStack {
				Text("Shop ID: ")
				TextField("Amazon River Branch", text: $tenantID)
					.onChange(of: tenantID) { newValue in
						tenantID = textFilter(value: newValue)
					}
			}
		}.padding(.horizontal, 40.0)
	}
	
	@ViewBuilder
	private func makeForm(label: String, placeHolder: String, text: Binding<String>) -> some View {
		HStack {
			let newText = Binding<String>(
				get: { text.wrappedValue },
				set: { text.wrappedValue = textFilter(value: $0) }
			)
			Text(label)
			TextField(placeHolder, text: newText)
		}
	}
	
	func textFilter(value: String) -> String {
		let pattern = "[^A-Za-z0-9]+"
		return value.replacingOccurrences(of: pattern, with: "", options: [.regularExpression])
	}
}
Binding&lt;String&gt;, set, get?
 
 
Q