Some of the users of my app reported that, the widgets cannot be loaded, event after restarts and re-installs. It seems that it is not rare, I have tens of reports on this (and probably much more who didn't report).
It seems the widget is blank even on the gallery screen while you are first adding it:
struct SingleWidgetProvider: AppIntentTimelineProvider {
@Environment(\.widgetFamily) var family
private let viewModel: WidgetViewModel = WidgetViewModel()
func placeholder(in context: Context) -> SingleEntry {
SingleEntry(date: Date(), habit: .singleSample)
}
func snapshot(for configuration: SingleWidgetConfigurationIntent, in context: Context) async -> SingleEntry {
guard let habit = Array(PersistenceManager.shared.retrieveHabitList().habits).first else {
return SingleEntry(date: Date(), habit: .singleSample)
}
let displayable = viewModel.displayableFromHabit(habit, separateComponents: true, secondOffset: 0)
return SingleEntry(date: Date(), habit: displayable)
}
func timeline(for configuration: SingleWidgetConfigurationIntent, in context: Context) async -> Timeline<SingleEntry> {
var entries: [SingleEntry] = []
guard let counter = configuration.currentCounter else {
return Timeline(entries: [SingleEntry(date: Date(), habit: .singleSample)], policy: .atEnd)
}
guard let habit = PersistenceManager.shared.retrieveHabit(habitKey: counter.id) else {
return Timeline(entries: [SingleEntry(date: Date(), habit: .singleSample)], policy: .atEnd)
}
let currentDate = Date()
for secondOffset in 0 ..< 100 {
let displayable = viewModel.displayableFromHabit(
habit,
separateComponents: true,
secondOffset: secondOffset,
symbolName: habit.habitSymbol?.name ?? "",
overrideColor: configuration.currentColor.color,
overrideButtonVisibility: configuration.currentButtonVisibility,
overrideDisplayOption: configuration.currentDisplayOption
)
let entryDate = Calendar.current.date(byAdding: .second,
value: secondOffset,
to: currentDate)!
let entry = SingleEntry(date: entryDate, habit: displayable)
entries.append(entry)
}
return Timeline(entries: entries, policy: .atEnd)
}
}
struct SingleEntry: TimelineEntry {
let date: Date
let habit: HabitDisplayable
}
static var singleSample: HabitDisplayable {
return HabitDisplayable(habitKey: nil,
title: LS("sampleWidgetHabitTitle"),
dates: [Date(timeIntervalSince1970: 1507158360)],
displayOption: .dayMonthYear,
secondOffset: 0,
separateComponents: true)
}
func displayableFromHabit(
_ habit: Habit,
separateComponents: Bool,
secondOffset: Int,
symbolName: String = "",
overrideColor: Color? = nil,
overrideButtonVisibility: WidgetButtonVisibility? = nil,
overrideDisplayOption: WidgetDisplayOption? = nil
) -> HabitDisplayable {
let latestDates: [HabitDate]
let displayOption: DisplayOption
if let overrideDisplayOption {
if overrideDisplayOption == .sameAsCounter {
displayOption = habit.settings?.toValue.displayOption ?? .dayMonthYear
} else {
displayOption = DisplayOption(rawValue: overrideDisplayOption.rawValue - 1) ?? .dayMonthYear
}
} else {
displayOption = habit.settings?.toValue.displayOption ?? .dayMonthYear
}
let displayMode = displayOption.mode
switch displayMode {
case .timePassed, .date:
latestDates = PersistenceManager.shared.latestDate(habit: habit).map { [$0] } ?? []
case .activity:
latestDates = PersistenceManager.shared.retrieveHabitDates(habit: habit, limitDate: displayOption.limitDate())
}
let displayButton: Bool
if let overrideButtonVisibility {
if overrideButtonVisibility == .sameAsCounter {
displayButton = habit.settings?.toValue.buttonType == .onRow
} else {
displayButton = overrideButtonVisibility == .show
}
} else {
displayButton = false
}
return HabitDisplayable(
habitKey: habit.habitKey,
title: habit.title,
dates: latestDates.map { $0.date },
displayOption: displayOption,
secondOffset: secondOffset,
separateComponents: separateComponents,
color: overrideColor ?? habit.habitColor?.color ?? .appPrimary,
symbolName: symbolName,
displayButton: displayButton
)
}
I provided a large portion of my code, let me know if you need more. The strange thing here is, even if the DB connection is broken somehow, it should have shown the default option (singleSample).
I am not able to reproduce/fix this for months now, so any help is very appreciated.