0

I have a simple app in SwiftUI with a TextEditor and a button that brings up a SheetView with a toggle I named: disableAutoCorrection

When I toggle it, this should disable the auto corrector in my TextEditor, however, this only happens after fully closing and reopening the app.

Here's the code for the TextEditor:

TextEditor(text: $editorText)
                    .disableAutocorrection(userSettings.disableAutoCorrect ? true : false)
                    .padding()
                    .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0,
                           maxHeight: .infinity, alignment: .top)
                    .edgesIgnoringSafeArea(.bottom)

My Settings class:

class UserSettings: ObservableObject {
    
    @Published var disableAutoCorrect: Bool {
        didSet {
            UserDefaults.standard.set(disableAutoCorrect, forKey: "disableAutoCorrect")
        }
    }
    
    init() {
        self.disableAutoCorrect = UserDefaults.standard.object(forKey: "disableAutoCorrect") as? Bool ?? false
        
    }
    
}

In the TextEditor view I got a

 @ObservedObject var userSettings = UserSettings()

SheetView with the toggle:


struct SettingsView: View {
    
    @ObservedObject var userSettings = UserSettings()
    
    var body: some View {
        NavigationView {
            Form {
                Section(header: Text("Editor")) {
                    Toggle(isOn: $userSettings.disableAutoCorrect) {
                        Text("Disable AutoCorrection")
                    }
                }
            }
            .navigationTitle("Settings")
        }
    }
}

class disabledAutoCorrector: ObservableObject {
    @Published var disabledAuto: Bool = false
}

Thanks in advance!

LeonardoXUI
  • 411
  • 3
  • 10

2 Answers2

1

Try using @AppStorage for persistance of your settings. For one it is more the way of SwiftUI and for the other it updates its settings automatically everywhere.

This might be your view:

struct MyView: View {
    @AppStorage("disableAutoCorrect") var disableAutoCorrect: Bool = false
    
    var body: some View {
        // use the settings data
        Text("hello")
    }
}

And this would be your settings view:

struct SettingsView: View {
    @AppStorage("disableAutoCorrect") var disableAutoCorrect: Bool = false
    
    var body: some View {
        NavigationView {
            Form {
                Section(header: Text("Editor")) {
                    Toggle(isOn: $disableAutoCorrect) {
                        Text("Disable AutoCorrection")
                    }
                }
            }
            .navigationTitle("Settings")
        }
    }
}

This is how it works:

@AppStorage("nameItIsSavedByTheApp") var nameForThatView: Bool = false

Obviously Bool is the type of the settings and false is the default value if none is set yet (Make sure it is the same in all views for consistancy throughout all users, that it does not depend on which view they open first what default settings they will get).

Jelumar
  • 470
  • 1
  • 4
  • 14
  • Is it officially supported for two Views to stay in sync by using the same AppStorage key? I note that if I do this in the Simulator, it seems to work, but if I take a screenshot of MyView, change the value in SettingsView, then take another screenshot of MyView, I get the old value. Not sure if this is a bug in the Simulator, or if using two AppStorages with the same key isn't fully defined. – samkass Jul 02 '23 at 11:44
  • All it does, is to Link a userDefaults Entry with a variable you can use, display and change in a view. I am using it with one Settings view to change an entry and using this over AppStorage to alter several of my views depending on what the user has chosen. What I do is to copy the AppStorage line and add it to other Views I need it, so to be sure I have no typos in there. But it works. – Jelumar Jul 09 '23 at 10:28
  • Yes, it works for me... most of the time. But if some aspect of the View depends on the AppStorage var, a screenshot in the Simulator will pick up the old value if it changes elsewhere. I don't know if that's a bug in the Simulator, or if this is an officially supported way to synchronize two Views. That's why I asked about it being officially supported, not whether it works. – samkass Jul 09 '23 at 17:52
  • I would only use AppStorage to save Data that should be there again after closing the App (eg Settings or Configurations). If the goal is just to synchronize two views during Runtime I would either use a State or StateObject passed through as Binding or if those two views are not close to each other in the hierarchy use an EnvironmentObject as this can be defined in the App View and then used everywhere using its name similar to AppStorage. – Jelumar Jul 09 '23 at 19:42
0

Storing a user default doesn't automatically make it detectable elsewhere in your app, so your text editor may not have detected the change in settings from your settings view. You can get your object to listen for updates using a publisher – see this answer for an example. (Another answer to the same question talks about accessing your UserSettings as an @EnvironmentObject, which is a good shout as well.)

If you have to support SwiftUI apps in iOS13, you may have to use the above approach.

However, if iOS 14 is your baseline, you may find using an @AppStorage variable to be much easier, as it handles updates for you. See the Apple documentation and a Hacking with Swift article.

ScottM
  • 7,108
  • 1
  • 25
  • 42