Turns out this topic needs more than just one post. In part 1, I described how I was able to use AppStorage for an enum with associated values.
Here I will discuss a challenge I encountered when attempting to build the View to enable users to change their value for the languageChoice enum.
enum LanguageChoice: Equatable, Codable {
case all
case oneToOne(Language, Language)
case oneToAll(Language)
case allToOne(Language)
case symmetricSubset(Set<Language>)
Step 1, create a version of LanguageChoice with no associated values:
enum SimpleLanguageChoice: String, CaseIterable {
case all
case oneToOne
case oneToAll
case allToOne
case symmetricSubset
}
Step 2, create a state variable using the new enum type and bind it to a picker:
@State var languageChoice: SimpleLanguageChoice = .all
var body: some View {
VStack {
Picker("Languages Choice", selection: $languageChoice) {
ForEach(SimpleLanguageChoice.allCases, id: \.self) {
Text($0.rawValue)
.tag($0)
}
}
}
}
Step 3, add UI below the picker to display the appropriate controls to go with the value of languageChoice.
@ViewBuilder var bottomStuff: some View {
switch languageChoice {
case .all:
EmptyView()
case .oneToAll:
Picker("Ask in", selection: $language) {
ForEach(Language.allCases, id: \.self) {
Text($0.rawValue)
.tag($0)
}
}
// et cetera
Step 4, add an AppStorage var to get the persisted LanguageChoice value and use it to configure the Picker UI.
@AppStorage("languageChoice") var persistedLanguageChoice: LanguageChoice = .all
@State var languageChoice: SimpleLanguageChoice
init() {
languageChoice = SimpleLanguageChoice.simpleChoice(for: persistedLanguageChoice)
}
Insert record scratch sound here! This code created the following compiler error:'self' used before all stored properties are initialized
I wanted to use the persisted languageChoice to set up the UI to allow users to choose a new languageChoice. But doing this required reading persisted languageChoice, which was not allowed. Alas. So to get things to work I needed to set a default value for languageChoice in the declaration and then update the value in init. Just do this, right?
@State var languageChoice: SimpleLanguageChoice = .all
init() {
languageChoice = SimpleLanguageChoice.simpleChoice(for: persistedLanguageChoice)
Nope. If you set an initial value for a State variable in the declaration, updating it in init gets more complicated. You need to do this:
init() {
_languageChoice = State(initialValue: SimpleLanguageChoice.simpleChoice(for: persistedLanguageChoice)