0

I am trying to loop over @AppStorage variables and use them to create TextFields with predefined values. I've tried several things as adding and removing $ for variable1-5 and variable, but nothing seems to work.

To make it easier I reduced the amount of variables and removed all the unneeded code.

import SwiftUI

struct ContentView: View {
    static let store = UserDefaults(suiteName: "group.not-important.etc")
    
    @AppStorage("variable1", store: store) var variable1 = "a"
    @AppStorage("variable2", store: store) var variable2 = "b"
    @AppStorage("variable3", store: store) var variable3 = "c"
    @AppStorage("variable4", store: store) var variable4 = "d"
    @AppStorage("variable5", store: store) var variable5 = "e"
    
    var body: some View {
        NavigationStack {
            Form { 
                Section(header: Text("Variables")) {                    
                    HStack (alignment: .center) {
                        ForEach([
                            $variable1, // also tried variable1, etc...
                            $variable2,
                            $variable3,
                            $variable4,
                            $variable5
                        ], id: \.self) { variable in // also tried $variable
                            TextField(
                                "",
                                text: variable // also tried $variable, variable.wrappedName, variable.text, etc...
                            )
                        }
                    }
                }             
            }
        }
    }
}

Please help to understand what is the issue here and why I can not get it to work.

More info on errors here:

  1. Provided example throws an error for ForEach line: Generic struct 'ForEach' requires that 'Binding<String>' conform to 'Hashable'

  2. When trying $variable in instead of variable in I get: Inferred projection type 'String' is not a property wrapper

  3. TextField("", text: $variable) throws: Cannot convert value of type 'String' to expected argument type 'Binding<String>'

AleXiuS
  • 694
  • 7
  • 12

1 Answers1

1

I think the main issue with your attempts, is that you are trying to create an array of variables, but an array does not accept that, it accepts values, String, Int, custom types etc..., but not variables. This is why it does not "work".

Since you may have lots of variables that you want to process in a loop, I suggest you use an array instead. This makes looping very easy, as shown in this example code. You can still have other independent variables in your code.

Here is the example code, using the Array extension to easily store the array in AppStorage, from: SwiftUI: What is @AppStorage property wrapper

struct ContentView: View {
    static let store = UserDefaults(suiteName: "group.not-important.etc")
    
    @AppStorage("arr", store: store) var arr: [String] = ["a","b","c","d","e"]
    
    var body: some View {
        Form {
            Section(header: Text("Variables")) {
                HStack (alignment: .center) {
                    ForEach(arr.indices, id: \.self) { index in
                        TextField("", text: $arr[index])
                            .border(.red) // <-- for testing
                    }
                }
            }
        }
    }
}

extension Array: RawRepresentable where Element: Codable {
    public init?(rawValue: String) {
        guard let data = rawValue.data(using: .utf8),
              let result = try? JSONDecoder().decode([Element].self, from: data)
        else {
            return nil
        }
        self = result
    }

    public var rawValue: String {
        guard let data = try? JSONEncoder().encode(self),
              let result = String(data: data, encoding: .utf8)
        else {
            return "[]"
        }
        return result
    }
}