1 year ago
#338460
Kintsuba
Infinite loop onAppear in SwiftUI, but init() gives me another error
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")
}
}
}
}
swiftui
foreach
onappear
0 Answers
Your Answer