I am trying to save a small amount of data with picker using AppStorage across multiple views. However the issue I'm running into is that when I select one value and link to AppStorage it changes the value for all the others. What I want is to save the value for each selection over multiple views. If I use @State variable the selections work fine, but the values don't get saved when I close and reopen the app. I'm pretty sure I need to send each selection to it's own @AppStorage variable, but I'm having a hard time figuring out how to do that.
struct Animals: Identifiable {
var id = UUID().uuidString
var name: String
var animalTypes: [AnimalTypes]
}
var allAnimals = [
Animals(name: "fred", animalTypes: [.shiba, .lab]),
Animals(name: "barney", animalTypes: [.shiba, .dobberman, .lab]),
Animals(name: "molly", animalTypes: [.snowshoe, .burmese, .bengal]),
Animals(name: "bob", animalTypes: [.burmese]),
Animals(name: "wilma", animalTypes: [.snowshoe, .bengal]),
]
enum AnimalTypes: String, CaseIterable, Codable {
// Dog Breeds
case shiba, lab, dobberman
// Cat Breeds
case snowshoe, burmese, bengal
}
struct AnimalsView: View {
@State var animals: Animals!
var body: some View {
TabView {
ForEach(allAnimals) { animal in
AnimalSelectionView(animals: animal)
.tag(animal.name)
}
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
}
}
struct AnimalSelectionView: View {
@AppStorage("selection") var animalSelection: Int = 0 // Saves the same value across all pickers (how to save individual values?)
// @State private var animalSelection: Int = 0 // Sets value for each picker in each tabview, but doesn't save
@State var animals: Animals!
var body: some View {
VStack {
if animals.animalTypes.count <= 1 {
Text("\(animals.animalTypes[0].rawValue)")
} else {
Text("\(animals.animalTypes[animalSelection].rawValue)")
}
if animals.animalTypes.count > 1 {
Picker(selection: $animalSelection, label: Text("Select Animal Type")) {
ForEach(0 ..< animals.animalTypes.count, id:\.self) { item in
Text("\(item + 1)")
.font(.caption)
}
}
.pickerStyle(SegmentedPickerStyle())
.frame(width: 100, height: 17)
}
}
}
}
I see that you have decided to create a property on your
Animals
class calledid
. Happily, this is the perfect thing to use to save uniqueUserDefaults
values for eachAnimals
object.You can do something like this:
Keep in mind that you won't be able to retrieve the same value if you instantiate two different objects that have the same arguments passed in the init.
Example:
Edit for clarity:
Here,
fred
andfred2
are mostly the same, but theid
property for each of these instances ofAnimals
will be different. Therefore, if you try to access the selection value of one using the other'sid
, you will not receive the correct value. You must hold on to the exactAnimals
instance to access the value that you stored inUserDefaults
, or at least hold on to itsid
as needed. This rule holds true across app sessions. If you instantiate anAnimals
object and then quit and reboot the app, when that sameAnimals
object is re-instantiated, it will be assigned a differentid
value as per your definitionAnimals.id = UUID().uuidString
.In order to persist their
id
s across app sessions, you can either store the animals inCoreData
,UserDefaults
, grant them a staticid
, or generate anid
based on theAnimals
properties.Creating a static
id
would look like this:Generating an
id
based on their properties might look like:The method of generating
id
s based on theAnimals
properties works well, although if you create twoAnimals
with the exact same properties, your logic won't be able to tell them apart as they will generate the sameid
. They will then fetch and set the same value inUserDefaults
because they would share the same key.If you intend for these animals to be created dynamically by the users of your app, you will need to store them either in
CoreData
orUserDefaults
. If, however, your animals will all be created by you, you can either statically define the ID or generate them based on theAnimals
properties.