How to use different content in same timeline?

I am creating a widget for my application.

For the large size of the widget, I want to show 4 cells with different content that I fetch from an API call. But I couldn't manage to do it. This is how my widget looks like currently:

Should I create a different entry struct for every cell? Or is there a way to put array/list in the entry struct and append 4 data to that entry from I got from the request? Does TimelineEntry have subscripts I can use? Like, entry[0].title

Any suggestions about how can I achieve this?

My entry struct:

struct NewsEntry: TimelineEntry {
    var date: Date
    let configuration: ConfigurationIntent
    let header: String
    let imageUrl: String
}

My getTimeline function:

func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
    var entries: [NewsEntry] = []
    let currentDate = Date()
    let later = Calendar.current.date(byAdding: .minute, value: 5, to: Date())!
    
    NewsProvider.request() { (data, error) in
        guard let data = data else {
            if let error = error {
                print(error)
            }
            return
        }
        
        let entry = NewsEntry(date: currentDate, configuration: configuration, header: data[0].title!, imageUrl: data[0].imageUrlDetail!)
        entries.append(entry)
        
        let timeline = Timeline(entries: entries, policy: .after(later))
        completion(timeline)
    }
}

My EntryView:

struct WidgetEntryView : View {
    var entry: Provider.Entry
    
    @Environment(\.widgetFamily)
    var widgetFamily

    var body: some View {
        VStack(alignment: .trailing, spacing: 6) {
            Image("logo")
                .frame(maxWidth: .infinity, alignment: .leading)

            if widgetFamily == .systemLarge {
                var columns: [GridItem] =
                         Array(repeating: .init(), count: 2)

                LazyVGrid(columns: columns) {
                    ForEach((0..<4)) { index in
                        ZStack (alignment: .bottomLeading) {
                            if let url = URL(string: entry.imageUrl), let imageData = try? Data(contentsOf: url),
                            let uiImage = UIImage(data: imageData) {
                            Image(uiImage: uiImage)
                                .centerCropped()
                                .frame(maxHeight: 150, alignment: .center)
                                .cornerRadius(10)
                                        .overlay(RoundedRectangle(cornerRadius: 10)
                                        .stroke(Color.gray, lineWidth: 1))
                                        .shadow(radius: 10)
                            } else {
                                Image("ph_background")
                                    .centerCropped()
                                    .frame(maxHeight: 150, alignment: .center)
                                    .cornerRadius(10)
                                            .overlay(RoundedRectangle(cornerRadius: 10)
                                            .stroke(Color.gray, lineWidth: 1))
                                            .shadow(radius: 10)
                            }
                                
                            Text(entry.header)
                                    .font(.system(size: 12))
                                    .foregroundColor(.white)
                                    .fontWeight(.light)
                                    // .frame(maxHeight: 50)
                                    .background(Rectangle().fill(Color.black).blur(radius: 20))
                                    .padding(.bottom, 5)
                                    .padding(.leading, 5)
                                    .padding(.trailing, 5)
                                    .padding(.top, 5)
                        }
                    }
                    .frame(height: 160)
                }
            }
        }
        .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
        .padding()
        .background(LinearGradient(gradient: Gradient(colors: [Color(red:0.2, green:0.2, blue:0.2), .black]), startPoint: .top, endPoint: .bottom))
    }
}
Answered by oguzhanvarsak in 688910022

Changed the variables to [String] in the TimelineEntry and I achieved what I want.

I updated my struct and entry line like this:

var newsList: [String] = [] // First, appending the fetched data here
var imageList: [String] = []

struct NewsEntry: TimelineEntry {
    var date: Date
    let configuration: ConfigurationIntent
    let header: [String]
    let imageUrl: [String]
}
for index in 0 ..< 5 {
    newsList.append(data[index].title!)
    imageList.append(data[index].imageUrlDetail!)
}

// Then using passing the arrays to TimelineEntry as [String]
for hourOffset in 0 ..< 5 {
    let entryDate = Calendar.current.date(byAdding: .minute, value: hourOffset, to: currentDate)!
    let entry = NewsEntry(date: entryDate, configuration: configuration, header: newsList, imageUrl: imageList)
    entries.append(entry)
}

The only problem right now is I don't know if I'm using it correctly because I get crash with "Index out of range" on entry.imageUrl[index]. This is the LazyVGrid my updated View function:

LazyVGrid(columns: columns) {
    ForEach((0..<4)) { index in
        ZStack (alignment: .bottomLeading) {
            if let url = URL(string: entry.imageUrl[index]), let imageData = try? Data(contentsOf: url),
            let uiImage = UIImage(data: imageData) { // Thread 1: Fatal error: Index out of range
            Image(uiImage: uiImage)
                .centerCropped()
                .frame(maxHeight: 150, alignment: .center)
                .cornerRadius(10)
                        .overlay(RoundedRectangle(cornerRadius: 10)
                        .stroke(Color.gray, lineWidth: 1))
                        .shadow(radius: 10)
            } else {
                Image("ph_background")
                    .centerCropped()
                    .frame(maxHeight: 150, alignment: .center)
                    .cornerRadius(10)
                            .overlay(RoundedRectangle(cornerRadius: 10)
                            .stroke(Color.gray, lineWidth: 1))
                            .shadow(radius: 10)
            }
                
            Text(entry.header[index])
                    .font(.system(size: 12))
                    .foregroundColor(.white)
                    .fontWeight(.light)
                    // .frame(maxHeight: 50)
                    .background(Rectangle().fill(Color.black).blur(radius: 20))
                    .padding(.bottom, 5)
                    .padding(.leading, 5)
                    .padding(.trailing, 5)
                    .padding(.top, 5)
        }
    }
    .frame(height: 160)
}
Accepted Answer

Changed the variables to [String] in the TimelineEntry and I achieved what I want.

I updated my struct and entry line like this:

var newsList: [String] = [] // First, appending the fetched data here
var imageList: [String] = []

struct NewsEntry: TimelineEntry {
    var date: Date
    let configuration: ConfigurationIntent
    let header: [String]
    let imageUrl: [String]
}
for index in 0 ..< 5 {
    newsList.append(data[index].title!)
    imageList.append(data[index].imageUrlDetail!)
}

// Then using passing the arrays to TimelineEntry as [String]
for hourOffset in 0 ..< 5 {
    let entryDate = Calendar.current.date(byAdding: .minute, value: hourOffset, to: currentDate)!
    let entry = NewsEntry(date: entryDate, configuration: configuration, header: newsList, imageUrl: imageList)
    entries.append(entry)
}

The only problem right now is I don't know if I'm using it correctly because I get crash with "Index out of range" on entry.imageUrl[index]. This is the LazyVGrid my updated View function:

LazyVGrid(columns: columns) {
    ForEach((0..<4)) { index in
        ZStack (alignment: .bottomLeading) {
            if let url = URL(string: entry.imageUrl[index]), let imageData = try? Data(contentsOf: url),
            let uiImage = UIImage(data: imageData) { // Thread 1: Fatal error: Index out of range
            Image(uiImage: uiImage)
                .centerCropped()
                .frame(maxHeight: 150, alignment: .center)
                .cornerRadius(10)
                        .overlay(RoundedRectangle(cornerRadius: 10)
                        .stroke(Color.gray, lineWidth: 1))
                        .shadow(radius: 10)
            } else {
                Image("ph_background")
                    .centerCropped()
                    .frame(maxHeight: 150, alignment: .center)
                    .cornerRadius(10)
                            .overlay(RoundedRectangle(cornerRadius: 10)
                            .stroke(Color.gray, lineWidth: 1))
                            .shadow(radius: 10)
            }
                
            Text(entry.header[index])
                    .font(.system(size: 12))
                    .foregroundColor(.white)
                    .fontWeight(.light)
                    // .frame(maxHeight: 50)
                    .background(Rectangle().fill(Color.black).blur(radius: 20))
                    .padding(.bottom, 5)
                    .padding(.leading, 5)
                    .padding(.trailing, 5)
                    .padding(.top, 5)
        }
    }
    .frame(height: 160)
}
How to use different content in same timeline?
 
 
Q