Skip to content

Instantly share code, notes, and snippets.

@SeanMcTex
Created May 8, 2020 20:05
Show Gist options
  • Save SeanMcTex/8040605f9fa3c1fc2a55b421221d6933 to your computer and use it in GitHub Desktop.
Save SeanMcTex/8040605f9fa3c1fc2a55b421221d6933 to your computer and use it in GitHub Desktop.
A pitfall to avoid when filtering SwiftUI TextFields

Today’s very hard-won lesson: be careful how you do TextField content filtering! We were filtering input to a text field (making sure the field only accepted numbers, etc.) like so:

@Binding var value: String
TextField( placeholder, text: $value )
    .onReceive( Just( value ), perform: { 
		self.value = self.contentType.filterCharacters( oldValue: $0 ) 
	})

This worked great when we were binding a @State variable to value. However, as soon as we tried to convert the object that we were binding there to an @ObservableObject, the app started freezing up. There were lots of changes that had to be made simultaneously, so it took us a good while to run down the culprit, but basically we had created an update loop: the .onReceive handler triggered an update to the @ObservableObject, which triggered the .onReceive handler, which triggered…

While it’s a little wordy, adding a check that something had changed before setting the new value solved the problem:

TextField( placeholder, text: $value )
    .onReceive( Just( value ), perform: {
        let newValue = self.contentType.filterCharacters( oldValue: $0 )
        if newValue != self.value {
            self.value = newValue
        }
    })
@lauraesaian
Copy link

Better way for now to call onChange and no need of import Combine and Just:
TextField( placeholder, text: $value ) .onChange(of: value) { newValue self.value = self.contentType.filterCharacters( oldValue: newValue ) }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment