0

I want to call "getTestCounts" before displaying "Text(testCounts.counts[index].number)".

However, if I use onAppear, I end up with an infinite loop.

I think that since we are creating unique IDs with UUID and spinning them around in ForEach, we end up with an infinite loop.

Infinite loop when using onAppear in SwiftUI I tried to solve this problem using init() with reference to

"Cannot use instance member 'calendar' within property initializer; property initializers run before 'self' is available" The following error occurs.

How can I solve this problem? Thanks for suggestions.

Here is the UI I want to create I want to display the API response value under the date.

Here is the source code where the infinite loop occurs.

struct CalendarView: View {
    @Binding var year: String
    @Binding var month: String
    @Binding var day: String
    @EnvironmentObject var testCounts: TestCounts
    var body: some View {
        let calendar = Calendar(identifier: .gregorian)
        let selectedDate = calendar.date(from: DateComponents(year: Int(year), month: Int(month), day: Int(day)))
        let calendarDates = generateDates(selectedDate!)

        LazyVGrid(columns: Array(repeating: GridItem(.fixed(60.0), spacing: 0), count: 7), spacing: 0) {
            ForEach(calendarDates) { date in
                Button(action: {}, label: {
                    VStack(spacing: 0) {
                        if let date = date.date, let day = Calendar.current.day(for: date) {
                            Text("\(day)").fontWeight(.semibold).frame(width: 45, alignment: .leading).foregroundColor(Color("dayTextBrown"))
                            ForEach(0 ..< testCounts.counts.count, id: \.self) { index in
                                if testCounts.counts[index].date == DateTime.dateToStr(date) {
                                    Text(testCounts.counts[index].number)
                                    Text(testCounts.counts[index].hcount)
                                }
                            }
                        } else {
                            Text("").frame(width: 45, alignment: .leading)
                        }
                   }.frame(width: 60, height: 60).onAppear { 
                       getTestCounts(date.date ?? Date(), "all")
                   }
               }).background(.white).border(Color("drabBrown"), width: 1)
            }
        }
    }

    func getTestCounts(_ date: Date, _ timeType: String) {
        let since = Calendar.current.startOfMonth(for: date)
        let stringSince = DateTime.dateToStr(since!)

        let until = Calendar.current.endOfMonth(for: date)
        let stringUntil = DateTime.dateToStr(until!)

        TestCountsApi(LoginInformation.shared.token, shopId: LoginInformation.shared.defaultShopId, since: stringSince, until: stringUntil, timeType: timeType).request { json, error, result in
            switch result {
            case .success, .successWithMessage:
                TestCounts.shared.setTestCounts(json!)
            case .apiError:
                errorMessage = json!.message!
            case .communicationError:
                errorMessage = error!.localizedDescription
            case .otherError:
                errorMessage = "otherError"
            }
        }
    }
}


struct CalendarDates: Identifiable {
    var id = UUID()
    var date: Date?
}

func generateDates(_ date: Date) -> [CalendarDates] {
    var days = [CalendarDates]()

    let startOfMonth = Calendar.current.startOfMonth(for: date)
    let daysInMonth = Calendar.current.daysInMonth(for: date)
    guard let daysInMonth = daysInMonth, let startOfMonth = startOfMonth else {
        return []
    }

    for day in 0 ..< daysInMonth {
        days.append(CalendarDates(date: Calendar.current.date(byAdding: .day, value: day, to: startOfMonth)))
        }
    }

    guard let firstDay = days.first, let lastDay = days.last,
          let firstDate = firstDay.date, let lastDate = lastDay.date,
          let firstDateWeekday = Calendar.current.weekday(for: firstDate),
          let lastDateWeekday = Calendar.current.weekday(for: lastDate)
    else { return [] }

    let firstWeekEmptyDays = firstDateWeekday - 1
    let lastWeekEmptyDays = 7 - lastDateWeekday

    for _ in 0 ..< firstWeekEmptyDays {
        days.insert(CalendarDates(date: nil), at: 0)
    }

    for _ in 0 ..< lastWeekEmptyDays {
        days.append(CalendarDates(date: nil))
    }
    return days
}

class TestCounts: ObservableObject {
    struct TestCount {
        var date: String
        var number: Int
        var hcount: Int
    }

    static let shared = TestCounts()
    @Published var counts: [TestCount] = []

    func setTestCounts(_ json: TestCountsJson) {
        counts = []

        if let countsJsons = json.counts {
            for countJson in countsJsons {
                counts.append(TestCount(
                    date: countJson.date ?? "",
                    number: countJson.number ?? 0,
                    hcount: countJson.hcount ?? 0
                ))
            }
        }
    }
}

Here is the source code that tries to use init().

struct CalendarView: View {
    @Binding var year: String
    @Binding var month: String
    @Binding var day: String
    @EnvironmentObject var testCounts: TestCounts

    let calendar = Calendar(identifier: .gregorian)
    let selectedDate = calendar.date(from: DateComponents(year: Int(year), month: Int(month), day: Int(day)))
    let calendarDates = generateDates(selectedDate!)

    var body: some View {
        LazyVGrid(columns: Array(repeating: GridItem(.fixed(60.0), spacing: 0), count: 7), spacing: 0) {
            ForEach(calendarDates) { date in
                Button(action: {}, label: {
                    VStack(spacing: 0) {
                        if let date = date.date, let day = Calendar.current.day(for: date) {
                            Text("\(day)").fontWeight(.semibold).frame(width: 45, alignment: .leading).foregroundColor(Color("dayTextBrown"))
                            ForEach(0 ..< testCounts.counts.count, id: \.self) { index in
                                if testCounts.counts[index].date == DateTime.dateToStr(date) {
                                    Text(testCounts.counts[index].number)
                                    Text(testCounts.counts[index].hcount)
                                }
                            }
                        } else {
                            Text("").frame(width: 45, alignment: .leading)
                        }
                    }.frame(width: 60, height: 60)
                }).background(.white).border(Color("drabBrown"), width: 1)
            }
        }
    }
}

class TestViewModel {
    var date: Date
    var timeType: String
    var errorMessage = ""

    init (date: Date, timeType: String) {
        self.date = date
        self.timeType = timeType
       getTestCounts(date, timeType)
    }
    func getTestCounts(_ date: Date, _ timeType: String) {
        let since = Calendar.current.startOfMonth(for: date)
        let stringSince = DateTime.dateToStr(since!)

        let until = Calendar.current.endOfMonth(for: date)
        let stringUntil = DateTime.dateToStr(until!)

        TestCountsApi(LoginInformation.shared.token, shopId: LoginInformation.shared.defaultShopId, since: stringSince, until: stringUntil, timeType: timeType).request { json, error, result in
            switch result {
            case .success, .successWithMessage:
                print("success")
                TestCounts.shared.setTestCounts(json!)
            case .apiError:
                self.errorMessage = json!.message!
                print("errorMessage")
            case .communicationError:
                self.errorMessage = error!.localizedDescription
                print("errorMessage")
            case .otherError:
                self.errorMessage = "otherError"
                print("errorMessage")
            }
        }
    }
}


Kintsuba
  • 1
  • 1
  • What is exactly that you are trying to do? I mean UI wise. Could you upload a UI sketch that you want to create? – Tomas Jablonskis Mar 26 '22 at 10:18
  • @Tomas Jablonskis Thanks for your comment.I have added the image. The source code for the day of the week part has been omitted. – Kintsuba Mar 26 '22 at 10:48
  • Welcome to Stack Overflow! Please take the [tour](https://stackoverflow.com/tour) and see: [How do I ask a good question?](https://stackoverflow.com/help/how-to-ask) and [How to create a Minimal, Reproducible Example (MRE)](https://stackoverflow.com/help/minimal-reproducible-example). This really needs to be pared down to an MRE. There is a lot of code we don't need, but you also need to use some sort of simulated data so we can try to run this. You may answer your own questions by making the MRE as well. – Yrb Mar 26 '22 at 13:42

0 Answers0