I tried looking everywhere online and asked AI, but it seems none of the solutions resolved my issue. I am using CoreData or SwiftData with CloudKit to fetch data from the database. I created a simple app to practice communication between iOS/iPadOS and the Apple Watch app, along with a widget.
I was able to send data to my Apple Watch app without any problems, and everything runs smoothly. When I add, update, or remove people via the iOS/iPad app, my Watch app updates perfectly. However, there’s one issue with the widget: it won’t update unless I open the app on the Watch, at which point the widget finally updates.
Is there a way to trigger the widget’s timeline to update when there are any changes to SwiftData or CoreData? Also, updating the widget every 5 minutes is fine with me, but unfortunately, the widget doesn’t update at all every 5 minutes.
I’ve included the full widget code below—could you help identify any issues? Do you have any suggestions for making the timeline call if SwiftData has been updated? I tried using @Query, but I understand that @Query doesn’t work in widgets, if I’m correct.
import WidgetKit
import SwiftUI
import SwiftData
import CoreData
import CloudKit
import UserNotifications
@Model
final class Person {
var uid: String = UUID().uuidString
var name: String?
var birthday: Date?
var height: Double?
var weight: Int?
init(uid: String, name: String, birthday: Date, height: Double, weight: Int) {
self.uid = uid
self.name = name
self.birthday = birthday
self.height = height
self.weight = weight
}
}
func setupModelContainer() -> ModelContainer {
let config = ModelConfiguration()
#if DEBUG
autoreleasepool {
let descriptor = NSPersistentStoreDescription(url: config.url)
let options = NSPersistentCloudKitContainerOptions(containerIdentifier: "iCloud.com.myapp.Watch-Connection-Data")
descriptor.cloudKitContainerOptions = options
descriptor.shouldAddStoreAsynchronously = false
descriptor.isReadOnly = true
if let model = NSManagedObjectModel.makeManagedObjectModel(for: [Person.self]) {
let container = NSPersistentCloudKitContainer(name: "Watch Connection Data", managedObjectModel: model)
container.persistentStoreDescriptions = [descriptor]
// Load the persistent stores
container.loadPersistentStores { storeDescription, error in
if let error = error {
fatalError("Failed to load persistent store: \(error.localizedDescription)")
} else {
print("Persistent store loaded successfully: \(storeDescription)")
}
}
// Remove the store after schema creation
if let store = container.persistentStoreCoordinator.persistentStores.first {
do {
try container.persistentStoreCoordinator.remove(store)
} catch {
fatalError("Failed to remove persistent store: \(error.localizedDescription)")
}
}
}
}
#endif
return try! ModelContainer(for: Person.self, configurations: config)
}
@MainActor // Ensure this is run on the main actor
func fetchPeopleFromContainer(completion: @escaping ([Person]) -> Void) {
let container = setupModelContainer()
let context = container.mainContext
let fetchDescriptor = FetchDescriptor<Person>()
do {
let people = try context.fetch(fetchDescriptor)
for x in people {
print("Person name: \(x.name)")
}
completion(people)
} catch {
print("Error fetching people: \(error.localizedDescription)")
completion([])
}
}
struct PeopleWidgetEntry: TimelineEntry {
let date: Date
let people: [Person]
}
struct Provider: TimelineProvider {
func placeholder(in context: Context) -> PeopleWidgetEntry {
PeopleWidgetEntry(date: Date(), people: [])
}
func getSnapshot(in context: Context, completion: @escaping (PeopleWidgetEntry) -> Void) {
fetchPeopleData { people in
let entry = PeopleWidgetEntry(date: Date(), people: people)
completion(entry)
}
}
func getTimeline(in context: Context, completion: @escaping (Timeline<PeopleWidgetEntry>) -> Void) {
fetchPeopleData { people in
let entry = PeopleWidgetEntry(date: Date(), people: people)
let timeline = Timeline(entries: [entry], policy: .after(Date().addingTimeInterval(60 * 5))) // 5-minute interval
completion(timeline)
}
}
func fetchPeopleData(completion: @escaping ([Person]) -> Void) {
Task { @MainActor in
fetchPeopleFromContainer { people in
completion(people)
}
}
}
}
struct Watch_WidgetEntryView: View {
@Environment(\.widgetFamily) var widgetFamily
var entry: Provider.Entry
var body: some View {
switch widgetFamily {
case .accessoryInline:
Watch_Inline_View(personData: entry.people)
case .accessoryCorner:
Watch_Corner_View(personData: entry.people)
case .accessoryCircular:
Watch_Circular_View(personData: entry.people)
case .accessoryRectangular:
Watch_Rectangular_View(personData: entry.people)
default:
Text("Not available at this time: \(widgetFamily)")
}
}
}