skip to Main Content

I feel like I must be doing something stupid. All I’m trying to do if pull from UserDefaults to set a Picker value, then save to UserDefaults when the Picker changes. The issue I’m running into comes when I try and make the code work for iOS14 and 15.

struct SettingsView: View {

    @State var rangeSelection: Int = UserDefaults.standard.integer(forKey: "vocalRange")

    @State var rangeOptions = ["Soprano", "Mezzo-Soprano/Alto", "Extended Range"]
    
    
    var body: some View {

        VStack{
            Form {
                Section {
                    Picker(selection: $rangeSelection, label: Text("Range")) {
                        ForEach(0 ..< rangeOptions.count, id: .self) { range in
                            Text(rangeOptions[range])
                                    .tag(range)
                        }
                    }
                    .onChange(of: rangeSelection, perform: { value in
                        UserDefaults.standard.setValue(rangeSelection, forKey: "vocalRange")
                    })
                }
            }
        }
        .navigationTitle("Settings")
        .navigationBarTitleDisplayMode(.inline)
    }
}

This seems to work fine, and the ‘onChange’ fires great. Only weird thing is, if you leave the page and immediately go back in, the ‘rangeSelection’ variable resets to what it was originally. Once you restart the app however, the change is reflected correctly.


As an alternative, I tried the following

 @State var rangeSelection: Int = 0

...
    .onAppear(perform:{
        rangeSelection = UserDefaults.standard.integer(forKey: "vocalRange")
    })

This creates another fun issue. In iOS15 it works fine, but in iOS14, once you’ve changed the picker, the OnAppear fires before the OnChange, and reverts the Picker back to its previous value. This only happens because I’m using the picker in a Form/List, so it changes pages when you select the Picker.

I feel like there is an obvious solution I should be using.

2

Answers


  1. Chosen as BEST ANSWER

    Thanks to @loremipsum I used AppStorage. I declared the variable like this, and no 'onChange' was needed!

    @AppStorage("vocalRange") var rangeSelection: Int = UserDefaults.standard.integer(forKey: "vocalRange")
    

  2. You can set delay in .OnAppear method to work it

    .onAppear(perform:{
        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
         rangeSelection = UserDefaults.standard.integer(forKey: "vocalRange")
        }
    })
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search