1

There are several useful questions on this topic, but all I've found either use deprecated syntax from earlier betas (e.g., BindableObject) or do not require a binding be passed (e.g., to a Toggle).

I want to create a list of Toggles that are bound to elements in an array. I've tried many methods, but the syntax is never corrrect. The version below most closely matches the answers to an existing question.

struct Item: Identifiable {
    var id: String { self.name }
    var name: String
    var enabled: Bool
}

final class ItemSet: ObservableObject {
    @Published var items: [Item]

    init() {
        items = [
            Item(name: "aaa", enabled: true),
            Item(name: "bbb", enabled: false),
            Item(name: "ccc", enabled: true)
        ]
    }
}

var myItems = ItemSet()

struct ContentView: View {
    @ObservedObject var items: ItemSet

    var body: some View {
        List {
            ForEach(items.items) { item in
                Toggle(item.name, isOn: $item.enabled)
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView(items: myItems)
    }
}

The compiler error I get in Xcode 11.1 is:

Use of unresolved identifier '$item'

on the line where Toggle is defined.

I was under the impression that each Item would itself need to be an ObservableObject with a @Published var enabled: Bool parameter, but I have not been able to get that working either, and all stackoverflow answers seems to say avoid making Item itself ObservableObject.

Any help would be much appreciated.

1
  • I believe the issue here is using ForEach. Because item is a struct, it's a copy of that struct? The Landmarks example from Apple uses an index directly into the items.items array... likely for this reason. Commented Oct 23, 2019 at 21:35

1 Answer 1

5

You are confusing the property wrapper for items (which is a current value subject) with the binding parameter that a Toggle expects. See corrected implementation with binding below:

import SwiftUI
import Combine

struct Item: Identifiable {
  var isEnabled: Binding<Bool>
  var id: String { self.name }
  var name: String
  init(name: String, enabled enabledValue: Bool) {
    self.name = name
    let enabled = CurrentValueSubject<Bool, Never>(enabledValue)
    isEnabled = Binding<Bool>(
      get: { enabled.value },
      set: { enabled.value = $0}
    )
  }
}

final class ItemSet: ObservableObject {
  @Published var items: [Item]

  init() {
    items = [
      Item(name: "aaa", enabled: true),
      Item(name: "bbb", enabled: false),
      Item(name: "ccc", enabled: true)
    ]
  }
}

var myItems = ItemSet()

struct ContentView: View {
  @ObservedObject var items: ItemSet

  var body: some View {
    List {
      ForEach(items.items) { item in
        Toggle(isOn: item.isEnabled, label: { Text (item.name) })
      }
    }
  }
}

struct ContentView_Previews: PreviewProvider {
  static var previews: some View {
    ContentView(items: myItems)
  }
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.