Binding an Element of an Array of an Observableobject:'Subscript(_:)' Is Deprecated

Xcode 11 beta 5: 'subscript(_:)' is deprecated: See Release Notes for migration path.

Xcode 11, beta 6 UPDATE:

Good news! Just as I suspected, in beta 6, the Binding conformance to MutableCollection has been been replaced with something else. Instead of conforming to MutableCollection, it now let your access the elements via @dynamicMemberLookup. The result is you now can keep doing dataStore.bools[0] and no longer get a warning!

Xcode 11, beta 5 (old answer)

If you want to get rid of the deprecation, you can use the code below:

I used something similar to answer a slightly different question. In that other case, it was a Binding, not an ObservableObject (that is why I do not mark it as duplicate). However, the basics are the same: https://stackoverflow.com/a/57333200/7786555

I have the feeling that in the next beta, something will change again, as there are some contradictions in the release notes.

final class DataStore: ObservableObject {
@Published var bools: [Bool] = [true, false]

func element(idx: Int) -> Binding<Bool> {
return Binding<Bool>(get: { () -> Bool in
return self.bools[idx]
}) {
self.bools[idx] = $0
}
}
}

struct ContentView: View {
@EnvironmentObject var dataStore: DataStore
var body: some View {
HStack {
Spacer()
Toggle(isOn: dataStore.element(idx: 0)) {
Text(dataStore.bools[0] ? "On" : "Off")
}
Spacer()
Toggle(isOn: dataStore.element(idx: 1)) {
Text(dataStore.bools[1] ? "On" : "Off")
}
Spacer()
}
}
}

Alternatively, you can use the solution for the other question, which involves extending Binding:

final class DataStore: ObservableObject {
@Published var bools: [Bool] = [true, false]
}

extension Binding where Value: MutableCollection, Value.Index == Int {
func element(_ idx: Int) -> Binding<Value.Element> {
return Binding<Value.Element>(
get: {
return self.wrappedValue[idx]
}, set: { (value: Value.Element) -> () in
self.wrappedValue[idx] = value
})
}
}

struct ContentView: View {
@EnvironmentObject var dataStore: DataStore
var body: some View {
HStack {
Spacer()
Toggle(isOn: $dataStore.bools.element(0)) {
Text(dataStore.bools[0] ? "On" : "Off")
}
Spacer()
Toggle(isOn: $dataStore.bools.element(1)) {
Text(dataStore.bools[1] ? "On" : "Off")
}
Spacer()
}
}
}

Binding to subscript doesn't update TextField (macOS)

try this:

TextField("Placeholder", text: $store[selection].name)
.id(selection) // <-- here

.identified(by:) Deprecated?

.identified(by:) is deprecated. As you correctly stated, this is not noted in the release notes for Xcode beta, but in the release notes for iOS beta, which is why you couldn't find it. It's a little confusing because the changes relating to SwiftUI are scattered across the release notes for iOS 13 beta, Xcode 11 beta, and macOS Catalina beta.

https://developer.apple.com/documentation/ios_ipados_release_notes/ios_ipados_13_beta_5_release_notes

The identified(by:) method on the Collection protocol is deprecated in
favor of dedicated init(:id:selection:rowContent:) and
init(
:id:content:) initializers. (52976883, 52029393)

But the identified(by:) deprecation happened in beta 4, so the following also applies:

SwiftUI APIs deprecated in previous betas are now removed. (52587863)

This question is sort of a duplicate of SwiftUI ForEach 'identified(by:)' is deprecated. Use ForEach(_:id:) or List(_:id:), but the confusion around where the deprecation is mentioned in the release notes merits keeping it as a separate question.

How do I bind a SwiftUI element to a value in a Dictionary?

I managed to make is work by using a custom binding for each filter.

final class ExternalData: BindableObject {
let didChange = PassthroughSubject<Void, Never>()

var filters: Dictionary<String, Bool> = [:] {
didSet {
didChange.send(())
}
}

init() {
filters["Juniper"] = true
filters["Beans"] = false
}

var keys: [String] {
return Array(filters.keys)
}

func binding(for key: String) -> Binding<Bool> {
return Binding(getValue: {
return self.filters[key] ?? false
}, setValue: {
self.filters[key] = $0
})
}
}

The keys property list the filters keys as String so that it can be displayed (using ForEach(externalData.keys))

The binding(for:) method, create a custom Binding for the given key. This binding is given to the Toggle to read/write the current value in the wrapped dictionary.

The view code:

struct ExampleView : View {

@EnvironmentObject var externalData : ExternalData

var body: some View {
VStack {
ForEach(externalData.keys) { key in
Toggle(isOn: self.externalData.binding(for: key)) {
Text(key)
}
}
}
}
}

@Binding and ForEach in SwiftUI

You can use something like the code below. Note that you will get a deprecated warning, but to address that, check this other answer: https://stackoverflow.com/a/57333200/7786555

import SwiftUI

struct ContentView: View {
@State private var boolArr = [false, false, true, true, false]

var body: some View {
List {
ForEach(boolArr.indices) { idx in
Toggle(isOn: self.$boolArr[idx]) {
Text("boolVar = \(self.boolArr[idx] ? "ON":"OFF")")
}
}
}
}
}

Not able to use the Toggle Switch with if else conditions in swift ui

Right now, you're using TextField outside of the view hierarchy, and just inside the onChange. In fact, as you mentioned, Xcode is giving you a warning about the fact that it is unused.

To solve this, you can use an if clause inside the hierarchy itself:

struct ContentView : View {
var body: some View {
TwoView().environmentObject(UserSettings())
}
}

struct TwoView: View {
@EnvironmentObject var userSettings: UserSettings
@State var load: Bool = false

var body: some View {
NavigationView {
VStack {
Form {
Toggle("Casual loading", isOn: $load)
.onChange(of: load) { value in
//only imperative, non-View hierarchy code should go in this block
if load == false
{
userSettings.loadrate = 0
}
}
if load { //<-- Here
TextField("Casual Loading", value: $userSettings.loadrate, format: .number)
}
}
}
}
}
}

class UserSettings: ObservableObject
{
@Published var loadrate = Float()
}


Related Topics



Leave a reply



Submit