1

In a meal planning app a user selects items they need for each day of the week which creates an array of items for each day. The arrays are then inserted into a shopping list array creating an Array of Arrays with all the items.

I'm stuck trying to show the combined list. In my code I am returning the count of arrays correctly, however when I try to display the items in each array I'm failing. Any help would be appreciated!

Here is a simplified example of what I'm trying to do:

import SwiftUI

struct ShoppingListView: View {
    
    var shoppingList: Array<Array<String>>

    
    var body: some View {
        VStack(alignment: .leading) {
            ForEach(0..<shoppingList.count, id: \.self) {list in
                Text("number of arrays")
                VStack {
                    List(0..<list) { item in
                        Text("item = \(item)")
                    }
                }
            }
        }
    }
}

struct ShoppingListView_Previews: PreviewProvider {
    static var previews: some View {
        let list = [["eggs", "bread", "milk", "cheese" ],["steak", "potatoes", "salad kit"]]
        ShoppingListView(shoppingList: list)
    }
}

2 Answers 2

6

First of all, newer use 0..<shoppingList.count inside ForEach or List, because the day you'll change items count you'll face an issue with your list not updating, like in this this case. Use shoppingList.indices instead.

You're enumerating indexes, but looks like you're expecting to get an item in the block. It's not gonna happen.

Perfectly create a struct for your items and conform it to Identifiable, then you'll be able to call ForEach(shoppingList) { and get your item in the block.

Until then you can use enumerating indices without problems, just get your item by index from the list:

var shoppingList: Array<Array<String>> = [["eggs", "bread", "milk", "cheese" ],["steak", "potatoes", "salad kit"]]

var body: some View {
    VStack(alignment: .leading) {
        ForEach(shoppingList.indices, id: \.self) { i in
            Text("number of arrays")
            let sublist = shoppingList[i]
            VStack {
                List(sublist.indices, id: \.self) { j in
                    Text("item = \(sublist[j])")
                }
            }
        }
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

@TJMac: The right way of defining the thing you want is Array<Array<String>>
Basing things off indices isn't great when the data changes, because something that used to be bread for example is now in the row where eggs is. Can cause those unstable identifier warnings and cause strange animations. I think doing the ID from the name like @workingdog is a more reliable method. But yeah, indices is better because things like ArraySlice don't always start at index 0. Ideally, the items in this shopping list would be their own struct that, for as long as they live, each item has their own unique and constant ID.
2

you could just do this:

struct ShoppingListView: View {

var shoppingList: [[String]] = [["1","2","3"], ["4","5","6"], ["7","8"]]

var body: some View {
    VStack(alignment: .leading) {
        Text("number of arrays is \(shoppingList.count)")
        ForEach(shoppingList, id: \.self) { list in
            VStack {
                List(list, id: \.self) { item in
                    Text("item = \(item)")
                }
            }
        }
    }
}
}

2 Comments

I'm not sure where I went wrong in my prior attempts, but I thought I had attempted this before without success. With your response I went back and now it's working fine. Thank you for the help, this is the solution I ended up going with.
Thanks. For me it was the inclusion of the id parameter on the ForEach that I'd missed.

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.