Search code examples
stringtextfieldswiftuiuserdefaults

Save string from TextField in UserDefaults with SwiftUI


How can the string from a TextField be stored in UserDefaults using SwiftUI?

I have seen this tutorial on how to save the state of a toggle in UserDefaults and that looks promising, but I can't figure out how to use the same idea for storing the content from a TextField: https://www.youtube.com/watch?v=nV-OdfQhStM&list=PLerlU8xuZ7Fk9zRMuh7JCKy1eOtdxSBut&index=3&t=329s

I still want to be able to update the string by typing new text in the TextField, but unless I do that, I want the string to be maintained in the view. Both when changing page and completely exitting the app.


Solution

  • For things like these I suggest you use the .debounce publisher.

    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)
        }
    }
    

    The .debounce publisher documentation says:

    Use this operator when you want to wait for a pause in the delivery of events from the upstream publisher. For example, call debounce on the publisher from a text field to only receive elements when the user pauses or stops typing. When they start typing again, the debounce holds event delivery until the next pause.

    You don't really want to save the text in the UserDefaults each time the user types something (i.e. for every character he/she types). You just want the text to be ultimately saved to the UserDefaults. The debounce publisher waits for the user to stop typing according to the time you set in the debounce init (in the example above 0.2 secs) and then forwards the event to the sink subscriber that actually saves the text. Always use the debounce publisher when you have a lot of events (for example new text typed in a text field) that may trigger "heavy" operations (whatever you can think of: something related to CoreData, network calls and so forth).