0

I'm having a difficult time trying to figure out how to ForEach loop some array's of array's of json data in SwiftUI. I used https://app.quicktype.io to get my data struct from this URL here.

I'm looking to get classes FeaturedHeaderView and FeaturedPackageView, which have title's of "Hot Right Now" and "What We're Using" which also contain the FeaturedPackageView data. My problem is I'm only looping through the first FeaturedHeaderView and FeaturedPackageView repeatedly, which I assumed there was two for each section. Is my data struct incorrect? I've never attempted complex json data yet, so I'm unsure how to handle it properly and if the ForEach loop is what I'm looking for. The end goal would be to have a List with `"Hot Right Now" and it's items and then then "What We're Using" and it's items.

I was able to get the FeaturedBannersView class just fine using two ForEach loops and thought it would be the same approach for the rest of the data?

Banner View

The banner view working

ScrollView(.horizontal) {
    HStack {
        ForEach(self.welcome.views,  id: \.viewClass) { views in
            ForEach(views.banners ?? [], id:\.url) { banner in
                ZStack (alignment: .bottomLeading) {
                    GeometryReader { geometry in
                        RequestImage(Url(banner.url), animation: nil)
                            .aspectRatio(contentMode: .fit)
                            .frame(width: geometry.size.width)
                            .clipped()
                            .cornerRadius(CGFloat(views.itemCornerRadius ?? 0))
                    }
                    HStack {
                        Text(banner.title ?? "")
                            .fontWeight(.bold)
                            .font(.title3)
                            .foregroundColor(Color.white)
                    }
                    .padding(.all, 15)
                }
                .frame(width: 263, height: 148)
            }
        }
    }
    .padding(.leading, 10)
    .padding(.trailing, 10)
}

The issue

Issue

My data struct

struct Welcome: Codable {
    let views: [WelcomeView]
}

struct WelcomeView: Codable {
    let viewClass: String?
    let banners: [Banner]?
    let views: [PurpleView]?

    enum CodingKeys: String, CodingKey {
        case viewClass = "class"
        case views
    }
}

struct Banner: Codable {
    let url: String?
    let title, package, repoName: String?
}

struct PurpleView: Codable {
    let viewClass: String?
    let views: [FluffyView]

    enum CodingKeys: String, CodingKey {
        case viewClass = "class"
        case views
    }
}

struct FluffyView: Codable {
    let viewClass: String?
    let title: String?
    let package, packageName, packageAuthor: String?
    let repoName: RepoName?
    let packageIcon: String?

    enum CodingKeys: String, CodingKey {
        case viewClass = "class"
        case title, package, packageName, packageAuthor, repoName, packageIcon
    }
}

enum RepoName: String, Codable {
    case chariz = "Chariz"
    case packix = "Packix"
}

List {
    ForEach(self.welcome.views, id: \.viewClass) { view in
        ForEach(view.views ?? [], id: \.viewClass) { purple in
            ForEach(purple.views, id: \.packageName) { fluffy in
                if fluffy.viewClass == "FeaturedHeaderView" {
                    Text(fluffy.title ?? "")
                        .font(.title3)
                        .fontWeight(.bold)
                } else {
                    HStack (spacing: 15){
                        RequestImage(Url(fluffy.packageIcon ?? ""), animation: nil)
                            .aspectRatio(contentMode: .fit)
                            .frame(width: 60, height: 60)
                            .clipped()
                            .cornerRadius(13.5)
                        VStack (alignment: .leading){
                            Text(fluffy.packageName ?? "")
                                .font(.body)
                            Text(fluffy.packageAuthor ?? "")
                                .foregroundColor(Color.secondary)
                                .font(.callout)
                            Text(fluffy.repoName?.rawValue ?? "")
                                .foregroundColor(Color.secondary)
                                .font(.subheadline)
                        }
                    }
                }
            }
        }
    }
}
.onAppear() {
    request()
}

1 Answer 1

1

The reason that you're getting the repeats is that you're using .viewClass and .packageName as your ids for the ForEach loops, but in the JSON, those values are not actually unique. FeaturedStackView, for example, gets repeated.

How to solve it:

Add IDs to your models and use them for the ForEach keys.


// MARK: - WelcomeView
struct WelcomeView: Codable {
    var id = UUID() //<-- HERE
    let viewClass: String
    let itemSize: String?
    let itemCornerRadius: Int?
    let banners: [Banner]?
    let horizontalSpacing: Int?
    let views: [PurpleView]?

    enum CodingKeys: String, CodingKey {
        case viewClass = "class"
        case itemSize, itemCornerRadius, banners, horizontalSpacing, views
    }
}

struct PurpleView: Codable {
    var id = UUID()
    let viewClass: String
    let preferredWidth: Int
    let views: [FluffyView]
    let xPadding: Int?

    enum CodingKeys: String, CodingKey {
        case viewClass = "class"
        case preferredWidth, views, xPadding
    }
}

struct FluffyView: Codable {
    var id = UUID()
    let viewClass: String
    let title: String?
    let useBoldText: Bool?
    let package, packageName, packageAuthor: String?
    let repoName: RepoName?
    let packageIcon: String?
    let orientation: String?
    let views: [TentacledView]?
    let text: String?
    let action: String?
    let yPadding: Int?

    enum CodingKeys: String, CodingKey {
        case viewClass = "class"
        case title, useBoldText, package, packageName, packageAuthor, repoName, packageIcon, orientation, views, text, action, yPadding
    }
}

Then, in each of your ForEach loops, use id: \.id

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

Comments

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.