7

I am passing data to a ForEach and had no problems until I used the .enumerated() method. I got the error that:

  1. Generic struct 'ForEach' requires that 'EnumeratedSequence<[String]>' conform to 'RandomAccessCollection'

  2. Type 'EnumeratedSequence<[String]>.Element' (aka '(offset: Int, element: String)') cannot conform to 'Hashable'; only struct/enum/class types can conform to protocols

As for the first error then you'll see in the code that I'm just passing in an array. So I don't understand this error.

As for the second error I'm not sure about this one.

The data being passed into the array is fed from UserDefaults as you'll see in the code below.

import SwiftUI

struct ContentView: View {
    
    @State private var name = ""
    @State private var myArray = [String]()
    @State private var isShowing = false
    @State private var actionSheet = false
    let defaults = UserDefaults.standard
   
    var body: some View {
        VStack {
            Spacer()
            TextField("Insert name", text: $name)
                .padding()
            ScrollView(.horizontal) {
                HStack {
                    ForEach(myArray.enumerated(), id: \.self) { index, name in
                        Circle()
                            .onTapGesture {
                                self.actionSheet.toggle()
                            }
                            .frame(width: 50, height: 50)
                            .actionSheet(isPresented: $actionSheet) {
                                ActionSheet(title: Text("Titles"), message: Text("This is the message"), buttons: [
                                                .default(Text("Delete item")){
                                                    var loaded =  defaults.stringArray(forKey: "myData")
                                                    
                                                }
                                ])
                            }
                    }
                }.padding()
                
            }
            Text(String(myArray.count))
                .font(.system(size: 30, weight: .black, design: .rounded))
                .foregroundColor(.green)
            VStack {
                Button(action: {self.isShowing.toggle() }){ Text("Show")}
                if isShowing {
                    ForEach(myArray, id: \.self) { name in
                    Text(name)
                    }
                }
            }.animation(.interactiveSpring())
            Spacer()
            
            HStack {
                Button(action: {
                   
                    
                }){
                    Text("Delete")
                        .fontWeight(.bold)
                        .padding()
                        .foregroundColor(.white)
                        .background(Color.red)
                        .cornerRadius(10)
                }
                Button(action: {
                    myArray.append(name)
                    defaults.set(myArray, forKey: "myData")
                    self.name = ""
                }){
                    Text("Save")
                        .fontWeight(.bold)
                        .padding()
                        .foregroundColor(.white)
                        .background(Color.blue)
                        .cornerRadius(10)
                }
               

            }
           

        }.onAppear {
            guard let loaded = defaults.object(forKey: "myData") as? [String] else {
                return
            }
            self.myArray = loaded
        }
    }
}

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

Text

0

2 Answers 2

22

ForEach expects a type conforming to RandomAccessCollection, but Array.enumerated() returns an EnumeratedSequence<[Element]>, which only conforms to Sequence.

You can wrap it in an Array to get an array, which conforms to RandomAccessCollection:

ForEach(Array(myArray.enumerated()), id: \.1) { (n, element) in
}

I personally like to use zip

ForEach(zip(myArray.indices, myArray), id: \.1) { (index, element) in
}

because it works with non-integer indices as well.

Sign up to request clarification or add additional context in comments.

2 Comments

That did the trick, I'm curious, what is the keypath "1", for the parameter id, referring to??
It's referring to the second element of the tuple, which is the element of the array. It should be Hashable to work
17

The enumerated() returns sequence of tuples, which you can convert to array, like

HStack {
    ForEach(Array(myArray.enumerated()), id: \.1) { index, name in
        Circle()

3 Comments

What does the \.1 do?
@adri567 it takes the name element of the tuple: \.0 would be the index, and \.1 is the name.
May have a problem if there are duplicates in the array.

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.