SwiftData via CloudKit Only Syncing Upon Relaunch

Hello I'm a new developer and am learning the ropes. I have an app that I'm testing and seem to have run into a bug. The data is syncing from one device to another, however it takes closing the app on the Mac or force closing the app on iOS/iPadOS to get the app to reflect the new data.

Is there specific code I code share to help solve this issue or any suggestions that someone may have? Thank you ahead of time for your assistance.

import SwiftData

@main
struct ApplicantProcessorApp: App {
    var sharedModelContainer: ModelContainer = {
        let schema = Schema([
            Applicant.self,
        ])
        let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)

        do {
            return try ModelContainer(for: schema, configurations: [modelConfiguration])
        } catch {
            fatalError("Could not create ModelContainer: \(error)")
        }
    }()

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(sharedModelContainer)
    }
}

struct ContentView: View {
    var body: some View {
        FilteredApplicantListView()
    }
}

#Preview {
    ContentView()
        .modelContainer(SampleData.shared.modelContainer)
}

struct FilteredApplicantListView: View {
    @State private var searchText = ""
    
    var body: some View {
        NavigationSplitView {
            ApplicantListView(applicantFilter: searchText)
                .searchable(text: $searchText, prompt: "Enter Name, Email, or Phone Number")
                .autocorrectionDisabled(true)
        } detail: { }
    }
}

import SwiftData

struct ApplicantListView: View {
    @Environment(\.modelContext) private var modelContext
    @Query private var applicants: [Applicant]
    
    @State private var newApplicant: Applicant?
    
    init(applicantFilter: String = "") {
// Filters
    }
    
    var body: some View {
        Group {
            if !applicants.isEmpty {
                List {
                    ForEach(applicants) { applicant in
                        NavigationLink {
                            ApplicantView(applicant: applicant)
                        } label: {
                            HStack {
                                VStack {
                                    HStack {
                                        Text(applicant.name)
                                        Spacer()
                                    }
                                    HStack {
                                        Text(applicant.phoneNumber)
                                            .font(.caption)
                                        Spacer()
                                    }
                                    HStack {
                                        Text(applicant.email)
                                            .font(.caption)
                                        Spacer()
                                    }
                                    HStack {
                                        Text("Expires: \(formattedDate(applicant.expirationDate))")
                                            .font(.caption)
                                        Spacer()
                                    }
                                }
                                if applicant.applicationStatus == ApplicationStatus.approved {
                                    Image(systemName: "checkmark.circle")
                                        .foregroundStyle(.green)
                                        .font(.title)
                                } else if applicant.applicationStatus == ApplicationStatus.declined {
                                    Image(systemName: "xmark.circle")
                                        .foregroundStyle(.red)
                                        .font(.title)
                                } else if applicant.applicationStatus == ApplicationStatus.inProgress {
                                    Image(systemName: "hourglass.circle")
                                        .foregroundStyle(.yellow)
                                        .font(.title)
                                } else if applicant.applicationStatus == ApplicationStatus.waitingForApplicant {
                                    Image(systemName: "person.circle")
                                        .foregroundStyle(.yellow)
                                        .font(.title)
                                } else {
                                    Image(systemName: "yieldsign")
                                        .foregroundStyle(.yellow)
                                        .font(.title)
                                }
                            }
                        }
                    }
                    .onDelete(perform: deleteItems)
                }
            } else {
                ContentUnavailableView {
                    Label("No Applicants", systemImage: "pencil.fill")
                }
            }
        }
        .navigationTitle("Applicants")
        .toolbar {
            ToolbarItem(placement: .navigationBarTrailing) {
                EditButton()
            }
            
            ToolbarItem {
                Button(action: addApplicant) {
                    Label("Add Item", systemImage: "plus")
                }
            }
        }
        .sheet(item: $newApplicant) { applicant in
                        NavigationStack {
                            ApplicantView(applicant: applicant, isNew: true)
                        }
                    }
    }
    
    private func addApplicant() {
        withAnimation {
            let newItem = Applicant()
            modelContext.insert(newItem)
            newApplicant = newItem
        }
    }
    
    private func deleteItems(offsets: IndexSet) {
        withAnimation {
            for index in offsets {
                modelContext.delete(applicants[index])
            }
        }
    }
    
    func formattedDate(_ date: Date) -> String {
        let dateFormatter = DateFormatter()
        dateFormatter.dateStyle = .short
        dateFormatter.timeStyle = .none
        return dateFormatter.string(from: date)
    }
}
import SwiftData

@Model
final class Applicant {
    var name = ""
    var email = ""
    var phoneNumber = ""
    var applicationDate = Date.now
    var expirationDate: Date {
        return Calendar.current.date(byAdding: .day, value: 90, to: applicationDate)!
    }

In the "Signing and Capabilities" section of your app target, make sure you add the "Background Modes" capability and enable "Remote notifications"

https://developer.apple.com/documentation/swiftdata/syncing-model-data-across-a-persons-devices

There is an eventChangedNotification on NSPersistentCloudKitcontainer. You can add a notification/listener to your view/Observable class and re-fetch data like so:

import CoreData
import SwiftData
import CloudKit

@Observable
class ListViewModel {

  var cloudkitNotificationPublisher = NotificationCenter.default.publisher(for: NSPersistentCloudKitContainer.eventChangedNotification)

  func handleCloudkitNotification(_ notification: NotificationCenter.Publisher.Output) { 
    if let userInfo = notification.userInfo,
       let event = userInfo[NSPersistentCloudKitContainer.eventNotificationUserInfoKey] as? NSPersistentCloudKitContainer.Event 
    {
      if event.type == .import {
         // fetch entities/models here
      }
    }
  }

}

And then in your view, you can attach an .onReceive(_:) modifier to your view, passing in the publisher

var body: some View {
  VStack {
    // my list here
  }
  .onReceive(listViewModel.cloudkitNotificationPublisher) { publisher in
    listViewModel.handleCloudkitNotification(publisher)
  }
}

More information can be found here: https://fatbobman.com/en/posts/coredatawithcloudkit-4/#checking-network-synchronization-status

and here: https://developer.apple.com/documentation/coredata/nspersistentcloudkitcontainer/3618808-eventchangednotification

SwiftData via CloudKit Only Syncing Upon Relaunch
 
 
Q