1

I'm still trying to get my head around Swift/SwiftUI, so apologies for what's likely a really simple question. Consider this simple SwiftUI file:

import SwiftUI

struct sampleStruct {
    let entryID: Int // comes from the system
    let name: String
}


struct TestView: View {
    
    var listEntries = [sampleStruct(entryID: 301, name: "Pete"),
                       sampleStruct(entryID: 5003, name: "Taylor"),
                       sampleStruct(entryID: 13, name: "Suzie")]
    
    var body: some View {
        VStack{
            ForEach(listEntries, id: \.entryID) { idx in
                Text(idx.name)
            }
        }
    }
}

struct TestView_Previews: PreviewProvider {
    static var previews: some View {
        TestView()
    }
}

Here's my question: I'm looking for a simple way to add the position of the element in the array to the text field. I.e. instead of Pete, Taylor, Susie, I'd like to get:

1 Pete

2 Taylor

3 Suzie

I thought that this could do the trick:

...
VStack{
            ForEach(listEntries, id: \.entryID) { idx in
                Text( listEntries.firstIndex { $0.id == idx.entryID } )
                Text(idx.name)
            }
        }
...

But, I'm getting the error "No exact matches in call to initializer".

Any help would be appreciated,

Philipp

1 Answer 1

1

The simplest approach would be to use enumerated(), which returns a sequence of pairs (index and item):

struct TestView: View {
    
    var listEntries = [sampleStruct(entryID: 301, name: "Pete"),
                       sampleStruct(entryID: 5003, name: "Taylor"),
                       sampleStruct(entryID: 13, name: "Suzie")]
    
    var body: some View {
        VStack{
            ForEach(Array(listEntries.enumerated()), id: \.1.entryID) { (index,item) in
                Text("\(index + 1) \(item.name)")
            }
        }
    }
}

Note that for extremely large collections you might want a different approach for performance reasons, but for anything reasonably sized, this will work well. A quick and easy upgrade to it would be to used indexed() from Swift Algorithms in place of .enumerated() (you could omit the Array(...) wrapper if you did this as well.


If for learning purposes you want to see what you'd have to do to adjust your original approach, you'd want to:

  1. Use .entryID instead of .id, which doesn't exist on your model
  2. Provide a default index in case firstIndex returns nil
  3. Interpolate the result into a String

One solution might look like this:

struct TestView: View {
    
    var listEntries = [sampleStruct(entryID: 301, name: "Pete"),
                       sampleStruct(entryID: 5003, name: "Taylor"),
                       sampleStruct(entryID: 13, name: "Suzie")]
    
    var body: some View {
        VStack{
            ForEach(listEntries, id: \.entryID) { item in
                let index = listEntries.firstIndex { $0.entryID == item.entryID } ?? -1
                Text("\(index + 1) \(item.name)")
            }
        }
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Awesome - many thanks!

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.