0

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)")
        }
    }
}

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.