Search code examples
iosswiftuicalendar

How do I pass CoreData into UICalendarview


I'm trying to use my CoreData entity 'Item' to show decorations on a calendar (using UIcalendarview in iOS16). I have my view called CalView calling the CalendarHelper struct (where the UICalendarView is coded and setup). It is showing decorations on every day currently, but I want to only show decorations on the days where there is a CoreData entry, and then enable navigation when you click on those days to the detail view. The problem is that I can't figure out how to get the CoreData entity into the CalendarHelper and UICalendarView to use it. It's currently giving me an error stating that I can't pass a FetchedResult in to an expected ObservedResult. But when I change them all to FetchedResult on that function, it gives me an error saying it's expecting a type of Item.

NOTE: I posted this over here as well but I think I messed up that post by deleting the original when I updated it. https://stackoverflow.com/questions/75138925/pass-coredata-into-uicalendarview-to-show-decorations-and-click-on-date-for-deta

CALVIEW:

struct CalView: View {
    
    var body: some View {
        ZStack {
            VStack {            
                HStack {
                   CalendarHelper(interval: DateInterval(start: .distantPast, end: .distantFuture))
                }
            }
        }
    }
}

CALENDARHELPER:

struct CalendarHelper: UIViewRepresentable {
    
    @Environment(\.managedObjectContext) private var viewContext
    @FetchRequest(
        sortDescriptors: SleepSort.default.descriptors,
            animation: .default)
        private var items: FetchedResults<Item>
    
    let interval: DateInterval
    
    func makeUIView(context: Context) -> UICalendarView {
        let calView = UICalendarView()
        calView.delegate = context.coordinator
        calView.calendar = Calendar(identifier: .gregorian)
        calView.availableDateRange = interval
        calView.fontDesign = .rounded
        let dateSelection = UICalendarSelectionSingleDate(delegate: context.coordinator)
        calView.selectionBehavior = dateSelection
        calView.wantsDateDecorations = true
        return calView
    }
    func makeCoordinator() -> Coordinator {
        Coordinator(parent: self, items: items)
        }
    
    func updateUIView(_ uiView: UICalendarView, context: Context) {

    }
    
    class Coordinator: NSObject, UICalendarViewDelegate, UICalendarSelectionSingleDateDelegate {
            var parent: CalendarHelper
            @ObservedObject var items: Item
        
            init(parent: CalendarHelper, items: ObservedObject<Item>) {
                self.parent = parent
                self._items = items
            }
            
            @MainActor
            func calendarView(_ calendarView: UICalendarView, decorationFor dateComponents: DateComponents) -> UICalendarView.Decoration? {
                let font = UIFont.systemFont(ofSize: 10)
                let configuration = UIImage.SymbolConfiguration(font: font)
                let image = UIImage(systemName: "star.fill", withConfiguration: configuration)?.withRenderingMode(.alwaysOriginal)
                return .image(image)
            }
            
            func dateSelection(_ selection: UICalendarSelectionSingleDate,
                               didSelectDate dateComponents: DateComponents?) {
            }
            
            func dateSelection(_ selection: UICalendarSelectionSingleDate,
                               canSelectDate dateComponents: DateComponents?) -> Bool {
                return true
            }
            
        }
}

Solution

  • Finally got around to messing with this and solved it this way:

    @MainActor
                func calendarView(_ calendarView: UICalendarView, decorationFor dateComponents: DateComponents) -> UICalendarView.Decoration? {
                    
                    let foundSleep = sleepItems.filter {$0.awakeDate?.startOfDay == dateComponents.date?.startOfDay}
                    if foundSleep.isEmpty { return nil }
    
                    let today = Calendar.current.startOfDay(for: Date())
                    
                  //  return UICalendarView.Decoration(__color: .red, size: .large)
                    return .customView {
                        let scoreCalView = UILabel()
                        scoreCalView.text = "🛏️"
                        return scoreCalView
                    }
                }
    
    extension Date {
        var startOfDay: Date {
            Calendar.current.startOfDay(for: self)
        }
    }