Для подобных вещей я предлагаю вам использовать .debounce
издателя.
import SwiftUI
import Combine
class TestViewModel : ObservableObject {
private static let userDefaultTextKey = "textKey"
@Published var text = UserDefaults.standard.string(forKey: TestViewModel.userDefaultTextKey) ?? ""
private var canc: AnyCancellable!
init() {
canc = $text.debounce(for: 0.2, scheduler: DispatchQueue.main).sink { newText in
UserDefaults.standard.set(newText, forKey: TestViewModel.userDefaultTextKey)
}
}
deinit {
canc.cancel()
}
}
struct ContentView: View {
@ObservedObject var viewModel = TestViewModel()
var body: some View {
TextField("Type something...", text: $viewModel.text)
}
}
Документация издателя .debounce
гласит:
Используйте этот оператор, если вы хотите дождаться паузы в доставке событий от вышестоящего издателя.Например, вызовите debounce на издателе из текстового поля, чтобы получать элементы только тогда, когда пользователь делает паузу или прекращает печатать.Когда они снова начинают печатать, отладка удерживает доставку события до следующей паузы.
Вы на самом деле не хотите сохранять текст в UserDefaults
каждый раз, когда пользовательчто-то печатает (т.е. для каждого персонажа, который он / она печатает).Вы просто хотите, чтобы текст был в конечном итоге сохранен в UserDefaults.Издатель debounce ожидает, пока пользователь прекратит печатать в соответствии с временем, установленным вами в init debounce (в примере выше 0,2 с), а затем перенаправит событие подписчику sink
, который фактически сохранит текст.Всегда используйте издателя debounce, когда у вас много событий (например, новый текст, набранный в текстовом поле), которые могут вызвать «тяжелые» операции (что бы вы ни думали: что-то, связанное с CoreData, сетевые вызовы и т. Д.).