Btnisme123
Btnisme123

Reputation: 87

Custom UICalendarView not update at the first time

I am customizing a UIViewCalendar and try to fetch data on it . If I used sample data it worked but when I used data from firebase , data fetched but not showing at the first time , only after I clicked the left-right button to switch between month in calendar (ex: move from November to October , November -> December)

Here is the fetch data method

    @Published var appointments = [Appointment]()

@MainActor
    func getAppointments() async throws {
        do{
            DispatchQueue.main.async{
                self.calendarState = .loading
            }
            //result of compactMap is optional
            //will change depend on initial variable
            appointments = try await db.collection(Constants.appointmentCollection).getDocuments()
                .documents.compactMap{
                     let id  = $0.data()[Constants.idFieldInFirestore] as? String ?? ""
                    let userName  = $0.data()[Constants.userNameFieldInFirestore] as? String ?? ""
                     let uid = $0.data()[Constants.uidFieldInFirestore] as? String ?? ""
                     let date  = $0.data()[Constants.dateFieldInFirestore] as? Timestamp
                    let doctorName =  $0.data()[Constants.doctorNameFieldInFirestore] as? String ?? ""
                    let isConfirm =  $0.data()[Constants.isConfirmFieldInFirestore] as? Bool ?? false
                    let appointmentTitle =  $0.data()[Constants.appointmentTitleFieldInFirestore] as? String ?? ""
                    let appointmentTypeValue =  $0.data()[Constants.appointmentTypeFieldInFirestore] as? String ?? ""
'dd'T'HH':'mm':'ssZZZ"
                    let dateConverted = date?.dateValue()
                    print(dateConverted)
                    let appointment = Appointment(userName: userName, uid: uid, date: dateConverted!, isConfirm:isConfirm,doctorName: doctorName, appointmentTitle: appointmentTitle )
                    appointments.append(appointment)
                    return appointment
                }
            DispatchQueue.main.async{
                for appointment in self.appointments{
                    //print(appointment.dateComponent)
                }
                print("appointment.dateComponent")
                self.calendarState = .loadedList(self.appointments)
            }
        }catch{
            DispatchQueue.main.async{
                self.calendarState = .failed(error)
            }
        }
    }

Here is the UICalendarView class

struct CalendarView:UIViewRepresentable{
    let interval : DateInterval
    @ObservedObject var appointmentStore : AppointmentStore
    @Binding var dateSelected: DateComponents?
    @Binding var displayEvents: Bool
    var state = FirebaseDataSource.AddingState.idle
//implement listener event for data
    
    func makeCoordinator() -> Coordinator {
        Coordinator(parent: self, appointmentStore: appointmentStore)
    }
    func makeUIView(context: Context) -> UICalendarView {
        let view = UICalendarView()
        view.delegate = context.coordinator
        view.calendar = Calendar(identifier: .gregorian)
        view.availableDateRange = interval
        let dataSelection = UICalendarSelectionSingleDate(delegate: context.coordinator)
        view.selectionBehavior = dataSelection
        return view
    }
    
     func updateUIView(_ uiView: UICalendarView, context: Context) {
        if let changedAppointment = appointmentStore.changedAppointment{
            uiView.reloadDecorations(forDateComponents: [changedAppointment.dateComponent], animated: true)
            appointmentStore.changedAppointment = nil
        }
        
        if let movedAppointment = appointmentStore.movedAppointment{
            uiView.reloadDecorations(forDateComponents: [movedAppointment.dateComponent], animated: true)
            appointmentStore.movedAppointment = nil
        }
    }
    
    typealias UIViewType = UICalendarView
    
    class Coordinator : NSObject , UICalendarViewDelegate ,UICalendarSelectionSingleDateDelegate{
        func dateSelection(_ selection: UICalendarSelectionSingleDate, didSelectDate dateComponents: DateComponents?) {
            parent.dateSelected = dateComponents
            guard let dateComponents else {return}
            let foundAppointment = $appointmentStore.firebaseDataSource.appointments
                .filter{$0.date.startOfDay.wrappedValue == dateComponents.date!.startOfDay}
            if !foundAppointment.isEmpty{
                parent.displayEvents.toggle()
            }
        }
        
        func dateSelection(_ selection: UICalendarSelectionSingleDate, canSelectDate dateComponents: DateComponents?) -> Bool{
            return true
        }
        var parent : CalendarView
        @ObservedObject var appointmentStore : AppointmentStore
        
        init(parent: CalendarView, appointmentStore: AppointmentStore) {
            self.parent = parent
            self.appointmentStore = appointmentStore
        }
        
        @MainActor
        func calendarView(_ calendarView: UICalendarView, decorationFor dateComponents: DateComponents) -> UICalendarView.Decoration? {
            print("item:")
            print(appointmentStore.firebaseDataSource.appointments.count)
            let foundAppointments = appointmentStore.firebaseDataSource.appointments
                .filter{$0.date.startOfDay == dateComponents.date?.startOfDay }
            if foundAppointments.isEmpty {
                return nil}
            if foundAppointments.count > 1 {
               
                return .image(UIImage(systemName: ""),color: .red,
                              size: .large)
            }
            let singleAppointment = foundAppointments.first!
            return .customView{
                print("Run update");
                let icon = UILabel()
                icon.text = singleAppointment.appointmentType.icon
                return icon
            }
        }
    }
    
}



How can I trigger the update event of UICalendarView?

Upvotes: 0

Views: 310

Answers (1)

Btnisme123
Btnisme123

Reputation: 87

I fixed it today after taking a look in Chris Wu 's article https://chriswu.com/posts/swiftui/uicalendarview/ I realized that my problem is I am currently nested the Published object.

My old flow :

FirebaseDataSource class(Created a list and fetching data)

-> AppointmentStore(Control data -update-delete....)

-> MainView(which contained UICalendarView)

I put my list in FirebaseDataSource instead of AppointmentStore so the MainView didn't get it .

My new flow :

FirebaseDataSource class(fetching data)

-> AppointmentStore(Created a list)

-> MainView(which contained UICalendarView)

Like this :

class AppointmentStore : ObservableObject{
     //var appointments  = [Appointment]()
    @Published var changedAppointment: Appointment?
    @Published var movedAppointment: Appointment?

    @Published private(set) var addingState = FirebaseDataSource.AddingState.idle
    @Published var appointments = [Appointment]()

    var  firebaseDataSource : FirebaseDataSource = FirebaseDataSource()
   
    init()  {
        Task{
            //TODO: fetch data from firebase
            do{
                //pass appointment here
                appointments = try await self.firebaseDataSource.getAppointments()
                
            }catch{
                print(error.localizedDescription)
            }
        }
    }

Upvotes: 0

Related Questions