I am unable to feed and read core data objects/properties into my Widget view. I am only able to feed and present it Doubles/Strings beforehand and it will work.
Prior to this, I have made sure all Core Data related files are shared between both the Widget Extension and the main app. The App Group has been set up and the data can be loaded (I am able to load all transactions in the Provider, calculate the total, and feed that double into the Widget view).
However, when it comes to actually feeding an array of transactions into the Widget View, the data cannot be loaded and all I see are empty rectangles. However, I'm pretty certain the data is accessible as the colours are accurate, and if I display the count of the array, it is accurate too. Below is my attached code.
struct RecentExpenditureWidget: Widget {
let kind: String = "ExpenditureWidget"
var body: some WidgetConfiguration {
IntentConfiguration(kind: kind, intent: RecentWidgetConfigurationIntent.self, provider: Provider()) { entry in
ExpenditureWidgetEntryView(entry: entry)
}
.configurationDisplayName("Recent Transactions")
.description("View your latest expenses.")
.supportedFamilies([.systemSmall])
}
}
struct Provider: IntentTimelineProvider {
typealias Intent = RecentWidgetConfigurationIntent
public typealias Entry = RecentWidgetEntry
func placeholder(in context: Context) -> RecentWidgetEntry {
RecentWidgetEntry(date: Date(), amount: loadAmount(type: .week), transactions: loadTransactions(type: .week, count: 3))
}
func getSnapshot(for configuration: RecentWidgetConfigurationIntent, in context: Context, completion: @escaping (RecentWidgetEntry) -> ()) {
let entry = RecentWidgetEntry(date: Date(), amount: loadAmount(type: configuration.duration), transactions: loadTransactions(type: configuration.duration, count: 3))
completion(entry)
}
func getTimeline(for configuration: RecentWidgetConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
let entry = RecentWidgetEntry(date: Date(), amount: loadAmount(type: configuration.duration), transactions: loadTransactions(type: configuration.duration, count: 3))
let timeline = Timeline(entries: [entry], policy: .atEnd)
completion(timeline)
}
func loadAmount(type: TimePeriod) -> Double {
let dataController = DataController()
let itemRequest = dataController.fetchRequestForRecentTransactions(type: type)
let transactions = dataController.results(for: itemRequest)
var holdingTotal = 0.0
transactions.forEach { transaction in
holdingTotal += transaction.wrappedAmount
}
return holdingTotal
}
func loadTransactions(type: TimePeriod, count: Int) -> [Transaction] {
let dataController = DataController()
let itemRequest = dataController.fetchRequestForRecentTransactionsWithCount(type: type, count: count)
return dataController.results(for: itemRequest)
}
}
struct RecentWidgetEntry: TimelineEntry {
let date: Date
let amount: Double
let transactions: [Transaction]
}
struct ExpenditureWidgetEntryView : View {
let entry: Provider.Entry
var body: some View {
VStack(spacing: 5) {
ForEach(entry.transactions) { transaction in
HStack(spacing: 2) {
Capsule()
.fill(Color(transaction.category?.wrappedColour ?? ""))
.frame(width: 4)
Text(transaction.wrappedNote)
.lineLimit(1)
.font(.system(size: 12, weight: .regular, design: .rounded))
.foregroundColor(Color.PrimaryText)
Text(transaction.amount, format: .currency(code: Locale.current.currencyCode ?? "USD"))
.font(.system(size: 12, weight: .regular, design: .rounded))
.foregroundColor(Color.SubtitleText)
}
.frame(height: 15)
}
}
.padding(15)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.PrimaryBackground)
}
}
This is the result I get on my actual devices:
The only (by extremely dumb) workaround I have found is to extract each property of a transaction in the loadTransaction()
function, and then feed it into the Widget.
func loadTransactions(type: TimePeriod, count: Int) -> String {
let dataController = DataController()
let itemRequest = dataController.fetchRequestForRecentTransactionsWithCount(type: type, count: count)
return dataController.results(for: itemRequest).first!.wrappedNote
}