A recent project requires EventKit, so I found this article on the official website. Although it’s an old document, it’s worth checking out for anyone who needs to access EventKit. The latest document is just on this basis to do some updates, for readers who are good at English, can directly see the official website, I here is a translation of the official document, plus a little bit of their own practice.

Introduces Calendars and Reminders

The EventKit framework helps you access a user’s calendar and reminder information. Although two different applications display the user’s calendar and reminder data, the framework for manipulating the data is the same. Similarly, the database that stores this data (called a Calendar database) holds both Calendar and reminder information.

EventKit architecture

Important: apps in iOS10.0 and later must configure instructions for accessing this data in their info.plist files, otherwise apps will crash. To access the data, Calendars and Reminders Info. Plist must contain NSRemindersUsageDescription and NSCalendarsUsageDescription respectively the two keys.

EventKit not only allows your app to retrieve a user’s existing calendar and reminder data, it also lets your app create new events and reminders for any calendar. In addition, EventKit allows users to edit and delete their events and reminders (collectively referred to as “calendar items”). More advanced tasks, such as adding an alarm or specifying repeated events, can also be done through EventKit. If there are calendar database changes outside of your application, EventKit can detect the changes through notifications so your application can run properly. Changes made to calendar entries using EventKit are automatically synchronized to the relevant calendar (CalDAV, Exchange, and so on).

This document describes the concepts and common programming tasks of EventKit. If you want to display or edit calendar events and/or reminder data in your application, you should read this document. EventKit provides limited access to the user’s calendar database. It doesn’t include everything you need to implement a full-featured calendar or reminders app, such as adding attendees or accounts.

At a Glance

This document contains the following sections that describe how to integrate user calendar and reminder data into your application:

  • Read and Write calendar Events: Explains how to retrieve, create, and modify calendar events.
  • Read and write alerts: explains how to retrieve, create, and modify alerts.
  • Configuring an alarm: Describes how to attach an alarm to a calendar item.
  • Create duplicate events: Explains how to set an event to duplicate events.
  • Observing external changes to the Calendar database: Explain how to register notifications related to external changes to the Calendar database
  • Interface for events: How to display an event view controller to allow users to create and edit events

Read and write calendar events

You can use the EKEventStore class to get, create, edit, and delete events from a user’s Calendar database. You can get a custom set of events that matches the predicates you provide, or you can get a single event by its unique identifier. Once an event is retrieved, its associated calendar information can be accessed through properties of the EKEvent class. Similarly, you can modify the calendar information of the EKEvent class by setting its properties.

Important: after iOS10.0 APP, if you need access to the calendar data, need to be in your APP Info. The file contains NSCalendarsUsageDescription keys, write not write is not important, but for the sake of a better user experience written advice

Connect to the Event Store

Initialize an EKEventStore object with the specified initializer:

EKEventStore *store = [[EKEventStore alloc] initWithAccessToEntityTypes:EKEntityMaskEvent];
Copy the code

Practice has found that the above initialization method is deprecated and only available on MAC OS10.8-10.9. So now we just initialize it with direct alloc init. After initialization EKEventStore, trying to get or create entity types of data, you must call requestAccessToEntityType: completion:.

An EKEventStore object takes a considerable amount of time to initialize and release, so you should not initialize and release a separate EKEventStore object for each event-related task. Instead, initialize a single EKEventStore object when the application loads and reuse it to ensure that your connection is permanent.

EKEventStore objects cannot be released before other EventKit objects; Otherwise, unknown behavior may occur.

Retrieve events

There are two ways to retrieve events

  • A fetch via a predicate or search query returns zero or more events that match a given query.
  • Getting by a unique identifier returns a single event corresponding to the given identifier.

Note: Retrieving events from the Calendar database does not necessarily return events in chronological order. To sort by date to a EKEvent object array, the array is called sortedArrayUsingSelector:, for compareStartDateWithEvent: methods provide selector.

Using a predicate

It is common to get events in the date range. EKEventStore method eventsMatchingPredicate: Gets all events within the date range specified in the predicate you provide. The following code demonstrates how to get all events that occurred between the day before and the year after the current date.

Please note: Although eventsMatchingPredicate: methods accept arguments of type NSPredicate, But you must provide a method by the EKEventStore predicateForEventsWithStartDate: endDate: calendar: create a predicate.

Use predicates to get event codes

    NSCalendar *calendar = [NSCalendar currentCalendar];

    NSDateComponents *oneDayAgoComponents = [[NSDateComponents alloc] init];
    oneDayAgoComponents.day = -1;
    NSDate *oneDayAgo = [calendar dateByAddingComponents:oneDayAgoComponents
                                                  toDate:[NSDate date]
                                                 options:0];
                                                 
    NSDateComponents *oneYearFromNorComponents = [[NSDateComponents alloc] init];
    oneYearFromNorComponents.year = 1;
    NSDate *oneYearFromNow = [calendar dateByAddingComponents:oneYearFromNorComponents
                                                       toDate:[NSDate date]
                                                      options:0];

    EKEventStore *store = [(AppDelegate *)UIApplication.sharedApplication.delegate eventStore];
    NSPredicate *predicate = [store predicateForEventsWithStartDate:oneDayAgo
                                                            endDate:oneYearFromNow
                                                          calendars:nil];
    NSArray *events = [store eventsMatchingPredicate:predicate];

    NSLog(@"%zd,%@",events.count,events);
Copy the code

By passing a EKCalendar array of objects as predicateForEventsWithStartDate: endDate: calendar, calendar parameters, you can specify a subset to search the calendar. You can get the user’s calendar from the calendarsForEntityType: method of the EKEventStore object. Pass kNilOptions to tell the method to get from all of the user’s calendars.

Because the eventsMatchingPredicate: methods are synchronous, you probably don’t want to run them on the main thread of your application. For asynchronous behavior, run the method on another thread using the dispatch_async function or an NSOperation object.

Use unique identifiers

If you previously got a unique identifier for an event through a predicate, you can use the EKEventStore object’s method eventWithIdentifier: to get the event. If it is a cyclic event, this method returns the first occurrence of the event. You can use the eventIdentifier attribute to get a unique identifier for an event.

Create and edit events

Note: If you’re developing on iOS, you can choose to have the user modify the event data using the Event View Controller provided by the EventKit UI framework. For information on how to use these event view controllers, see Providing interfaces to Events.

Create a new event using the eventWithEventStore: method of the EKEvent class.

You can edit the details of events by setting the corresponding properties of events, whether newly created or previously retrieved from the calendar database. Some of the details you can edit include the following methods:

  • titleProperty to edit the title of the event
  • startDateandendDateProperty to edit the start and end dates of the event
  • calendarProperty to edit the calendar associated with the event
  • alarmsProperty to edit alerts associated with the event (see Configuring alerts later for more information)
  • recurrenceRulesProperty to edit the loop rules for the event (if it is a repeating event, see Creating repeating Events below for more details)

Save and delete events

Important: If your application makes changes to the user’s calendar database, it must be confirmed by the user before making any changes. The application should never modify the calendar database without specific instructions from the user.

Changes made to an event are not permanent until the event is saved. SaveEvent: by using the method of EKEventStore span: commit: error: save your changes to the events. Database from the calendar if you want to delete an event, use the EKEventStore methods removeEvent: span: commit: error:. Whether you save or delete an event, implementing the corresponding method automatically synchronizes your changes with the calendar (CalDAV, Exchange, and so on) to which the event belongs.

If you are keep a recurring event, then you the changes can be applied to all the events in the future, by saveEvent: span: commit: error: method of span parameter specifies EKSpanFutureEvents. Similarly, you can use for removeEvent: span: commit: error: method of span parameter specifies EKSpanFutureEvents to delete all events in the future

Note: If you pass NO to the commit parameter, be sure to call the COMMIT: method later to permanently save the changes. If you pass NO to the commit parameter of any of the above methods, you will need to manually call the commit method later in the code to save the changes

Perform batch operations on events

You can then use the method of EKEventStore enumerateEventsMatchingPredicate all events predicate matching execute operation: you have to pass the EKEventStore method predicateForEventsWithStartDate: en DDate :calendars: to create the predicates used in the above method. The operation you provide is a Block of type EKEventSearchCallback.

typedef void (^EKEventSearchCallback)(EKEvent *event, BOOL *stop);
Copy the code

Block is passed two parameters: event The event that is currently being operated on. Stop a Boolean value, to determine enumerateEventsMatchingPredicate: usingBlock: should stop processing events. If so, any events that match the predicate but have not yet been processed remain unprocessed.

Important: Keep in mind that using this method may cause significant changes to a user’s Calendar database. When you ask the user for confirmation, make sure the user fully understands what will be done.

Because enumerateEventsMatchingPredicate: usingBlock: method is synchronous, so you may not want to in your application’s main thread running on it. For asynchronous behavior, run the method on another thread using the dispatch_async function or an NSOperation object.

Read and write reminders

A reminder is a task associated with a particular time or place. A reminder is a task associated with a particular time or place. They are similar to calendar events, but can be marked as completed and do not necessarily span an exact time period.

Because EKReminder inherits from EKCalendarItem, you can do the same for reminders as you do for events, such as using addAlarm: to add an alarm, or addRecurrenceRule: to set a recursive rule.

Important: if your iOS application links in iOS 10.0 or later, you need access to remind data, please ensure that on your Info. File contains NSRemindersUsageDescription keyword

Search tips

As with events, you must first establish a connection to the EKEventStore to access existing alerts. If you are not connected to the Event Store, see Connecting to the Event Store.

Initialize the connection and access reminder, passing EKEntityMaskReminder instead of EKEntityMaskEvent.

EKEventStore *store = [[EKEventStore alloc] initWithAccessToEntityTypes:EKEntityMaskReminder];
Copy the code

Again, this code is obsolete, so just alloc init and create an EKEventStore object.

Just like searching for events, there are two ways to retrieve reminders.

Using a predicate

You can call the fetchRemindersMatchingPredicate: completion: remind to access multiple matching with the predicate. Pass predicates returned by the following methods:

  • predicateForIncompleteRemindersWithDueDateStarting:ending:calendars:Find outstanding reminders in an optional period of time
  • predicateForCompletedRemindersWithCompletionDateStarting:ending:calendars:Find completed reminders in an optional time period
  • predicateForRemindersInCalendars:Look for all the alerts

By passing blocks to the Completion parameter, you can iterate between matching reminders, as shown in the code below.

    NSPredicate *predicate = [store predicateForRemindersInCalendars:nil];
    [store fetchRemindersMatchingPredicate:predicate completion:^(NSArray *reminders) {
        for (EKReminder *reminder in reminders) {
            // do something for each reminder
            
        }
    }];
Copy the code

Note: Unlike getting events through predicates (see Using predicates), you can get reminders asynchronously through predicates without having to dispatch to another thread. If you want to terminate the request back through the predicate, call cancelFetchRequest:, parameter is fetchRemindersMatchingPredicate: completion: the returned identifier.

Use unique identifiers

If you know that a particular reminds a unique identifier, you can call calendarItemWithIdentifier: object method. CalendarItemWithIdentifier: you can get any calendar entries (remind and event), and eventWithIdentifier: only for events.

Create and edit reminders

You can create reminders using EKReminder’s reminderWithEventStore: class method. The title and calendar attributes are required. The calendar property of a reminder is the list that groups it.

Reminders, like events, can trigger an alarm based on time or location to remind the user of a task. For more information on how to attach alarms to calendar items, see Configuring alarms.

To associate a start date or expiration date with a reminder, use the startDateComponents and dueDateComponents properties. To complete the reminder, set the Completed property to YES, which automatically sets completionDate to the current date.

Save and delete reminders

Important: If your application makes changes to the user’s calendar database, it must be confirmed by the user before making any changes. The application should never modify the calendar database without specific instructions from the user.

Reminders are saved like events. Would like to remind save to the Calendar database, called saveReminder: commit: error: method. To delete an event, call removeReminder: commit: error: method.

Remember that you must explicitly set the title and calendar properties before saving the reminder.

Note: As with saving or deleting events, make sure that if you pass NO to the COMMIT parameter, you later call the COMMIT: method to save the changes.

Configure the alarm

An easy way to alert users to upcoming events is to set an alarm for their calendar items. No matter what application is currently running, an alarm appears in the foreground as a notification alerting the user of scheduled events. If the alarm is set to a calendar event, the notification comes from the calendar application. If an alarm is set as a reminder, the notification comes from the alert application. An alarm can be time-based and triggered at a specified time. Or location-based, triggered when crossing a geo-fence.

Alarms can be used for calendar events and reminders.

Note: The alarm is not intended as a UILocalNotification. An alarm requires you to create an event or reminder that is visible in the user’s calendar or reminder application. UILocalNotification is better suited for general use that does not involve a Calendar database.

Mount and remove alarms

You can add an alarm to an event using the addAlarm: method. An alarm can be created using an absolute date or an offset relative to the event start date. Alerts created with relative offsets must occur before or on the start date of the event.

In OS X, you can trigger an action while issuing an alarm; For example, set:

  • The emailAddress property for sending E-mail messages
  • The soundName property used to play sounds
  • Open the URL property of the URL

You can remove the alarm from the event using the removeAlarm: method.

Geo-fencing

Note: Geo-fencing is supported on BOTH OS X and iOS, but they are more effective on mobile devices.

A geofencing is a virtual boundary around a geographical location that triggers an alarm of an event when it is crossed. Geofencing is a useful way to remind users of the tasks they need to perform when entering or leaving an area. For example, when a user leaves their workplace, an alarm goes off to remind them to stop at a grocery store. As a developer, you can control the latitude and longitude of the center, as well as the radius of the geographic fence.

Configure a geographic fence for events by creating an alarm and setting its structural location and proximity. Call the locationWithTitle: method to create a structured location. To set longitude and latitude coordinates, pass CLLocation to the geoLocation property of the returned structured location. A radius attribute value of 0 will use the default RADIUS of the system. To select your radius, specify a value in meters.

While geofencing alarms can be applied to events, they are better suited for alerts.

Creating duplicate events

A repeat event repeats at a specified time interval. To make an event a recurring event, assign it a recurring rule that describes when the event occurred. The recursive rule is represented by an instance of the EKRecurrenceRule class.

Recursion applies to calendar events and reminders. Unlike repeated events, only the first unfinished reminder of a repeated set is available. This is true of Both EventKit and the alerts app. When the reminder is complete, the next reminder in the recursive set is available.

Use basic rules

Can use initRecurrenceWithFrequency: interval: end: method to create a simple day, week, month or year model recursive rules. You provide three values for this method:

  • Recursion frequency. This is aEKRecurrenceFrequencyType indicating whether the recurrence rule is daily, weekly, monthly, or yearly.
  • Recursive interval. This is an integer greater than 0 that specifies how often the pattern repeats. For example, if the recursion rule is a weekly recursion rule and its interval is 1, then the pattern repeats once a week. If the recursion rule is a monthly recursion rule and its interval is 3, then the pattern repeats every three months.
  • The recursion is over. The optional argument isEKRecurrenceEndClass indicating when the recursive rule ends. Recursive endings can be based on a specific end date or number of occurrences. If you don’t want to specify an end for recursive rules, pass nil.

Use complex rules

You can use

    initRecurrenceWithFrequency:
                       interval:
                  daysOfTheWeek:
                 daysOfTheMonth:
                monthsOfTheYear:
                 weeksOfTheYear:
                  daysOfTheYear:
                   setPositions:
                            end:
Copy the code

Method to create a complex recursive rule. For basic recursive rules, you can provide frequency, interval, and optional termination for repeated events. In addition, you can provide optional combinations of values that describe custom rules, as shown in the following table

Complex recursive rule decomposition

The parameter name Accept the value of the What can be combined example
Day: What day of the week the event occurred aEKRecurrenceDayOfWeekAn array of objects All recursion rules except the daily recursion rule containsEKTuesdayandEKFridayAn array of objects will create a recursion that occurs every Tuesday and Friday.
Month day: Days in the month in which the event occurred An array of non-zero NSNumber objects in the range -31 to 31. Negative values indicate the countdown from the end of the month. Only repeat the rule monthly. An array containing the values 1 and -1 creates a recursion that occurs on the first and last day of each month.
Month: Month of the year in which events occur. NSNumber array of objects ranging from 1 to 12, corresponding to the Gregorian calendar month. Repeat the rules annually only. If the initial event occurred on January 10, you can provide an array of values 1 and 2 to create recursions that occur on January 10 and February 10 each year.
Anniversary: The number of weeks in a year in which events occur. An array of non-zero NSNumber objects in the range -53 to 53. A negative value means counting backwards from the end of the year. Repeat the rules annually only. If the initial event occurs on a Wednesday, you can provide an array of values 1 and -1 to create recursions that occur on the first and last Wednesday of each year. If the specified week does not include Wednesday of the current year (which can be the first or last week of the year), no event will occur.
Day year: The day of the year on which events occur. An array of non-zero NSNumber objects ranging from -366 to 366. A negative value means counting backwards from the end of the year. Repeat the rules annually only. You can provide an array of values 1 and -1 to create recursions that occur on the first and last day of the year.
Set position: the number of times included in the recursive rule. This filter applies to the set of events that are determined based on the additional parameters you provide. An array of non-zero NSNumber objects ranging from -366 to 366. Negative values count backwards from the end of the occurrence list. All recursion rules except the daily recursion rule If you provide an array of values 1 and -1 for an annual recursive rule that specifies Monday through Friday as its value, representing the number of days per week, the recursion occurs only on the first and last business day of the year.

You can provide values for any number of parameters in the table above. Parameters that do not apply to a particular recursive rule are ignored. If you provide a value for multiple of the above parameters, recursion occurs only on days that apply to all provided values.

Once a recursive rule is created, you can apply it to calendar events or reminders using the addRecurrenceRule: object method of EKCalendarItem.

Observe external changes to the calendar database

It is possible for other processes or applications to modify the calendar database while your application is running. If your application gets calendar events or reminders, you should register to be notified of changes to the calendar database. By doing this, you ensure that the calendar and reminder information displayed to the user is current.

registration

When EKEventStore objects detected calendar database changes, it will send a EKEventStoreChangedNotification notice. If your application processes event or alert data, register for this notification.

The following code is registered EKEventStoreChangedNotification notice, as shown below.

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(storeChanged:)
                                             name:EKEventStoreChangedNotification
                                           object:eventStore];
Copy the code

Response to the notification

When you receive a EKEventStoreChangedNotification notice, you get the object – such as EKEvent, EKReminder or EKCalendar – may have changed. The effect of these changes depends on whether events are added, modified, or removed.

  • If you add an event, it won’t affect any events or reminders you previously got, but the added event may be within the date range of the event you displayed to the user.
  • If an event is modified or deleted, it represents the eventEKEventandEKRemindrAttributes of an object may expire.

Due to changes in the Calendar databases, usually your local data is invalid or incomplete, so when you receive EKEventStoreChangedNotification notice, should be to get the current date of the event. If you’re modifying an event and you don’t want to refetch it unless absolutely necessary, you can call the refresh method of the event. If the method returns YES, the event can continue to be used; Otherwise, you need to obtain it again.

Note: When the calendar database changes, the events modified in the Event View Controller with the EventKit UI are automatically updated. For a more in-depth look at the EventKit UI, read the next chapter, providing interfaces for Events.

Provides interfaces for events

Important: The EventKit UI framework referenced in this chapter is for iOS only. If you’re developing for OS X, you’re responsible for building your own event view controller; For more information, see the NSViewController Class Reference.

The EventKit UI framework provides two types of view controllers to manipulate events:

  • EKEventViewController: Use this class if you have an event that you want to display.
  • EKEventEditViewController: use this class allows the user to create, edit, or delete events.

Show event data

To use the EKEventViewController class, you must have an existing event retrieved from the event store. Before rendering this type of view controller, you need to set the event properties and any other display options. The following code shows how to create an event view controller and add it to the navigation controller, assuming the myEvent already exists. If the user is not allowed to edit the event, set the allowsEditing property to NO.

    EKEventViewController *eventViewController = [[EKEventViewController alloc] init];
    eventViewController.event = myEvent;
    eventViewController.allowsEditing = YES;
    navigationController = [[UINavigationController alloc] initWithRootViewController:eventViewController];
Copy the code

You need to assign a delegate to the event view controller to receive notification when the user finishes viewing the event. The delegate conforms to EKEventViewDelegate agreement, and must implement eventViewController: didCompleteWithAction: method.

Modifying Event Data

To allow users to create, edit, or delete, please use the EKEventEditViewController class and EKEventEditViewDelegate protocol. You can create an event edit view controller similar to the event view controller, but you must set the eventStore property (setting the event property is optional).

  • When you render your view controller, if the Event property is nil, the user will create a new event in the default calendar and save it to the specified event store.
  • If the event attribute is not nil, the user edits an existing event. Events must reside in the specified event store, or an exception will be thrown.

EKEventEditViewController instances of the class is designed for modal, as shown in the following code. In this code, self is the top view controller of the navigation controller. For more information about modal view controllers, read Modal Presenting a View Controller.

Render the event edit view controller modally

    EKEventEditViewController* controller = [[EKEventEditViewController alloc] init];
    controller.eventStore = myEventStore;
    controller.editViewDelegate = self;
    [self presentModalViewController:controller animated:YES];
Copy the code

You must also specify a delegate to be notified when the user finishes editing the event. The delegate conforms to EKEventEditViewDelegate agreement, and must implement the eventteditviewcontroller: didCompleteWithAction: methods to dissolve modal view controller. Typically, the object that presents the view controller modally is responsible for dismissing it.

The delegate will dissolve the modal view

- (void)eventEditViewController:(EKEventEditViewController *)controller
          didCompleteWithAction:(EKEventEditViewAction)action
    {
        [self dismissModalViewControllerAnimated:YES];
    }
Copy the code

Delegates also pass the actions that the user takes to complete the edit. Users can cancel changes, save events, or delete events. If you need more after the user exit modal view code, implement eventteditviewcontroller: didCompleteWithAction: delegate method.