You must use [EKEvent eventWithEventStore:] to create an event

443 views Asked by At

I have used CalenderKit on my app. I have 2 types of users my goal is to collect calender data from targeted type and reflect it to other targeted type.

  • First type went quite well, I have collected the date range data and simply pulled it to Firebase. Works fine.

  • Second type makes me lose my mind. So idea here is to pull stored data from Firebase, generate it to EKEvent and reflect it to an empty Calendar via CalenderKit.

// EventBox Class
struct EventBox {
    let startDate: String
    let endDate: String
    let isAllDay: Bool
    let title: String
}
    var userEventBox: [EventBox] = []
    
    func getEvents() {
        
        self.db.collection(XX).document(XX).collection("Calendar").addSnapshotListener { [self] (querySnapshot, error) in
            
            self.userEventBox = []
            
            if let e = error {
                print("There was an issue retrieving data from Firestore. \(e)")
            } else {
                
                if let snapshotDocuments = querySnapshot?.documents {
                    
                    for doc in snapshotDocuments {
                        
                        let data = doc.data()
                        if let title = data["title"] as? String ,
                           let startDate = data["startDate"] as? String ,
                           let endDate = data["endDate"] as? String ,
                           let isAllDay = data["isAllDay"] as? Bool
                        {
                            let newEventBox = EventBox(startDate: startDate, endDate: endDate, isAllDay: isAllDay, title: title)
                            self.userEventBox.append(newEventBox)
                            print(newEventBox)
                            self.generate() //Triggers second func after data collected and stored
                        }
                    }
                }
            }
        }
    }
    

    func generate() {
        
        for generation in userEventBox {

// I had issues when I tried to save data to Firebase as Date and pull it back. So I decided to store dates as String and use dateFormatter when downloaded.
            
            let isoStartDate = generation.startDate
            let isoEndDate = generation.endDate
            
            let dateFormatter = ISO8601DateFormatter()
            let startDate = dateFormatter.date(from:isoStartDate)!
            let endDate = dateFormatter.date(from:isoEndDate)!

      //dates formatted      

            if let event = EKEvent() as? EKEvent {

            event.title = generation.title
            event.isAllDay = generation.isAllDay
            event.startDate = startDate
            event.endDate = endDate
            event.calendar.cgColor = CGColor(red: 1, green: 1, blue: 1, alpha: 1)
                self.generated = [event]
            }
            
        }
    }
    
    var generated = [EKEvent()] // This variable is where I store all events after its generated

// And finally I am triggering the first getEvents func in override

    override func eventsForDate(_ date: Date) -> [EventDescriptor] {
        
        self.getEvents()
        
        let calenderKitEvents = generated.map(EKWrapper.init)
        
        return calenderKitEvents
    }
}

Problem is I am having this error and I couldnt figured it out for days. Any help will be appreciated.

1

There are 1 answers

2
Ugur Usta On

So here is what I did and what has been achieved with it.

// created another class for generation
struct EventBoxWithDate {
    var startDate: Date
    var endDate: Date
    var isAllDay: Bool
    var title: String
    var color: CGColor
}

// Than I created a custom EKWrapper


final class CustomEKWrapper: EventDescriptor {
    public var dateInterval: DateInterval {
        get {
            DateInterval(start: ekEvent.startDate, end: ekEvent.endDate)
        }
        
        set {
            ekEvent.startDate = newValue.start
            ekEvent.endDate = newValue.end
        }
    }
    
    public var isAllDay: Bool {
        get {
            ekEvent.isAllDay
        }
        set {
            ekEvent.isAllDay = newValue
        }
    }
    
    public var text: String {
        get {
            ekEvent.title
        }
        
        set {
            ekEvent.title = newValue
        }
    }

    public var attributedText: NSAttributedString?
    public var lineBreakMode: NSLineBreakMode?
    
    public var color: UIColor {
        get {
            UIColor(cgColor: ekEvent.color)
        }
    }
    
    public var backgroundColor = UIColor()
    public var textColor = SystemColors.label
    public var font = UIFont.boldSystemFont(ofSize: 12)
    public weak var editedEvent: EventDescriptor? {
        didSet {
            updateColors()
        }
    }
    
    public private(set) var ekEvent: EventBoxWithDate
    
    public init(eventKitEvent: EventBoxWithDate) {
        self.ekEvent = eventKitEvent
        applyStandardColors()
    }
    
    public func makeEditable() -> Self {
        let cloned = Self(eventKitEvent: ekEvent)
        cloned.editedEvent = self
        return cloned
    }
    
    public func commitEditing() {
        guard let edited = editedEvent else {return}
        edited.dateInterval = dateInterval
    }
    
    private func updateColors() {
      (editedEvent != nil) ? applyEditingColors() : applyStandardColors()
    }
    
    /// Colors used when event is not in editing mode
    private func applyStandardColors() {
      backgroundColor = dynamicStandardBackgroundColor()
      textColor = dynamicStandardTextColor()
    }
    
    /// Colors used in editing mode
    private func applyEditingColors() {
      backgroundColor = color.withAlphaComponent(0.95)
      textColor = .white
    }
    
    /// Dynamic color that changes depending on the user interface style (dark / light)
    private func dynamicStandardBackgroundColor() -> UIColor {
      let light = backgroundColorForLightTheme(baseColor: color)
      let dark = backgroundColorForDarkTheme(baseColor: color)
      return dynamicColor(light: light, dark: dark)
    }
    
    /// Dynamic color that changes depending on the user interface style (dark / light)
    private func dynamicStandardTextColor() -> UIColor {
      let light = textColorForLightTheme(baseColor: color)
      return dynamicColor(light: light, dark: color)
    }
    
    private func textColorForLightTheme(baseColor: UIColor) -> UIColor {
      var h: CGFloat = 0, s: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0
      baseColor.getHue(&h, saturation: &s, brightness: &b, alpha: &a)
      return UIColor(hue: h, saturation: s, brightness: b * 0.4, alpha: a)
    }
    
    private func backgroundColorForLightTheme(baseColor: UIColor) -> UIColor {
      baseColor.withAlphaComponent(0.3)
    }
    
    private func backgroundColorForDarkTheme(baseColor: UIColor) -> UIColor {
      var h: CGFloat = 0, s: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0
      color.getHue(&h, saturation: &s, brightness: &b, alpha: &a)
      return UIColor(hue: h, saturation: s, brightness: b * 0.4, alpha: a * 0.8)
    }
    
    private func dynamicColor(light: UIColor, dark: UIColor) -> UIColor {
      if #available(iOS 13.0, *) {
        return UIColor { traitCollection in
          let interfaceStyle = traitCollection.userInterfaceStyle
          switch interfaceStyle {
          case .dark:
            return dark
          default:
            return light
          }
        }
      } else {
        return light
      }
    }
}

// And edited the code

    
    func getEvents() {
                
        self.db.collection(xx).document(xx).collection("Calendar").addSnapshotListener { [self] (querySnapshot, error) in
            
            self.userEventBox = []
            
            if let e = error {
                print("There was an issue retrieving data from Firestore. \(e)")
            } else {
                
                if let snapshotDocuments = querySnapshot?.documents {
                    
                    for doc in snapshotDocuments {
                        // @PAUL here I use timestamp for healthier usage as suggested.
                        let data = doc.data()
                        if let title = data["title"] as? String ,
                           let startDate = data["startDate"] as? Timestamp ,
                           let endDate = data["endDate"] as? Timestamp ,
                           let isAllDay = data["isAllDay"] as? Bool
                        {
                            let newEventBox = EventBox(startDate: startDate.dateValue(), endDate: endDate.dateValue(), isAllDay: isAllDay, title: title)
                            self.userEventBox.append(newEventBox)
                        }
                    }
                    self.generate()
                }
            }
        }
    }
    

    func generate() {
        

        for generation in userEventBox {
 
            let event = EventBoxWithDate(startDate: generation.startDate, endDate: generation.endDate, isAllDay: generation.isAllDay, title: generation.title, color: UIColor.green.cgColor)

           // EDITED THIS =>   self.generated = [event] 
           // TO THIS 
                self.generated.append(event)

            
        }
// AND ADDED
reloadData()
// EVERYTHING WORKS NOW
        }

    
    var generated: [EventBoxWithDate] = []
    
    override func eventsForDate(_ date: Date) -> [EventDescriptor] {
                
        let calenderKitEvents = generated.map(CustomEKWrapper.init)
        
        return calenderKitEvents
    }
}

//

This customization work quite well I can reflect my stored events on CalenderKit. Only thing left here is generated variable is storing all events but when code runs in override func and maps the generated variable it only shows the earliest event on the calender. When I figure it out I will update you . Maybe it will help someone else.

UPDATED THE GENERATE FUNC AND IT WORKS NOW FOR ALL EVENTS