Yiming Designer
Yiming Designer

Reputation: 697

Is it possible to bind a dictionary item into TextField?

In SwiftUI I'd like to store some key-value pair data in a dictionary. Can I bind an item in the dictionary to TextField or elsewhere that requires Binding?

I want to do something like this:

@State var myDic: [String : String] = [
    "name": "Tim"
    "company": "Apple"
]

TextField("Name", text: $myDic["name"] ?? "")

where the behavior is:

Upvotes: 1

Views: 62

Answers (2)

Yiming Designer
Yiming Designer

Reputation: 697

After long-time trying, I have a solution for this problem. I think this can be separated into 2 requirements:

  1. Nullable setter for dictionary (because the getter of dictionary is nullable).
extension Dictionary {
    
    subscript(removeNil key: Key) -> Value? {
        get { self[key] }
        set {
            if let newValue = newValue {
                self[key] = newValue
            } else {
                self.removeValue(forKey: key)
            }
        }
    }

}
  1. Display nullable variable in TextField where requires non-nullable. As is required: 1) the default value is displayed, when it's nil; 2) it turns into nil when set to default value.
public extension Binding where Value: Equatable {
    static func ??(binding: Binding<Value?>, fallback: Value) -> Binding<Value> {
        Binding(
            get: { binding.wrappedValue ?? fallback },
            set: {
                if binding.wrappedValue == fallback {
                    binding.wrappedValue = nil
                } else {
                    binding.wrappedValue = $0
                }
            }
        )
    }
}

Then it can be used like this:

@State var myDic: [String: String] = [
    "a" : "a",
    "b" : "b",
]

TextField("foo", text: $myDic[removeNil: "a"] ?? "")
    .onChange(of: myDic) {
        print(myDic)
    }

Testing on macOS 14.4.1: When I type "a", and double "delete" to clear the TextField, the output is like shown below, exactly what is required.

enter image description here

Upvotes: 0

Sweeper
Sweeper

Reputation: 273540

I would create a custom Dictionary subscript.

extension Dictionary where Value == String {
    subscript(emptyStringAsNil key: Key) -> Value {
        get { self[key] ?? "" }
        set { self[key] = newValue.isEmpty ? nil : newValue }
    }
}

Usage:

TextField("Foo", text: $myDic[emptyStringAsNil: "name"])

Upvotes: 3

Related Questions