55

I would be interested in knowing how to add a calendar event in the device, but using swift. I know there are some examples made in Objective-C, but at the moment nothing in swift. Many thanks.

Zorayr
  • 23,770
  • 8
  • 136
  • 129
  • Swift 2.0 Implementation added : [HERE ON LINK](http://stackoverflow.com/questions/246249/programmatically-add-custom-event-in-the-iphone-calendar/34790334#34790334) – Dashrath Jan 14 '16 at 13:04

7 Answers7

91

Note: If your app is crashing with This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSCalendarsUsageDescription key with a string value explaining to the user how the app uses this data., you'll need to add NSCalendarsUsageDescription to your info.plist. Can follow the example here.

Swift 5.0 Version

import Foundation
import EventKit

let eventStore : EKEventStore = EKEventStore()
      
// 'EKEntityTypeReminder' or 'EKEntityTypeEvent'

eventStore.requestAccess(to: .event) { (granted, error) in
  
  if (granted) && (error == nil) {
      print("granted \(granted)")
      print("error \(error)")
      
      let event:EKEvent = EKEvent(eventStore: eventStore)
      
      event.title = "Test Title"
      event.startDate = Date()
      event.endDate = Date()
      event.notes = "This is a note"
      event.calendar = eventStore.defaultCalendarForNewEvents
      do {
          try eventStore.save(event, span: .thisEvent)
      } catch let error as NSError {
          print("failed to save event with error : \(error)")
      }
      print("Saved Event")
  }
  else{
  
      print("failed to save event with error : \(error) or access not granted")
  }
}   

Reference : https://gist.github.com/mchirico/d072c4e38bda61040f91

Zorayr
  • 23,770
  • 8
  • 136
  • 129
toofani
  • 1,650
  • 13
  • 16
54

Swift 3.0 compatible:

func addEventToCalendar(title: String, description: String?, startDate: Date, endDate: Date, completion: ((_ success: Bool, _ error: NSError?) -> Void)? = nil) {
    let eventStore = EKEventStore()

    eventStore.requestAccess(to: .event, completion: { (granted, error) in
        if (granted) && (error == nil) {
            let event = EKEvent(eventStore: eventStore)
            event.title = title
            event.startDate = startDate
            event.endDate = endDate
            event.notes = description
            event.calendar = eventStore.defaultCalendarForNewEvents
            do {
                try eventStore.save(event, span: .thisEvent)
            } catch let e as NSError {
                completion?(false, e)
                return
            }
            completion?(true, nil)
        } else {
            completion?(false, error as NSError?)
        }
    })
}

And also import EventKit

So you can easily call this method from everywhere:

addEventToCalendar(title: "Girlfriend birthday", description: "Remember or die!", startDate: NSDate(), endDate: NSDate())

If you prefer, you can put this method inside an utiliy class and define it as 'static'.

Aark
  • 167
  • 3
  • 9
Luca Davanzo
  • 21,000
  • 15
  • 120
  • 146
  • 5
    You need to add "Privacy - Calendars Usage Description" in info.plist. – ThetNaing Mizo Aug 04 '17 at 03:34
  • 2
    thanks for @ThetNaingMizo for the info.plist info. I will take it a little further and will add the complete entry on the info.plist Key : Privacy - Calendars Usage Description Value : $(PRODUCT_NAME) calendar events – Jiraheta Apr 18 '18 at 16:22
13

You need to add "Privacy - Calendars Usage Description" to info.plist. Following code works with latest version of xcode and swift 3.

import EventKit
class EventHelper
{
    let appleEventStore = EKEventStore()
    var calendars: [EKCalendar]?
    func generateEvent() {
        let status = EKEventStore.authorizationStatus(for: EKEntityType.event)

        switch (status)
        {
        case EKAuthorizationStatus.notDetermined:
            // This happens on first-run
            requestAccessToCalendar()
        case EKAuthorizationStatus.authorized:
            // User has access
            print("User has access to calendar")
            self.addAppleEvents()
        case EKAuthorizationStatus.restricted, EKAuthorizationStatus.denied:
            // We need to help them give us permission
            noPermission()
        }
    }
    func noPermission()
    {
        print("User has to change settings...goto settings to view access")
    }
    func requestAccessToCalendar() {
        appleEventStore.requestAccess(to: .event, completion: { (granted, error) in
            if (granted) && (error == nil) {
                DispatchQueue.main.async {
                    print("User has access to calendar")
                    self.addAppleEvents()
                }
            } else {
                DispatchQueue.main.async{
                    self.noPermission()
                }
            }
        })
    }
    func addAppleEvents()
    {
        let event:EKEvent = EKEvent(eventStore: appleEventStore)
        event.title = "Test Event"
        event.startDate = NSDate() as Date
        event.endDate = NSDate() as Date
        event.notes = "This is a note"
        event.calendar = appleEventStore.defaultCalendarForNewEvents

        do {
            try appleEventStore.save(event, span: .thisEvent)
            print("events added with dates:")
        } catch let e as NSError {
            print(e.description)
            return
        }   
        print("Saved Event")
    }
}
Rujoota Shah
  • 1,251
  • 16
  • 18
5

I was able to adjust this and eliminate the compiler error mentioned in the comments to the answers above (and a few others), as follows:

 var eventStore : EKEventStore = EKEventStore()

    // 'EKEntityTypeReminder' or 'EKEntityTypeEvent'

    eventStore.requestAccessToEntityType(EKEntityType.Event, completion: {
        (granted, error) in

        if (granted) && (error == nil) {
            print("granted \(granted)")
            print("error \(error)")

            var event:EKEvent = EKEvent(eventStore: eventStore)

            event.title = "Test Title"
            event.startDate = NSDate()
            event.endDate = NSDate()
            event.notes = "This is a note"
            event.calendar = eventStore.defaultCalendarForNewEvents

            eventStore.saveEvent(event, span: EKSpan.ThisEvent, error: nil)

            print("Saved Event")
        } 
    })

However, I still get the following error at the bottom regarding "EKSpan.ThisEvent" : Incorrect argument label in call (have ':span:error:', expected ':span:commit:').

I tried changing "error" to "commit," but it gave me a compiler error saying it expected a Bool instead of nil. It seems like a problem having to do with updates in swift syntax.

Edit: I ended up following this tutorial and was able to get it to work.

  1. First, ask permission to access the calendar and (if that permission is granted) call a function to add the event.

    var savedEventId : String = ""
    
    func requestAccessPermission() {
        let eventStore = EKEventStore()
    
        let startDate = NSDate()
        let endDate = startDate.dateByAddingTimeInterval(60 * 60) // Ends one hour later
    
        if (EKEventStore.authorizationStatusForEntityType(.Event) != EKAuthorizationStatus.Authorized) {
            eventStore.requestAccessToEntityType(.Event, completion: {
            granted, error in
                self.createEvent(eventStore, title: "Test Title", startDate: startDate, endDate: endDate)
            })
        } else {
            createEvent(eventStore, title: "Test Title", startDate: startDate, endDate: endDate)
        }
    }
    
  2. The function that is called in the above code snippet to add the event:

    func createEvent(eventStore: EKEventStore, title: String, startDate: NSDate, endDate: NSDate) {
        let event = EKEvent(eventStore: eventStore)
        event.title = title
        event.startDate = startDate
        event.endDate = endDate
        event.calendar = eventStore.defaultCalendarForNewEvents
        do {
            try eventStore.saveEvent(event, span: .ThisEvent)
            savedEventId = event.eventIdentifier
        } catch {
            print("Error Saving")
        }
    }
    
Skwiggs
  • 1,348
  • 2
  • 17
  • 42
kamisama42
  • 595
  • 4
  • 18
3

This was really slow on iOS 11.2 Xcode 9.2, so I modified Luca Davanzo's answer to use queues (works a lot faster):

func addEventToCalendar(title: String, description: String?, startDate: Date, endDate: Date, completion: ((_ success: Bool, _ error: NSError?) -> Void)? = nil) {
        DispatchQueue.global(qos: .background).async { () -> Void in
            let eventStore = EKEventStore()

            eventStore.requestAccess(to: .event, completion: { (granted, error) in
                if (granted) && (error == nil) {
                    let event = EKEvent(eventStore: eventStore)
                    event.title = title
                    event.startDate = startDate
                    event.endDate = endDate
                    event.notes = description
                    event.calendar = eventStore.defaultCalendarForNewEvents
                    do {
                        try eventStore.save(event, span: .thisEvent)
                    } catch let e as NSError {
                        completion?(false, e)
                        return
                    }
                    completion?(true, nil)
                } else {
                    completion?(false, error as NSError?)
                }
            })
        }
    }
Ignacio Valdivieso
  • 1,181
  • 8
  • 5
1

Async/await without completion handlers:

func addCalendarEvent(eventTitle: String, startDate: Date, endDate: Date) {
    Task {
        do {
            let eventStore = EKEventStore()
            let requestResult: Bool = try await eventStore.requestAccess(to: .event)
            guard requestResult else {
                debugPrint("Couldn't not create a calendar event. Calendar access denied.")
                return
            }
            let event = EKEvent(eventStore: eventStore)
            event.title = eventTitle
            event.startDate = startDate
            event.endDate = endDate
            event.calendar = eventStore.defaultCalendarForNewEvents
            try eventStore.save(event, span: .thisEvent)
        } catch {
            debugPrint("Couldn't not create a calendar event. Error: \(error.localizedDescription)")
            return
        }
    }
}
0

same with location and alert

   func addEventToCalendar(title: String, description: String?, startDate: Date, endDate: Date, location: String?, completion: ((_ success: Bool, _ error: NSError?) -> Void)? = nil) {
    DispatchQueue.global(qos: .background).async { () -> Void in
        let eventStore = EKEventStore()

        eventStore.requestAccess(to: .event, completion: { (granted, error) in
            if (granted) && (error == nil) {
                let alarm = EKAlarm(relativeOffset: -3600.0)
                let event = EKEvent(eventStore: eventStore)
                event.title = title
                event.startDate = startDate
                event.endDate = endDate
                event.notes = description
                event.alarms = [alarm]
                event.location = location
                event.calendar = eventStore.defaultCalendarForNewEvents
                do {
                    try eventStore.save(event, span: .thisEvent)
                } catch let e as NSError {
                    completion?(false, e)
                    print ("\(#file) - \(#function) error: \(e.localizedDescription)")
                    return
                }
                completion?(true, nil)
            } else {
                completion?(false, error as NSError?)
                print ("\(#file) - \(#function) error: \(error)")
            }
        })
    }
}
Dmih
  • 530
  • 6
  • 9