I've been trying to find a good way to connect a TextField with a viewmodel, so that the inputs to the TextField don't immediately update the view.
Here is a working example:
struct TextFieldSamples: View {
@StateObject var vm = TFViewModel()
var body: some View {
print(Self._printChanges())
return VStack {
TextField("placeholder",
text: Binding(
get: { vm.outputText },
set: { vm.subject.value = $0 }
))
Text(vm.outputText) // this will only update when the textfield holds >5 characters
}
}
}
class TFViewModel: ObservableObject {
@Published var outputText: String = ""
let subject = CurrentValueSubject<String, Never>("")
var subscriptions = Set<AnyCancellable>()
init() {
subject
.filter({ $0.count > 5 })
.sink { self.outputText = $0 }
.store(in: &subscriptions)
}
}
However, I came across the propertyWrapped CurrentValueSubject below on the swiftbysundell.com blog
@propertyWrapper
struct Input<Value> {
var wrappedValue: Value {
get { subject.value }
set { subject.send(newValue) }
}
var projectedValue: AnyPublisher<Value, Never> {
subject.eraseToAnyPublisher()
}
private let subject: CurrentValueSubject<Value, Never>
init(wrappedValue: Value) {
subject = CurrentValueSubject(wrappedValue)
}
}
Trying to use it, will result in the error Binding<String> action tried to update multiple times per frame.
(Not only that, the input behavior of the TextField gets messed up. This shows especially with Japanese input where the character transformations don't are not working most of the time.)
Here's a example showing this behavior:
struct TextFieldSamples: View {
@StateObject var vm = TFViewModel()
var body: some View {
print(Self._printChanges())
return VStack {
TextField("placeholder", text: $vm.inputText)
Text(vm.outputText)
}
}
}
class TFViewModel: ObservableObject {
@Input var inputText: String = ""
@Published var outputText: String = ""
var subscriptions = Set<AnyCancellable>()
init() {
$inputText
.filter({ $0.count > 5 })
.sink { self.outputText = $0 }
.store(in: &subscriptions)
}
}
I don't quite understand why the PropertyWrapper version is not working as expected.