1. 程式人生 > >iOS開發之EventKit框架的應用

iOS開發之EventKit框架的應用

iOS開發之EventKit框架的應用

一、關於系統日曆和提醒事宜

      iOS系統自帶的Calendar應用非常強大,使用者可以在其中新增日程事件,並且其提供了介面供其他應用進行呼叫,可以向日歷中進行事件的讀取和寫入。

      首先,Canlendar應用預設建立了幾個型別的日曆,使用者也可以根據需要建立自定義的日曆,如下圖:

在上圖中,US Holidays、Birthdays、Siri Found in Apps和Calendar是預設建立的幾個日曆,Custom是自定義的日曆,當用戶新建日曆事件時,需要關聯到某個日曆,如下:

對於系統的Reminders,其主要作用是提供事件列表,使用者可以向事件列表中新增提醒事件,同樣,提供預設建立了兩個事件列表,使用者也可以根據需要自行建立新的事件列表,如下圖:

使用日曆和提醒事宜這兩個應用,可以提高生活工作效率,例如郵件應用通過與日曆的互動可以將會議郵件新增到使用者的日程中,EventKit框架則是提供了介面與這兩個應用進行互動。

二、EventKit框架概覽

    EventKit核心的用途是配合系統的日曆與提醒應用,對事件提醒進行管理,其中核心類即結構如下:

從圖中可以看出,重要資料的管理類為EKEventStore,其他類都是用來描述對應的資料,下面會一一介紹。

三、日曆事件操作

      第三方應用需要操作使用者的日曆事件,需要獲取使用者授權,首先需要在info.plist檔案中新增如下許可權請求欄位:

獲取當前Calendars應用中定義的日曆示例程式碼如下:

#import "ViewController.h"
#import <EventKit/EventKit.h>

@interface ViewController ()
@property (nonatomic, strong) EKEventStore *eventStore;
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 初始化
    self.eventStore = [[EKEventStore alloc] init];
    // 請求許可權
    [self.eventStore requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError * _Nullable error) {
        if (!granted) {
            NSLog(@"拒絕訪問");
        } else {
            // 獲取所有日曆
            NSArray *calendars = [self.eventStore calendarsForEntityType:EKEntityTypeEvent];
            for (EKCalendar *calendar in calendars) {
                NSLog(@"%@", calendar);
            }
        }
    }];
}
@end

上面方法會獲取到所有的日曆,也可以通過來源來獲取日曆,後面會具體介紹。

使用如下程式碼可以新增新的日曆,通常,第三方應用如果要向用戶日曆中新增事件,可以先新增一個新的日曆:

- (void)createNewCalendar {
    EKCalendar *calendar = [EKCalendar calendarForEntityType:EKEntityTypeEvent eventStore:self.eventStore];
    for (EKSource *source in self.eventStore.sources) {
        if (source.sourceType == EKSourceTypeLocal) {
            // 設定來源為本地
            calendar.source = source;
        }
    }
    calendar.title = @"琿少的事項日曆";//自定義日曆標題
    calendar.CGColor = [UIColor purpleColor].CGColor;//自定義日曆顏色
    NSError* error;
    [_eventStore saveCalendar:calendar commit:YES error:&error];
}

效果如下圖:

日曆事件的查詢需要構造NSPredicate物件,示例如下:

- (void)queryEvent {
    for (EKCalendar *cal in [self.eventStore calendarsForEntityType:EKEntityTypeEvent]) {
        if ([cal.title isEqualToString:@"琿少的事項日曆"]) {
            NSCalendar *calendar = [NSCalendar currentCalendar];
         
            NSDateComponents *oneMonthFromNowComponents = [[NSDateComponents alloc] init];
            oneMonthFromNowComponents.month = 1;
            NSDate *oneMonthFromNow = [calendar dateByAddingComponents:oneMonthFromNowComponents
                                                                toDate:[NSDate date]
                                                               options:0];
            // 獲取從今往後一個月的
            NSPredicate*predicate = [self.eventStore predicateForEventsWithStartDate:[NSDate date] endDate:oneMonthFromNow calendars:@[cal]];
            
            NSArray *eventArray = [self.eventStore eventsMatchingPredicate:predicate];
            NSLog(@"%@", eventArray);
        }
    }
}

通過儲存的相關介面,也可以對某個事件進行修改,或建立新的事件,示例如下:

- (void)createEvent {
    EKCalendar *calen = nil;
    for (EKCalendar *cal in [self.eventStore calendarsForEntityType:EKEntityTypeEvent]) {
        if ([cal.title isEqualToString:@"琿少的事項日曆"]) {
            calen = cal;
        }
    }
    EKEvent *event = [EKEvent eventWithEventStore:self.eventStore];
    event.title = @"從應用建立的事件";
    event.startDate = [NSDate date];
    event.calendar = calen;
    NSCalendar *calendar = [NSCalendar currentCalendar];
    NSDateComponents *oneMonthFromNowComponents = [[NSDateComponents alloc] init];
    oneMonthFromNowComponents.hour += 1;
    NSDate *endDate = [calendar dateByAddingComponents:oneMonthFromNowComponents
                                                        toDate:[NSDate date]
                                                       options:0];
    event.endDate = endDate;

    event.notes = @"備註";
    [event setAllDay:NO];//設定全天

    //儲存事件
    NSError *error = nil;
    [self.eventStore saveEvent:event span:EKSpanThisEvent commit:YES error:&error];
    NSLog(@"%@",error);
}

下面示例程式碼用來刪除日曆事件:

- (void)removeEvent {
    for (EKCalendar *cal in [self.eventStore calendarsForEntityType:EKEntityTypeEvent]) {
        if ([cal.title isEqualToString:@"琿少的事項日曆"]) {
            NSCalendar *calendar = [NSCalendar currentCalendar];
            
            NSDateComponents *oneMonthFromNowComponents = [[NSDateComponents alloc] init];
            oneMonthFromNowComponents.month = 1;
            NSDate *oneMonthFromNow = [calendar dateByAddingComponents:oneMonthFromNowComponents
                                                                toDate:[NSDate date]
                                                               options:0];
            // 獲取從今往後一個月的
            NSPredicate*predicate = [self.eventStore predicateForEventsWithStartDate:[NSDate date] endDate:oneMonthFromNow calendars:@[cal]];
            
            NSArray *eventArray = [self.eventStore eventsMatchingPredicate:predicate];
            [self.eventStore removeEvent:eventArray.firstObject span:EKSpanThisEvent error:nil];
        }
    }
}

三、提醒事件

      提醒事件的用法和日曆事件的用法基本一致,首先在Reminder應用中,每一個列表就是一個日曆,下面程式碼示例了向列表中插入提醒事件的方法:

- (void)createNewReminder {
    EKReminder *reminder = [EKReminder reminderWithEventStore:self.eventStore];
    reminder.title = @"從應用建立的事件";
    reminder.notes = @"備註";
    for (EKCalendar *cal in [self.eventStore calendarsForEntityType:EKEntityTypeReminder]) {
        if ([cal.title isEqualToString:@"自定義"]) {
            reminder.calendar = cal;
        }
    }
    //儲存事件
    NSError *error = nil;
    [self.eventStore saveReminder:reminder commit:YES error:&error];
    NSLog(@"%@", error);
}

同樣,對應的也有Reminder的查詢,刪除等操作。

四、EKEventStore詳解

      EKEventStore類是EventKit中的核心類,用來對日曆和提醒的事件進行操作。其中方法與屬性列舉如下:

@interface EKEventStore : NSObject
// 獲取使用者授權情況
+ (EKAuthorizationStatus)authorizationStatusForEntityType:(EKEntityType)entityType;
// 初始化方法
- (id)init;
// 使用指定的來源建立例項物件
- (instancetype)initWithSources:(NSArray<EKSource *> *)sources;
// 請求某個型別的事件訪問許可權
- (void)requestAccessToEntityType:(EKEntityType)entityType completion:(EKEventStoreRequestAccessCompletionHandler)completion;
// 唯一標識
@property(nonatomic, readonly) NSString *eventStoreIdentifier;
// 代理源  使用initWithSources方法例項化對應
@property (nonatomic, readonly) NSArray<EKSource *> *delegateSources;
// 可用的來源
@property (nonatomic, readonly) NSArray<EKSource *> *sources;
// 通過identifier獲取指定的來源物件
- (nullable EKSource *)sourceWithIdentifier:(NSString *)identifier;
// 通過型別來獲取所有的日曆物件
- (NSArray<EKCalendar *> *)calendarsForEntityType:(EKEntityType)entityType;
// 獲取預設的日曆物件
@property(nullable, nonatomic, readonly) EKCalendar *defaultCalendarForNewEvents;
- (nullable EKCalendar *)defaultCalendarForNewReminders;
// 通過identifier獲取日曆物件
- (nullable EKCalendar *)calendarWithIdentifier:(NSString *)identifier;
// 新增一個新的日曆,commit引數是都是否立刻提交修改
- (BOOL)saveCalendar:(EKCalendar *)calendar commit:(BOOL)commit error:(NSError **)error;
// 刪除某個日曆
- (BOOL)removeCalendar:(EKCalendar *)calendar commit:(BOOL)commit error:(NSError **)error;
// 根據identifier獲取日程物件
- (nullable EKCalendarItem *)calendarItemWithIdentifier:(NSString *)identifier;
// 獲取一組日程物件
- (NSArray<EKCalendarItem *> *)calendarItemsWithExternalIdentifier:(NSString *)externalIdentifier;
// 新建事件 span用來設定對於週期事件本次設定是否影響未來的事件
- (BOOL)saveEvent:(EKEvent *)event span:(EKSpan)span error:(NSError **)error;
- (BOOL)saveEvent:(EKEvent *)event span:(EKSpan)span commit:(BOOL)commit error:(NSError **)error;
// 移出事件
- (BOOL)removeEvent:(EKEvent *)event span:(EKSpan)span error:(NSError **)error;
- (BOOL)removeEvent:(EKEvent *)event span:(EKSpan)span commit:(BOOL)commit error:(NSError **)error;
// 通過identifier獲取事件
- (nullable EKEvent *)eventWithIdentifier:(NSString *)identifier;
// 使用給定的NSPredicate進行事件查詢
- (NSArray<EKEvent *> *)eventsMatchingPredicate:(NSPredicate *)predicate;
// 使用給定的NSPredicate進行事件列舉
- (void)enumerateEventsMatchingPredicate:(NSPredicate *)predicate usingBlock:(EKEventSearchCallback)block;
// 構造NSPredicate查詢物件
- (NSPredicate *)predicateForEventsWithStartDate:(NSDate *)startDate endDate:(NSDate *)endDate calendars:(nullable NSArray<EKCalendar *> *)calendars;
// 提醒相關
// 新建一條提醒
- (BOOL)saveReminder:(EKReminder *)reminder commit:(BOOL)commit error:(NSError **)error;
// 刪除提醒
- (BOOL)removeReminder:(EKReminder *)reminder commit:(BOOL)commit error:(NSError **)error;
// 進行提醒查詢
- (id)fetchRemindersMatchingPredicate:(NSPredicate *)predicate completion:(void (^)(NSArray<EKReminder *> * __nullable reminders))completion;
// 取消某此查詢
- (void)cancelFetchRequest:(id)fetchIdentifier;
// 構造查詢物件
- (NSPredicate *)predicateForRemindersInCalendars:(nullable NSArray<EKCalendar *> *)calendars;
- (NSPredicate *)predicateForIncompleteRemindersWithDueDateStarting:(nullable NSDate *)startDate ending:(nullable NSDate *)endDate calendars:(nullable NSArray<EKCalendar *> *)calendars;
- (NSPredicate *)predicateForCompletedRemindersWithCompletionDateStarting:(nullable NSDate *)startDate ending:(nullable NSDate *)endDate calendars:(nullable NSArray<EKCalendar *> *)calendars;

// 將所有的修改一次性進行提交
- (BOOL)commit:(NSError **)error;
// 重置所有的修改
- (void)reset;
// 重新整理來源
- (void)refreshSourcesIfNecessary;
@end

EKEntityType用來描述型別,定義如下:

typedef NS_ENUM(NSUInteger, EKEntityType) {
    EKEntityTypeEvent,          // 日曆型別
    EKEntityTypeReminder        // 提醒型別
};

EKAuthorizationStatus用來描述使用者授權狀態:

typedef NS_ENUM(NSInteger, EKAuthorizationStatus) {
    EKAuthorizationStatusNotDetermined = 0,   // 使用者尚未選擇
    EKAuthorizationStatusRestricted,          // 應用無權訪問
    EKAuthorizationStatusDenied,              // 使用者拒絕授權
    EKAuthorizationStatusAuthorized,          // 使用者授權
} ;

EKSpan用來描述所影響事件型別:

typedef NS_ENUM(NSInteger, EKSpan) {
    EKSpanThisEvent,     // 隻影響本次事件
    EKSpanFutureEvents   // 對未來的事件也會產生影響
};

五、EKSource類詳解

      首先,EKSource描述了,例如某些日曆是系統預設建立的,使用者沒有許可權進行修改或刪除,某些日曆是使用者自定義建立的,還有些可能來自雲端,這個類繼承自EKObject類,首先先看EKObject類的定義:

@interface EKObject : NSObject
// 資料物件是否有修改
@property (nonatomic, readonly) BOOL hasChanges;
// 是否是一個新的資料物件(為儲存到日曆或提醒中)
- (BOOL)isNew;
// 重置資料 將所有屬性清空
- (void)reset;
// 回滾未提交的操作
- (void)rollback;
// 重新整理資料
- (BOOL)refresh;
@end

EKSource是EKObject的子類,其中封裝屬性和方法如下:

@interface EKSource : EKObject
// 來源物件的ID 唯一標識
@property(nonatomic, readonly) NSString        *sourceIdentifier;
// 來源物件的型別
@property(nonatomic, readonly) EKSourceType     sourceType;
// 來源物件的標題
@property(nonatomic, readonly) NSString        *title;
// 此來源物件中所包含的日曆物件
@property(nonatomic, readonly) NSSet<EKCalendar *> *calendars;
// 獲取此來源物件中某個型別的日曆 (日曆或提醒)
- (NSSet<EKCalendar *> *)calendarsForEntityType:(EKEntityType)entityType;
@end

EKSourceType列舉如下:

typedef NS_ENUM(NSInteger, EKSourceType) {
    EKSourceTypeLocal,     // 本地
    EKSourceTypeExchange,  // 交換
    EKSourceTypeCalDAV,    // CalDAV或iCloud
    EKSourceTypeMobileMe,  // MobileMe
    EKSourceTypeSubscribed,// 訂閱
    EKSourceTypeBirthdays  // 生日
};

六、EKCalendar類詳解

      EKCalendar是具體的日曆物件,開發者可以對自定義的日曆進行讀寫操作,其也是繼承自EKObject物件,解析如下:

@interface EKCalendar : EKObject
// 通過制定的EKEventStore物件建立一個新的日曆物件
+ (EKCalendar*)calendarWithEventStore:(EKEventStore *)eventStore;
// 建立某個型別的日曆物件 (日曆或提醒)
+ (EKCalendar *)calendarForEntityType:(EKEntityType)entityType eventStore:(EKEventStore *)eventStore;
// 當前日曆物件所屬於的來源
@property(null_unspecified, nonatomic, strong) EKSource        *source;
// 日曆物件的ID
@property(nonatomic, readonly) NSString         *calendarIdentifier;
// 日曆物件的標題  會在 日曆 應用 或 提醒 應用中顯示
@property(nonatomic, copy)     NSString          *title;
// 當前日曆物件的型別
/*
typedef NS_ENUM(NSInteger, EKCalendarType) {
    EKCalendarTypeLocal,
    EKCalendarTypeCalDAV,
    EKCalendarTypeExchange,
    EKCalendarTypeSubscription,
    EKCalendarTypeBirthday
};
*/
@property(nonatomic, readonly)     EKCalendarType     type;
// 當前日曆是否支援編輯
@property(nonatomic, readonly) BOOL allowsContentModifications;
// 當前日曆是否為訂閱型別
@property(nonatomic, readonly, getter=isSubscribed) BOOL subscribed;
// 當前日曆物件是否可變
@property(nonatomic, readonly, getter=isImmutable) BOOL immutable;
// 對應的提示顏色
@property(null_unspecified, nonatomic) CGColorRef CGColor;
// 支援的事件狀態
/*
typedef NS_OPTIONS(NSUInteger, EKCalendarEventAvailabilityMask) {
    EKCalendarEventAvailabilityNone         = 0,    // calendar doesn't support event availability
    
    EKCalendarEventAvailabilityBusy         = (1 << 0),
    EKCalendarEventAvailabilityFree         = (1 << 1),
    EKCalendarEventAvailabilityTentative    = (1 << 2),
    EKCalendarEventAvailabilityUnavailable  = (1 << 3),
};
*/
@property(nonatomic, readonly) EKCalendarEventAvailabilityMask supportedEventAvailabilities;
// 支援的日曆型別
/*
typedef NS_OPTIONS(NSUInteger, EKEntityMask) {
    EKEntityMaskEvent      = (1 << EKEntityTypeEvent),
    EKEntityMaskReminder   = (1 << EKEntityTypeReminder)
};
*/
@property(nonatomic, readonly) EKEntityMask allowedEntityTypes;
@end

七、EKEvent與EKReminder

      EKEvent與EKReminder類在設計上是平行的兩個類,前面的示例程式碼中也有介紹。EKEvent對應系統日曆中的事件,EKReminder對應系統提醒應用中的事件。他們都繼承自EKCalendarItem類,EKCalendarItem也是EKObject的子類,這個類解析如下:

@interface EKCalendarItem : EKObject
// UUID 事件的唯一標識
@property(nonatomic, readonly) NSString *UUID;
// 事件所屬於的日曆
@property(nonatomic, strong, null_unspecified) EKCalendar *calendar;
// 事件的ID
@property(nonatomic, readonly) NSString *calendarItemIdentifier;
// 事件的標題
@property(nonatomic, copy, null_unspecified) NSString *title;
// 地理位置
@property(nonatomic, copy, nullable) NSString *location;
// 備註資訊
@property(nonatomic, copy, nullable) NSString *notes;
// 網址URL
@property(nonatomic, copy, nullable) NSURL *URL;
// 最後一次更改時間
@property(nonatomic, readonly, nullable) NSDate *lastModifiedDate;
// 事件建立的時間
@property(nonatomic, readonly, nullable, strong) NSDate *creationDate;
// 時區
@property(nonatomic, copy, nullable) NSTimeZone *timeZone;
// 是否有鬧鐘提醒
@property(nonatomic, readonly) BOOL hasAlarms; 
// 是否包含重複提醒的規則
@property(nonatomic, readonly) BOOL hasRecurrenceRules;
// 是否包含參會人資訊
@property(nonatomic, readonly) BOOL hasAttendees;
// 是否包含備註
@property(nonatomic, readonly) BOOL hasNotes;
// 參會人資訊
@property(nonatomic, readonly, nullable) NSArray<__kindof EKParticipant *> *attendees;
// 鬧鐘提醒資訊
@property(nonatomic, copy, nullable) NSArray<EKAlarm *> *alarms;
// 新增鬧鐘規則
- (void)addAlarm:(EKAlarm *)alarm;
// 刪除鬧鐘規則
- (void)removeAlarm:(EKAlarm *)alarm;
// 包含的定期規則
@property(nonatomic, copy, nullable) NSArray<EKRecurrenceRule *> *recurrenceRules;
// 新增定期規則
- (void)addRecurrenceRule:(EKRecurrenceRule *)rule;
// 刪除定期規則
- (void)removeRecurrenceRule:(EKRecurrenceRule *)rule;
@end

上面類中有引用到一些其他的核心類,如EKParticipant、EKAlarm和EKRecurrenceRule,後面會介紹。

EKEvent是EKCalendarItem的子類,其對應日曆應用中的事件,解析如下:

@interface EKEvent : EKCalendarItem
// 建立一個新的事件物件
+ (EKEvent *)eventWithEventStore:(EKEventStore *)eventStore;
// 系統分類的事件ID
@property(null_unspecified, nonatomic, readonly) NSString *eventIdentifier;
// 是否是一個全天事件
@property(nonatomic, getter=isAllDay) BOOL allDay;
// 事件開始日期
@property(null_unspecified, nonatomic, copy) NSDate *startDate;
// 事件結束日期
@property(null_unspecified, nonatomic, copy) NSDate *endDate;
// 觸發型事件 到達某個地理位置後觸發
@property(nonatomic, copy, nullable) EKStructuredLocation *structuredLocation;
// 與另一個事件的開始事件進行比較
- (NSComparisonResult)compareStartDateWithEvent:(EKEvent *)other;
// 事件的組織者
@property(nonatomic, readonly, nullable) EKParticipant *organizer;
// 事件行為狀態
@property(nonatomic) EKEventAvailability    availability;
// 事件確認狀態
/*
typedef NS_ENUM(NSInteger, EKEventStatus) {
    EKEventStatusNone = 0,
    EKEventStatusConfirmed,
    EKEventStatusTentative,
    EKEventStatusCanceled,
};
*/
@property(nonatomic, readonly) EKEventStatus          status;
// 事件是否是獨立的
@property(nonatomic, readonly) BOOL isDetached;
// 事件發生時間
@property(null_unspecified, nonatomic, readonly) NSDate *occurrenceDate;
// 重新整理
- (BOOL)refresh;
// 對於生日事件 對應的生日聯絡人ID
@property(nonatomic, readonly, nullable) NSString *birthdayContactIdentifier;
@end

EKReminder與EKEvent類似,其對應系統的提醒事宜應用中的事件:

@interface EKReminder : EKCalendarItem
// 新建提醒事件
+ (EKReminder *)reminderWithEventStore:(EKEventStore *)eventStore;
// 開始時間
@property(nonatomic, copy, nullable) NSDateComponents *startDateComponents;
// 發生時間
@property(nonatomic, copy, nullable) NSDateComponents *dueDateComponents;
// 是否已完成
@property(nonatomic, getter=isCompleted) BOOL completed;
// 完成時間
@property(nonatomic, copy, nullable) NSDate *completionDate;
// 優先順序 1-4 為高  5 為中  6-9為低
@property(nonatomic) NSUInteger priority;
@end

八、EKAlarm與EKStructuredLocation

EKAlarm對應鬧鐘物件,事件可以繫結鬧鐘提醒,解析如下:

@interface EKAlarm : EKObject
// 通過絕對時間建立鬧鐘
+ (EKAlarm *)alarmWithAbsoluteDate:(NSDate *)date;
// 通過與現在的相對事件建立鬧鐘
+ (EKAlarm *)alarmWithRelativeOffset:(NSTimeInterval)offset;
// 與現在的相對時間
@property(nonatomic) NSTimeInterval relativeOffset;
// 絕對時間
@property(nonatomic, copy, nullable) NSDate *absoluteDate;
// 地理位置觸發的鬧鐘,對應的地理位置
@property(nonatomic, copy, nullable) EKStructuredLocation   *structuredLocation;
// 觸發方式
/*
typedef NS_ENUM(NSInteger, EKAlarmProximity) {
    EKAlarmProximityNone,
    EKAlarmProximityEnter,   // 進入觸發條件
    EKAlarmProximityLeave    // 離開觸發條件
};
*/
@property(nonatomic) EKAlarmProximity    proximity;
// 鬧鐘型別
/*
typedef NS_ENUM(NSInteger, EKAlarmType) {
    EKAlarmTypeDisplay,
    EKAlarmTypeAudio,
    EKAlarmTypeProcedure,
    EKAlarmTypeEmail
};
*/
@property(nonatomic, readonly) EKAlarmType type;
// 提醒的郵件地址
@property(nonatomic, copy, nullable) NSString *emailAddress;
// 提醒的音效名稱
@property(nonatomic, copy, nullable) NSString *soundName;
// 提醒後開啟的網頁
@property(nonatomic, copy, nullable) NSURL *url;
@end

EKStructuredLocation用來描述地區區域結構,如下:

@interface EKStructuredLocation : EKObject
// 通過標題建立
+ (instancetype)locationWithTitle:(NSString *)title;
// 通過地圖點建立
+ (instancetype)locationWithMapItem:(MKMapItem *)mapItem NS_AVAILABLE(10_11, 9_0);
// 標題
@property(nullable, nonatomic, strong) NSString     *title;
// 經緯度地理資訊
@property(nonatomic, strong, nullable) CLLocation   *geoLocation;
// 半徑
@property(nonatomic) double                radius;
@end

九、EKParticipant類詳解

    EKParticipant類是與事件相關的聯絡人資料類,解析如下:

@interface EKParticipant : EKObject
// 網址
@property(nonatomic, readonly) NSURL           *URL;
// 姓名
@property(nonatomic, readonly, nullable) NSString        *name;
// 狀態
/*
typedef NS_ENUM(NSInteger, EKParticipantStatus) {
    EKParticipantStatusUnknown,  // 未知
    EKParticipantStatusPending,  // 等待處理
    EKParticipantStatusAccepted, // 接收
    EKParticipantStatusDeclined, // 拒絕
    EKParticipantStatusTentative,// 嘗試
    EKParticipantStatusDelegated,// 代理
    EKParticipantStatusCompleted,// 完成
    EKParticipantStatusInProcess // 進行中
};
*/
@property(nonatomic, readonly) EKParticipantStatus participantStatus;
// 人員的角色
/*
typedef NS_ENUM(NSInteger, EKParticipantRole) {
    EKParticipantRoleUnknown,        // 未知
    EKParticipantRoleRequired,       // 必要的
    EKParticipantRoleOptional,       // 可選參與的
    EKParticipantRoleChair,          // 主持人
    EKParticipantRoleNonParticipant  // 參與者
};
*/
@property(nonatomic, readonly) EKParticipantRole participantRole;
// 參與人型別
/*
typedef NS_ENUM(NSInteger, EKParticipantType) {
    EKParticipantTypeUnknown,
    EKParticipantTypePerson,
    EKParticipantTypeRoom,
    EKParticipantTypeResource,
    EKParticipantTypeGroup
};
*/
@property(nonatomic, readonly) EKParticipantType participantType;
// 是否是當前使用者
@property(nonatomic, readonly, getter=isCurrentUser) BOOL currentUser;
// 用來查詢聯絡人的NSPredicate物件
@property(nonatomic, readonly) NSPredicate *contactPredicate;