I have a SwiftData model called Diem. I want my App and my WidgetExtension to share DiemSharedView (already having both App and Widget as targets). However, DiemSharedView just doesn't show up on my Simulator's widget.
@Model final class Diem: Codable, Hashable, Identifiable {
@Attribute(.unique) var name: String
var date: Date
var detail: String?
var id: String { name }
func daysDiff() -> Int {
return Calendar.current.dateComponents([.day], from: Date(), to: date).day!
}
init(name: String, date: Date) {
self.name = name
self.date = date
}
// For Widget intent
init(entity: DiemEntity) {
self.name = entity.name
self.date = entity.date
self.detail = entity.detail
}
// Manually conform SwiftData model to Codable from GPT-4 (Bing Chat)
enum CodingKeys: CodingKey {
case name, date
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(name, forKey: .name)
try container.encode(date, forKey: .date)
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decode(String.self, forKey: .name)
date = try container.decode(Date.self, forKey: .date)
}
}
struct DiemSharedView: View {
var diem: Diem
var body: some View {
...
}
}
struct DiemWidget: Widget {
// the defaults
}
struct DiemWidgetEntry: TimelineEntry {
var date: Date
let diem: Diem?
}
struct DiemWidgetEntryView : View {
var entry: DiemWidgetEntry
var body: some View {
if let diem: Diem = entry.diem {
DiemSharedView(diem: diem) // ISSUE: blank on Simulator
} else {
DiemSharedView(diem: .placeholder) // works (displays when adding the widget on Simulator)
}
}
}
/*
Reference: https://developer.apple.com/documentation/swiftui/backyard-birds-sample
See the LICENSE.txt file for this sample’s licensing information.
*/
struct DiemWidgetIntent: WidgetConfigurationIntent {
static var title: LocalizedStringResource = "Select diem"
static var description = IntentDescription("Keeps track of a diem.")
@Parameter (title: "Diem")
var diem: DiemEntity?
init(diem: DiemEntity) {
self.diem = diem
}
init() {}
static var parameterSummary: some ParameterSummary {
Summary {
\.$diem
}
}
}
extension DiemWidgetIntent {
static var christmas: DiemWidgetIntent {
let intent = DiemWidgetIntent()
intent.diem = .init(from: .christmas)
return intent
}
static var independenceDay: DiemWidgetIntent {
let intent = DiemWidgetIntent()
intent.diem = .init(from: .independenceDay)
return intent
}
}
struct DiemEntity: AppEntity, Identifiable, Hashable {
var name: String
var date: Date
var detail: String?
var id: String {
name
}
func daysDiff() -> Int {
return Calendar.current.dateComponents([.day], from: Date(), to: date).day!
}
init(name: String, date: Date) {
self.name = name
self.date = date
}
init(from diem: Diem) {
name = diem.name
date = diem.date
}
var displayRepresentation: DisplayRepresentation {
DisplayRepresentation(title: "\(name)")
}
static var typeDisplayRepresentation = TypeDisplayRepresentation(name: "Diem")
static var defaultQuery = DiemEntityQuery()
}
struct DiemEntityQuery: EntityQuery, Sendable {
func entities(for identifiers: [DiemEntity.ID]) async throws -> [DiemEntity] {
let modelContext = ModelContext(try! ModelContainer(for: Schema([Diem.self])))
let diems = try modelContext.fetch(FetchDescriptor<Diem>(predicate: #Predicate { identifiers.contains($0.id) }))
return diems.map { DiemEntity(from: $0) }
}
func suggestedEntities() async throws -> [DiemEntity] {
let modelContext = ModelContext(try! ModelContainer(for: Schema([Diem.self])))
let diems = try modelContext.fetch(FetchDescriptor<Diem>())
return diems.map { DiemEntity(from: $0) }
}
}
/*
Reference: https://developer.apple.com/documentation/swiftui/backyard-birds-sample
See the LICENSE.txt file for this sample’s licensing information.
*/
struct DiemWidgetProvider: AppIntentTimelineProvider {
func placeholder(in context: Context) -> DiemWidgetEntry {
DiemWidgetEntry(date: Date(), configuration: DiemWidgetIntent())
}
func snapshot(for configuration: DiemWidgetIntent, in context: Context) async -> DiemWidgetEntry {
DiemWidgetEntry(date: Date(), configuration: configuration)
}
func timeline(for configuration: DiemWidgetIntent, in context: Context) async -> Timeline<DiemWidgetEntry> {
var entries: [DiemWidgetEntry] = []
// Generate a timeline consisting of five entries an hour apart, starting from the current date.
let currentDate = Date()
for hourOffset in 0 ..< 5 {
let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
let entry = DiemWidgetEntry(date: entryDate, configuration: configuration)
entries.append(entry)
}
return Timeline(entries: entries, policy: .atEnd)
}
}
All the previews work. DiemEntityQuery works.