iOS: User Data – Event & Calendar

6 minutes, 16 seconds

To access Reminders and Calendar data specifically, must include NSRemindersUsageDescription, NSCalendarsUsageDescription and NSContactsUsageDescription in Info.plist.

Note that we also need the contact usage because when we edit an event it can access our contact to access calendars.

Example to request access for user event.

Calendar Authorization


// header
import EventKit

// inside viewDidLoad
eventStore = EKEventStore.init()

eventStore.requestAccess(to: .event, completion: {
  (granted, error) in
  
  if granted {
    print("granted \(granted)")
    // add self.fetchEvents()
  }else {
    print("error \(error)")
  }
  
})

Fetching Calendar

Now after we get permission from user we will fetch events.


func fetchEvents(){
  let now = Date()
  let calendar = Calendar.current
  var dateComponents = DateComponents.init()
  dateComponents.day = 60
  let futureDate = calendar.date(byAdding: dateComponents, to: now) // 1
  
  let eventsPredicate = self.eventStore.predicateForEvents(withStart: now, end: futureDate!, calendars: nil) // 2 
  
  let events = self.eventStore.events(matching: eventsPredicate)
  
  for event in events{
    
    print("\(event.title)")
    
  }
}

1.We are creating a current timestamp and

  1. At eventsPredicate we don’t specify which calendar, so it will return from all calendars.

Events based on EKCalendar‘s


// declare
var calendarArray:[EKCalendar]!

// inside fetch events
func fetchEvents(){
  
  // Date
  let now = Date()
  let calendar = Calendar.current
  var dateComponents = DateComponents.init()
  dateComponents.day = 60 // how many 
  let date2daysFromNow = calendar.date(byAdding: dateComponents, to: now)

  // Calendars
  self.calendarArray = self.eventStore.calendars(for: .event)

  
  for calendar in self.calendarArray {
    
    let eventsPredicate = self.eventStore.predicateForEvents(withStart: now, end: date2daysFromNow!, calendars: [calendar])
    
    let events = self.eventStore.events(matching: eventsPredicate)

    print("calendar: \(calendar.title)")
    
    let color = UIColor.init(cgColor:calendar.cgColor) // do something with calendar color
    
    for event in events{
      
      print("event: \(event.title)")
      
    }
  }
}

Creating Calendar Event with iOS UI

We can use the build in iOS UI for displaying and editing calendar event: EKEventViewController and EKEventEditViewController.

We will show for both examples.


// add header
import EventKitUI

// we will need lastEvent just for get an EKEvent object


var lastEvent:EKEvent!
// add at when you looping the events
lastEvent = event

EKEventViewController


// declare this protocol
EKEventViewDelegate

// MARK: - EKEventViewDelegate

func eventViewController(_ controller: EKEventViewController, didCompleteWith action: EKEventViewAction) {
  dismiss(animated: true, completion: nil)
}

Present the view controller.


@IBAction func show(_ sender: Any) {
  
  let eventVC = EKEventViewController.init()
  eventVC.event = lastEvent
  eventVC.delegate = self
  
  let navCon = UINavigationController(rootViewController: eventVC)
  
  present(navCon, animated: true, completion: nil)
  
}

EKEventEditViewDelegate


// declare this protocol
EKEventEditViewDelegate, UINavigationControllerDelegate

Implementing the protocol.


@IBAction func show(_ sender: Any) {
  
  let eventVC = EKEventEditViewController.init()
  eventVC.event = lastEvent
  eventVC.editViewDelegate = self
  
  present(eventVC, animated: true, completion: nil)
  
}

// MARK:- EKEventEditViewDelegate
  
func eventEditViewController(_ controller: EKEventEditViewController, didCompleteWith action: EKEventEditViewAction) {
  controller.dismiss(animated: true, completion: nil)
}

Creating Event


@IBAction func create(_ sender: Any) {
  
  let eventVC = EKEventEditViewController.init()
  
  eventVC.event = EKEvent.init(eventStore: self.eventStore)
  
  // !this is important to make sure event can be saved
  eventVC.eventStore = self.eventStore 
  
  eventVC.editViewDelegate = self
  
  present(eventVC, animated: true, completion: nil)

}

For creating event, we have to implement this additional function for EKEventEditViewDelegate.


// MARK:- EKEventEditViewDelegate
func eventEditViewControllerDefaultCalendar(forNewEvents controller: EKEventEditViewController) -> EKCalendar {
    
  let calendar = self.eventStore.defaultCalendarForNewEvents
  
  controller.title = "Event for \(calendar.title)"
  
  return calendar
  
}

Creating Calendar Event Programmatically

We also can create an event with code.


func createEvent(){

  let event:EKEvent = EKEvent(eventStore: self.eventStore)
  
  event.title = "Test Title"
  event.startDate = Date()
  event.endDate = Date()
  event.notes = "This is a note"
  event.calendar = self.eventStore.defaultCalendarForNewEvents
  
  try! self.eventStore.save(event, span: EKSpan.thisEvent)
  
}

Recurring Events

We can create event with additional events in the future with specific rule which we using EKRecurranceRule


func createEvent(){

  let event:EKEvent = EKEvent(eventStore: self.eventStore)
  
  event.title = "Test Title"
  event.startDate = Date()
  event.endDate = Date()
  event.notes = "This is a note"
  event.calendar = self.eventStore.defaultCalendarForNewEvents
  
  let recurrenceRule = EKRecurrenceRule.init(recurrenceWith: .daily,
    interval: 1,
    daysOfTheWeek:[EKRecurrenceDayOfWeek.init(EKWeekday.sunday)],
    daysOfTheMonth: nil,
    monthsOfTheYear: nil,
    weeksOfTheYear: nil,
    daysOfTheYear: nil,
    setPositions: nil,
    end: EKRecurrenceEnd.init(occurrenceCount: 10))

  event.recurrenceRules = [recurrenceRule]
  
  try! self.eventStore.save(event, span: EKSpan.thisEvent)
  
}

At first this code will make an event for the start and end date.

In addition, we will also create a recurring event in the future with this rule.

We make the reccuring every week (interval 1) for Sunday at the same time.

We may also to set daysOfTheMonth, monthsOfTheYear, weeksOfTheYear or daysOfTheYear but we only can choose either one.

Creating an EKCalendar

This is a bit unusual to do but, at some case we might want to create our own calendar based on the app.


@IBAction func createCal(_ sender: Any) {
  
  var localSource: EKSource?
  
  for source in eventStore.sources{
    if source.sourceType == EKSourceType.local{
      localSource = source
      break
    }
  }
 
  var cal: EKCalendar?

  cal = EKCalendar.init(for: .event, eventStore: eventStore)
  
  cal?.title = "My App Calendar"
  cal?.source = localSource!;
  
  try! eventStore.saveCalendar(cal!, commit: true)
  
  print("cal title:\(cal?.title), id:\(cal?.calendarIdentifier)")
  
}