sindresorhus / Defaults

💾 Swifty and modern UserDefaults
https://swiftpackageindex.com/sindresorhus/Defaults/documentation/defaults
MIT License
1.97k stars 117 forks source link

Binding of array item #94

Closed Vincz closed 2 years ago

Vincz commented 2 years ago

Hi guys. I've been using this library to cleanup my codebase but I'm stuck with a little problem. Sorry if my question is stupid, I'm just starting with Swift and there is quite a lot to process.

My settings looks like this:

class Entry: Codable, Identifiable, Defaults.Serializable {
    var id: String
    var displayLogo: Bool = true
}
extension Defaults.Keys {
    static let entries = Key<[Entry]>("entries", default: [])
}

in a View, I'm looping through the entries like this and display a view for each entry.

struct EntryView: View {
      @State var entry: Entry

      var body: some View {
            Text("Inside entry \(entry.id)")
             Toggle("Display logo?", isOn: $entry.displayLogo) // <-- This 
       }
}

struct EntriesView: View {
    @Default(.entries) var entries : [Entry];

    var body: some View {
        HStack(spacing: 10) {
            ForEach(entries) { entry in
                 EntryView(entry: entry)
            }
        }
     }

The binding $entry.displayLogo doesn't work. I'd like to be able to update the configuration for a particular entry from inside the view (ie. Update the value of displayLogo). Any idea how I could achieve this behavior? I know I could pass an index through the sub views and use something like entries[entryIndex].displayLogo but it doesn't seem very clean. Thanks

sindresorhus commented 2 years ago

Your question is not related to Defaults, but I will answer it.

You need to pass the Binding to the EntryView. Notice the $ in ForEach. That new in Xcode 13.2 or something.

struct EntryView: View {
      @Binding var entry: Entry

      var body: some View {
            Text("Inside entry \(entry.id)")
             Toggle("Display logo?", isOn: $entry.displayLogo)
       }
}

struct EntriesView: View {
    @Default(.entries) var entries

    var body: some View {
        HStack(spacing: 10) {
            ForEach($entries) { entry in
                 EntryView(entry: entry)
            }
        }
     }
}
sindresorhus commented 2 years ago

Unrelated, but Entry should be a struct. And you don't need the type annotation on @Default.

And I would recommend using UUID for the id.

Vincz commented 2 years ago

Thank you for your help, you saved me a lot of time. Allow me to buy you a couple of beers.