Xcode 11 beta 5: “ 'subscript(_:)' is depr

2019-08-24 06:05发布

问题:

I want to use code such as the following:

import Foundation
import Combine
import SwiftUI

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

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

(Actually this code is quite useless, but it's just about that I want to pass an element of an array as a binding to a subview.)

In Xcode beta 2 this worked, but since beta 5 I get the following warning on both the "Toggle" lines:

'subscript(_:)' is deprecated: See Release Notes for migration path. And the app crashes when I try to launch it.

Indeed, I have read the release notes, and I believe that the issue has something to do with that "the Binding structure’s conditional conformance to the Collection protocol is removed".
The problem is that I don't understand how to use the sample code that they give with the code that I want to use. Can someone help me with that?

回答1:

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()
        }
    }
}


回答2:

Based on kontiki's very helpful answer, I have made the following extension, so that you can use subscript just like before:

extension Binding where Value: MutableCollection, Value.Index == Int {
    public subscript(position: Value.Index) -> Binding<Value.Element> {
        Binding<Value.Element>(get: {
            self.wrappedValue[position]
        }, set: {
            self.wrappedValue[position] = $0
        })
    }
}

In theory, if needed, you could do the same for subscript(bounds:).



标签: swift swiftui