UI not dynamically updating from push notifications fetchdata function

Hi All,

I really need your help, I have been racking my brain to work out why, after a push notification triggers a fetchdata function from the server, my new bookings dont dynamically update the counter against the booking types.

        print("Received remote notification: \(userInfo)")
        
        if let dataInfo = userInfo["data"] as? [String: Any],
           let jsonData = try? JSONSerialization.data(withJSONObject: dataInfo) {
            print("Processing data from notification...")
            
            DispatchQueue.main.async {
                self.eventsViewModel.updateFromPushNotification(data: jsonData) { result in
                    completionHandler(result)
                }
            }
        } else {
            print("Failed to parse notification data")
            completionHandler(.noData)
        }
    }

    func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
        print("Failed to register for remote notifications: \(error)")
    }

    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        print("Will present notification: \(notification.request.content.userInfo)")
        DispatchQueue.main.async {
            self.eventsViewModel.fetchData()
        }
        completionHandler([.banner, .badge, .sound])
    }

    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        print("Did receive notification response: \(response.notification.request.content.userInfo)")
        let userInfo = response.notification.request.content.userInfo
        if let fetchNeeded = userInfo["fetchNeeded"] as? Bool, fetchNeeded {
            print("Initiating data fetch due to user interaction...")
            DispatchQueue.main.async {
                self.eventsViewModel.fetchData()
            }
        }
        completionHandler()
    }
    @Published var bookings: [AnyBooking] = []
    @Published var newBookings: [AnyBooking] = []
    @Published var calendarBookings: [String: [AnyBooking]] = [:]
    @Published var selectedBooking: AnyBooking?
    
    private var cancellables = Set<AnyCancellable>()
    private let calendarManager = CalendarManager.shared // Add calendarManager

    func fetchData() {
        guard let url = URL(string: "https://allsound.wisewms.uk/webhook_get") else {
            print("Invalid URL for webhook request")
            return
        }
        var request = URLRequest(url: url)
        request.httpMethod = "GET"
        let task = URLSession.shared.dataTask(with: request) { [weak self] (data, response, error) in
            guard let self = self else { return }
            if let error = error {
                print("Error fetching data: \(error.localizedDescription)")
                return
            }
            if let data = data, !data.isEmpty {
                if let newBookings = self.processBookings(data: data) {
                    DispatchQueue.main.async {
                        self.bookings = newBookings
                        self.separateAndOrganizeBookings(bookings: newBookings)
                    }
                } else {
                    print("Failed to process bookings.")
                }
            } else {
                print("No data received from server.")
            }
        }
        task.resume()
    }

@main
struct AllSoundApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    @StateObject var eventsViewModel = EventsViewModel()
    @Environment(\.scenePhase) var scenePhase
    @AppStorage("selectedTheme") private var selectedTheme: Theme = .system

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(eventsViewModel)
                .environmentObject(appDelegate.navigationCoordinator)
                .preferredColorScheme(selectedTheme.colorScheme)
                .onChange(of: scenePhase) { oldPhase, newPhase in
                    if newPhase == .active {
                        eventsViewModel.fetchData()
                    }
                }
        }
    }
}

@Congo2005 Have you debugged the fetchData() method to confirm that you receive a network response and could you try observing changes to bookings property using the onChange modifier.

also, is AnyBooking a struct or a class ?

Thank you so much for the reply, I get a network response and the booking does successfully get retrieved, only I have to go in to one of the booking types, which triggers another fetchdata function call before the list is appended with the new booking on the UI, if I then go back to the booking type list, the counter then correctly states the number of new bookings, the AnyBooking is a class


import Foundation
import CoreData

class AnyBooking: Identifiable, Equatable, Hashable {
    let id: String
    var booking: any BookingProtocol

    var displayTitle: String { booking.displayTitle }
    var displaySubtitle: String? { booking.displaySubtitle }
    var details: [String: String] { booking.details }
    var orderedDetails: [(String, String)] { booking.orderedDetails }
    var calendarEventIdentifier: String? {
        get { booking.calendarEventIdentifier }
        set { booking.calendarEventIdentifier = newValue }
    }
    var bookingType: String { booking.bookingType }

    init(_ booking: any BookingProtocol) {
        self.booking = booking
        self.id = booking.id
        print("AnyBooking initialized with ID: \(self.id)") // Log ID on initialization
    }
    
    init(managedObject: NSManagedObject) {
        if let barBooking = managedObject as? BarBookingEntity {
            self.booking = BarBooking(coreDataEntity: barBooking)
        } else if let eventBooking = managedObject as? EventBookingEntity {
            self.booking = EventBooking(coreDataEntity: eventBooking)
        } else if let playlistBooking = managedObject as? PlaylistBookingEntity {
            self.booking = PlaylistBooking(coreDataEntity: playlistBooking)
        } else if let quoteBooking = managedObject as? QuoteBookingEntity {
            self.booking = QuoteBooking(coreDataEntity: quoteBooking)
        } else if let weddingBooking = managedObject as? WeddingBookingEntity {
            self.booking = WeddingBooking(coreDataEntity: weddingBooking)
        } else {
            fatalError("Unknown booking type")
        }
        self.id = self.booking.id
        print("AnyBooking initialized with ID: \(self.id) from CoreData") // Log ID on initialization from CoreData
    }

    static func == (lhs: AnyBooking, rhs: AnyBooking) -> Bool {
        return lhs.id == rhs.id
    }

    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }

    func addToCalendar(completion: @escaping (Bool, String?, Error?) -> Void) {
        booking.addToCalendar { success, message, error in
            DispatchQueue.main.async {
                completion(success, message, error)
            }
        }
    }
}

extension AnyBooking {
    var bookingDate: String? {
        switch self.booking {
        case let barBooking as BarBooking:
            return barBooking.bDate
        case let eventBooking as EventBooking:
            return eventBooking.eDate
        case let playlistBooking as PlaylistBooking:
            return playlistBooking.pDOccasion
        case let quoteBooking as QuoteBooking:
            return quoteBooking.qDateEvent
        case let weddingBooking as WeddingBooking:
            return weddingBooking.wDate
        default:
            return nil
        }
    }
}

extension AnyBooking {
    var phoneNumber: String? {
        switch self.booking {
        case let barBooking as BarBooking:
            return barBooking.bNumber
        case let eventBooking as EventBooking:
            return eventBooking.eNumber
        case let quoteBooking as QuoteBooking:
            return quoteBooking.qNumber
        case let weddingBooking as WeddingBooking:
            return weddingBooking.wNumber
        default:
            return nil
        }
    }
}
UI not dynamically updating from push notifications fetchdata function
 
 
Q