iOS 日歷提醒 EKEvent EKReminder

一、簡(jiǎn)介

EventKit 允許你的app 訪問(wèn)已經(jīng)存在于日歷和提醒里面的事件吃环,同時(shí)也允許你的app創(chuàng)建一個(gè)提醒事件添加到日歷或者提醒中也颤。用戶可以編輯刪除日歷和提醒里面的事件。EventKit 還可以添加鬧鈴郁轻,和重復(fù)提醒的事件翅娶。

EKEvent創(chuàng)建的事件在日歷中顯示文留,EKReminder創(chuàng)建的時(shí)間在提醒事項(xiàng)中顯示

注意:EKEvent 和 EKReminder都繼承于EKCalendarItem, EKCalendarItem類是日歷事件和提醒的抽象超類竭沫,此類提供常用的屬性和方法來(lái)訪問(wèn)日歷項(xiàng)的屬性燥翅,例如設(shè)置日歷的功能,標(biāo)題和位置蜕提,以及支持附加注釋森书,顯示與會(huì)者,設(shè)置多個(gè)鬧鐘和指定重復(fù)規(guī)則贯溅。EKCalendarItem 的calendarItemIdentifier 是它唯一標(biāo)示,使用EKCalendarItem *item = [store calendarItemWithIdentifier:identifer];可以通過(guò)一個(gè)唯一標(biāo)示獲取一個(gè)EKCalendarItem對(duì)象

二躲查、配置權(quán)限

iOS 10 之后如果app 想要訪問(wèn)日歷和提醒事項(xiàng)它浅,我們必須在 我們的app 的Info.plist文件中添加NSRemindersUsageDescription和NSCalendarsUsageDescription的key否則app會(huì)閃退

  // 獲取日歷權(quán)限
 EKAuthorizationStatus ekStatus = [EKEventStore authorizationStatusForEntityType:EKEntityTypeEvent]

  // 申請(qǐng)日歷權(quán)限
 [self.eventStore] requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError * _Nullable error) {

 }];
  // 獲取提醒事項(xiàng)權(quán)限
 EKAuthorizationStatus ekStatus = [EKEventStore authorizationStatusForEntityType: EKEntityTypeReminder]

  // 申請(qǐng)?zhí)嵝咽马?xiàng)權(quán)限
 [self.eventStore] requestAccessToEntityType:EKEntityTypeReminder completion:^(BOOL granted, NSError * _Nullable error) {

 }];

三、創(chuàng)建自定義日歷

/**
 *  創(chuàng)建日歷
 *
 *  @param entityType      日歷類型 EKEntityTypeEvent EKEntityTypeReminder
 *  @param title      日歷標(biāo)題
 *  @param completion 回調(diào)方法
 */
- (void)createCustomCalendarWithEntityType:(EKEntityType)entityType title:(NSString *)title completion:(CalendarCompletion)completion {
    // 創(chuàng)建日歷
    EKCalendar * customCalendar = [EKCalendar calendarForEntityType:entityType eventStore:self.eventStore];
    //  必須設(shè)置source镣煮,否則無(wú)法創(chuàng)建calendar
    customCalendar.source = [self getAvailableCalendarSource];
    customCalendar.title = title;
    
    NSError *error = nil;
    BOOL isSuccess = [self.eventStore saveCalendar:customCalendar commit:YES error:&error];
    
    if (!error) {
        if (![NSString isBlankString:customCalendar.calendarIdentifier]) {
            
            //存儲(chǔ)calendarIdentifier
            NSString *calendarIdfLocalKey = nil;
            if (entityType == EKEntityTypeEvent) {
                calendarIdfLocalKey = kEventCalendarIdentifierLocalKey;
            }else{
                calendarIdfLocalKey = kReminderCalendarIdentifierLocalKey;
            }
            [XTUserDefault setValue:customCalendar.calendarIdentifier forKey:calendarIdfLocalKey];
            
        }else{
            isSuccess = NO;
            error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:@{NSLocalizedDescriptionKey : @"eventIdentifier不存在"}];
        }
    }else{
        isSuccess = NO;
    }
    
   !completion?:completion(isSuccess,error);
    
}

- (EKSource *)getAvailableCalendarSource {
    EKSource *localSource = nil;
    for (EKSource *source in self.eventStore.sources){
        if (source.sourceType == EKSourceTypeCalDAV && [source.title isEqualToString:@"iCloud"]){
            localSource = source;
            break;
        }
    }
    if (localSource == nil){
        for (EKSource *source in self.eventStore.sources){
            if (source.sourceType == EKSourceTypeLocal){
                localSource = source;
                break;
            }
        }
    }
    
    if (localSource == nil) {
        NSLog(@"XTCalendarEventManager getAvailableCalendarSource error");
    }
    
    return localSource;
}

注意1:創(chuàng)建的EKCalendar必須指定source姐霍,否則saveCalendar會(huì)失敗

注意2: 不是所有的source都可用于添加提醒事件的,一般來(lái)說(shuō)典唇,系統(tǒng)開(kāi)啟iCloud镊折,使用iCloud的Source,如果系統(tǒng)關(guān)閉iCloud介衔,使用本地Source

注意3:因?yàn)?EventStore 是 Calendar database 的數(shù)據(jù)庫(kù)引擎恨胚,所以應(yīng)該盡量少的對(duì)他進(jìn)行創(chuàng)建和銷毀,所以推薦使用EventStore的時(shí)候使用單例模式

注意4: 需要本地保存創(chuàng)建的Calendar的identifier炎咖。

四赃泡、創(chuàng)建日歷提醒事件EKEvent

/**
 *  添加日歷提醒事項(xiàng)
 *
 *  @param eventIdentifierLocalKey  事件ID 本地存儲(chǔ)Key
 *  @param title      事件標(biāo)題
 *  @param startDate  開(kāi)始時(shí)間
 *  @param endDate    結(jié)束時(shí)間
 *  @param allDay     是否全天
 *  @param alarmArray 鬧鐘集合(傳nil,則沒(méi)有)
 *  @param notes      事件備注(傳nil乘盼,則沒(méi)有)
 *  @param url        事件url(傳nil升熊,則沒(méi)有)
 *  @param completion 回調(diào)方法
 */
- (void)createCalendarEventWithEventIdentifierLocalKey:(NSString *)eventIdentifierLocalKey calendarTitle:(NSString *)title notes:(NSString * __nullable)notes startDate:(NSDate *)startDate endDate:(NSDate *)endDate allDay:(BOOL)allDay alarmArray:(NSArray <NSString *> * __nullable)alarmArray url:(NSURL * __nullable)url completion:(CalendarCompletion)completion {
    
    if (![self checkCaledarAuthority:EKEntityTypeEvent]) {
        NSError * authorityError = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:@{NSLocalizedDescriptionKey : @"未授權(quán)"}];
        !completion?:completion(NO,authorityError);
        return;
    }
    
    
    BOOL isSuccess = NO;
    EKEvent *event = [EKEvent eventWithEventStore:self.eventStore];
    event.title = title;
    event.allDay = allDay;
    event.notes = notes;
    event.URL = url?:[NSURL URLWithString:@"xtcloudmeeting://"];
    event.startDate = startDate;
    event.endDate = endDate;
    
    //添加鬧鐘提醒
    if (alarmArray && alarmArray.count > 0) {
        for (NSString *timeString in alarmArray) {
            [event addAlarm:[EKAlarm alarmWithRelativeOffset:[timeString integerValue]]];
        }
    }
    
    // 存儲(chǔ)到日歷源中
    EKCalendar *eventCalendar = [self findCustomCalendar:EKEntityTypeEvent];
    if (eventCalendar) {
        [event setCalendar:eventCalendar];
    }else{
        [event setCalendar:[self.eventStore defaultCalendarForNewEvents]];
        NSLog(@"XTCalendarEventManager save defaultCalendarForNewEvents");
    }
    
    // 保存日歷
    NSError *isError;
    isSuccess = [self.eventStore saveEvent:event span:EKSpanThisEvent commit:YES error:&isError];
    
    if (!isError) {
        if (![NSString isBlankString:event.eventIdentifier] && ![NSString isBlankString:eventIdentifierLocalKey]) {
            //存儲(chǔ)日歷ID
            [XTUserDefault setValue:event.eventIdentifier forKey:[self getCalendarItemLocalKey:eventIdentifierLocalKey type:EKEntityTypeEvent]];
        }else{
            isSuccess = NO;
            isError = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:@{NSLocalizedDescriptionKey : @"eventIdentifier不存在"}];
        }
    }
    
    !completion?:completion(isSuccess,isError);
    
    NSLog(@"XTCalendarEventManager createCalendarEventWithEventIdentifierLocalKey %@",eventIdentifierLocalKey);
    
}

注意:也需要保存創(chuàng)建的EKEvent的identifier

五、查詢EKEvent

/**
 *  查日歷事件
 *
 *  @param eventIdentifierLocalKey    事件ID(標(biāo)識(shí)符) 本地存儲(chǔ)Key
 */
- (EKEvent *)checkCalendarEventWithIdentifier:(NSString *)eventIdentifierLocalKey{
    
    if (![self checkCaledarAuthority:EKEntityTypeEvent]) {
        NSLog(@"XTCalendarEventManager checkCalendarEventWithIdentifier 未授權(quán)");
        return nil;
    }
    
    NSString *eIdentifier = [XTUserDefault getValueForKey:[self getCalendarItemLocalKey:eventIdentifierLocalKey type:EKEntityTypeEvent]];
    if (![NSString isBlankString:eIdentifier]) {
        EKEvent *event = [self.eventStore eventWithIdentifier:eIdentifier];
        return event;
    }
    return nil;
}

/**
 *  查日歷事件(可查詢一段時(shí)間內(nèi)的事件)
 *
 *  @param startDate  開(kāi)始時(shí)間
 *  @param endDate    結(jié)束時(shí)間
 *  @param modifytitle    標(biāo)題绸栅,為空則都要查詢
 */
- (NSArray *)checkCalendarEventWithStartDate:(NSDate *)startDate endDate:(NSDate *)endDate modifytitle:(NSString * __nullable)modifytitle {
    
    if (![self checkCaledarAuthority:EKEntityTypeEvent]) {
        NSLog(@"XTCalendarEventManager checkCalendarEventWithDate 未授權(quán)");
        return nil;
    }
    
    // 查詢到所有的日歷
    EKCalendar *eventCalendar = [self findCustomCalendar:EKEntityTypeEvent];
    
    if (!eventCalendar) {
        eventCalendar = [self.eventStore defaultCalendarForNewEvents];
    }
    
    NSPredicate *predicate = [self.eventStore predicateForEventsWithStartDate:startDate endDate:endDate calendars:@[eventCalendar]];
    
    // 獲取到范圍內(nèi)的所有事件
    NSArray *request = [self.eventStore eventsMatchingPredicate:predicate];
    // 按開(kāi)始事件進(jìn)行排序
    request = [request sortedArrayUsingSelector:@selector(compareStartDateWithEvent:)];
    
    if ([NSString isBlankString:modifytitle]) {
        return request;
    }else{
        NSMutableArray *onlyRequest = [NSMutableArray array];
        for (int i = 0; i < request.count; i++) {
            EKEvent *event = request[i];
            if (event.title && [event.title isEqualToString:modifytitle]) {
                [onlyRequest addObject:event];
            }
        }
        return onlyRequest;
    }
}

六级野、刪除EKEvent


/**
 *  刪除日歷事件(刪除單個(gè))
 *
 *  @param eventIdentifierLocalKey    事件ID(標(biāo)識(shí)符) 本地存儲(chǔ)Key
 */
- (BOOL)deleteCalendarEventWithIdentifier:(NSString *)eventIdentifierLocalKey{
    
    if (![self checkCaledarAuthority:EKEntityTypeEvent]) {
        NSLog(@"XTCalendarEventManager deleteCalendarEventWithIdentifier 未授權(quán)");
        return NO;
    }
    
    BOOL isDelete = NO;
    
    NSString *eIdentifier = [XTUserDefault getValueForKey:[self getCalendarItemLocalKey:eventIdentifierLocalKey type:EKEntityTypeEvent]];
    EKEvent *event;
    if (![NSString isBlankString:eIdentifier]) {
        event = [self.eventStore eventWithIdentifier:eIdentifier];
        isDelete = [self.eventStore removeEvent:event span:EKSpanThisEvent error:nil];
    }
    NSLog(@"XTCalendarEventManager deleteCalendarEventWithIdentifier %@ %d",eventIdentifierLocalKey,isDelete);
    return isDelete;
}

/**
 *  刪除日歷事件(可刪除一段時(shí)間內(nèi)的事件)
 *
 *  @param startDate  開(kāi)始時(shí)間
 *  @param endDate    結(jié)束時(shí)間
 *  @param modifytitle    標(biāo)題,為空則都要?jiǎng)h除
 */
- (BOOL)deleteCalendarEventWithStartDate:(NSDate *)startDate endDate:(NSDate *)endDate modifytitle:(NSString * __nullable)modifytitle {
    
    if (![self checkCaledarAuthority:EKEntityTypeEvent]) {
        NSLog(@"XTCalendarEventManager deleteCalendarEventWithDate 未授權(quán)");
        return NO;
    }
    
    // 獲取到此事件
    NSArray *request = [self checkCalendarEventWithStartDate:startDate endDate:endDate modifytitle:modifytitle];
    
    for (int i = 0; i < request.count; i ++) {
        // 刪除這一條事件
        EKEvent *event = request[i];
        NSError*error =nil;
        // commit:NO:最后再一次性提交
        [self.eventStore removeEvent:event span:EKSpanThisEvent commit:NO error:&error];
    }
    //一次提交所有操作到事件庫(kù)
    NSError *errored = nil;
    BOOL commitSuccess= [self.eventStore commit:&errored];
    return commitSuccess;
}

七粹胯、修改EKEvent

/**
 *  修改日歷事件
 *
 *  @param eventIdentifierLocalKey 事件ID(標(biāo)識(shí)符) 本地key
 *  @param title      修改事件標(biāo)題
 *  @param startDate  修改開(kāi)始時(shí)間
 *  @param endDate    修改結(jié)束時(shí)間
 *  @param allDay     修改是否全天
 *  @param alarmArray 修改鬧鐘集合
 *  @param notes      修改事件備注
 *  @param url        修改事件url
 *  @param completion 回調(diào)方法
 */
- (void)modifyCalendarEventWithEventIdentifierLocalKey:(NSString *)eventIdentifierLocalKey calendarTitle:(NSString *)title notes:(NSString * __nullable)notes startDate:(NSDate *)startDate endDate:(NSDate *)endDate allDay:(BOOL)allDay alarmArray:(NSArray <NSString *> * __nullable)alarmArray url:(NSURL * __nullable)url completion:(CalendarCompletion)completion{
    
    if (![self checkCaledarAuthority:EKEntityTypeEvent]) {
        NSError * authorityError = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:@{NSLocalizedDescriptionKey : @"未授權(quán)"}];
        !completion?:completion(NO,authorityError);
        return;
    }
    
    // 獲取到此事件
    EKEvent *event = [self checkCalendarEventWithIdentifier:eventIdentifierLocalKey];
    if (event) {
        BOOL isDelete = [self deleteCalendarEventWithIdentifier:eventIdentifierLocalKey];
        NSLog(@"XTCalendarEventManager modify delete %d",isDelete);
        [self createCalendarEventWithEventIdentifierLocalKey:eventIdentifierLocalKey calendarTitle:title notes:notes startDate:startDate endDate:endDate allDay:allDay alarmArray:alarmArray url:url completion:completion];
    }else{
        // 沒(méi)有此條日歷
        [self createCalendarEventWithEventIdentifierLocalKey:eventIdentifierLocalKey calendarTitle:title notes:notes startDate:startDate endDate:endDate allDay:allDay alarmArray:alarmArray url:url completion:completion];
        
    }
}

八蓖柔、創(chuàng)建EKReminder提醒事項(xiàng)

和EKEvent類似

/**
 *  添加Reminder提醒事項(xiàng)
 *
 *  @param reminderIdentifierLocalKey  事件ID 本地存儲(chǔ)Key
 *  @param title      事件標(biāo)題
 *  @param startDate  開(kāi)始時(shí)間
 *  @param dueDate    預(yù)計(jì)結(jié)束時(shí)間
 *  @param alarmDate 鬧鐘Date
 *  @param notes      事件備注(傳nil,則沒(méi)有)
 *  @param url        事件url(傳nil风纠,則沒(méi)有)
 *  @param completion 回調(diào)方法
 */
- (void)createCalendarReminderWithReminderIdentifierLocalKey:(NSString *)reminderIdentifierLocalKey calendarTitle:(NSString *)title notes:(NSString * __nullable)notes startDate:(NSDate *)startDate dueDate:(NSDate *)dueDate alarmDate:(NSDate *)alarmDate url:(NSURL * __nullable)url completion:(CalendarCompletion)completion {
    
    if (![self checkCaledarAuthority:EKEntityTypeReminder]) {
        NSError * authorityError = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:@{NSLocalizedDescriptionKey : @"未授權(quán)"}];
        !completion?:completion(NO,authorityError);
        return;
    }
    
    EKReminder *reminder = [EKReminder reminderWithEventStore:self.eventStore];
    reminder.title = title;
    reminder.notes = notes;
    reminder.URL = url?:[NSURL URLWithString:@"xtcloudmeeting://"];
    
    reminder.startDateComponents = [[NSCalendar currentCalendar] components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitHour|NSCalendarUnitMinute|NSCalendarUnitSecond fromDate:startDate];
    reminder.dueDateComponents = [[NSCalendar currentCalendar] components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitHour|NSCalendarUnitMinute|NSCalendarUnitSecond fromDate:dueDate];
    
    //添加鬧鐘提醒
    [reminder addAlarm:[EKAlarm alarmWithAbsoluteDate:alarmDate]];
    
    // 存儲(chǔ)到日歷源中
    EKCalendar *reminderCalendar = [self findCustomCalendar:EKEntityTypeReminder];
    if (reminderCalendar) {
        [reminder setCalendar:reminderCalendar];
    }else{
        [reminder setCalendar:self.eventStore.defaultCalendarForNewReminders];
    }
    
    NSError *error = nil;
    BOOL isSuccess = [self.eventStore saveReminder:reminder commit:YES error:&error];
    
    if (!error) {
        if (![NSString isBlankString:reminder.calendarItemIdentifier] && ![NSString isBlankString:reminderIdentifierLocalKey]) {
            [XTUserDefault setValue:reminder.calendarItemIdentifier forKey:[self getCalendarItemLocalKey:reminderIdentifierLocalKey type:EKEntityTypeReminder]];
        }else{
            isSuccess = NO;
            error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:@{NSLocalizedDescriptionKey : @"eventIdentifier不存在"}];
        }
        
    }else{
        isSuccess = NO;
    }
    
    !completion?:completion(isSuccess,error);
    
    NSLog(@"XTCalendarEventManager createCalendarReminderWithReminderIdentifierLocalKey %@",reminderIdentifierLocalKey);
    
}

注意:

  1. EKReminder 可添加多個(gè)鬧鐘Alarm,但實(shí)際測(cè)試中發(fā)現(xiàn)渊抽,只有第一個(gè)會(huì)生效
  2. Alarm 通過(guò)alarmWithRelativeOffset 創(chuàng)建時(shí),后面參數(shù)offset 表示鬧鐘出發(fā)時(shí)間和參照時(shí)間點(diǎn)的偏移量议忽,為負(fù)數(shù)時(shí)懒闷,表示在參照時(shí)間點(diǎn)前offset秒觸發(fā)。此時(shí)如果把Alarm添加到EKEvent上,參照時(shí)間點(diǎn)為startDate愤估,如果是添加到EKReminder上帮辟,參照時(shí)間點(diǎn)為dueDate。建議給EKReminder添加鬧鐘Alarm時(shí)玩焰,使用alarmWithAbsoluteDate方式創(chuàng)建Alarm

九由驹、查詢EKReminder

/**
 *  查Reminder提醒項(xiàng)
 *
 *  @param reminderIdentifierLocalKey    事件ID(標(biāo)識(shí)符) 本地存儲(chǔ)Key
 */
- (EKReminder *)checkCalendarReminderWithIdentifier:(NSString *)reminderIdentifierLocalKey{
    
    if (![self checkCaledarAuthority:EKEntityTypeReminder]) {
        NSLog(@"XTCalendarEventManager checkCalendarReminderWithIdentifier 未授權(quán)");
        return nil;
    }
    NSString *eIdentifier = [XTUserDefault getValueForKey:[self getCalendarItemLocalKey:reminderIdentifierLocalKey type:EKEntityTypeReminder]];
    if (![NSString isBlankString:eIdentifier]) {
        EKCalendarItem *calendarItem = [self.eventStore calendarItemWithIdentifier:eIdentifier];
        if ([calendarItem isKindOfClass:[EKReminder class]]) {
            return (EKReminder *)calendarItem;
        }
        return nil;
    }
    return nil;
}

/**
 *  查詢Reminder提醒事項(xiàng) (一段時(shí)間)(以dueDate為參照)
 *
 *  @param startDate  開(kāi)始時(shí)間
 *  @param endDate    結(jié)束時(shí)間
 *  @param completion 回調(diào)方法
 */
- (void)checkCalendarIncompleteRemindersWithDueDateStarting:(nullable NSDate *)startDate endDate:(nullable NSDate *)endDate completion:(CalendarFetchDataCompletion)completion{
    
    if (![self checkCaledarAuthority:EKEntityTypeReminder]) {
        !completion?:completion(nil);
        return;
    }
    
    EKCalendar *reminderCalendar = [self findCustomCalendar:EKEntityTypeReminder];
    
    if (!reminderCalendar) {
        reminderCalendar = [self.eventStore defaultCalendarForNewReminders];
    }
    
    NSPredicate *predicate = [self.eventStore predicateForIncompleteRemindersWithDueDateStarting:startDate ending:endDate calendars:@[reminderCalendar]];
    
    [self.eventStore fetchRemindersMatchingPredicate:predicate completion:^(NSArray<EKReminder *> * _Nullable reminders) {
        !completion?:completion(reminders);
    }];
}

十、刪除EKReminder

/**
 *  刪除Reminber(刪除單個(gè))
 *
 *  @param reminderIdentifierLocalKey    reminder ID(標(biāo)識(shí)符) 本地存儲(chǔ)Key
 */
- (BOOL)deleteCalendarReminderWithIdentifier:(NSString *)reminderIdentifierLocalKey {
    
    if (![self checkCaledarAuthority:EKEntityTypeReminder]) {
        NSLog(@"XTCalendarEventManager deleteCalendarReminderWithIdentifier 未授權(quán)");
        return NO;
    }
    
    BOOL isDelete = NO;
    EKReminder *reminder = [self checkCalendarReminderWithIdentifier:reminderIdentifierLocalKey];
    if (reminder) {
        isDelete = [self.eventStore removeReminder:reminder commit:YES error:nil];
    }
    NSLog(@"XTCalendarEventManager deleteCalendarReminderWithIdentifier %@ %d",reminderIdentifierLocalKey,isDelete);
    return isDelete;
}

/**
 *  刪除Reminder提醒事項(xiàng) (一段時(shí)間)(以dueDate為參照)
 *
 *  @param startDate  開(kāi)始時(shí)間
 *  @param endDate    結(jié)束時(shí)間
 *  @param completion 回調(diào)方法
 */

- (void)deleteCalendarIncompleteRemindersWithDueDateStarting:(nullable NSDate *)startDate endDate:(nullable NSDate *)endDate completion:(CalendarCompletion)completion{
    
    if (![self checkCaledarAuthority:EKEntityTypeReminder]) {
        NSError * authorityError = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:@{NSLocalizedDescriptionKey : @"未授權(quán)"}];
        !completion?:completion(NO,authorityError);
        return;
    }
    
    weakify_self
    [self checkCalendarIncompleteRemindersWithDueDateStarting:startDate endDate:endDate completion:^(NSArray *itemArray) {
        strongify_self
        for (int i=0; i<itemArray.count; i++) {
            EKReminder *tempReminder = (EKReminder *)[itemArray objectAtIndex:i];
            [self.eventStore removeReminder:tempReminder commit:NO error:nil];
        }
        NSError *error = nil;
        BOOL commitSucces = [self.eventStore commit:&error];
        !completion?:completion(commitSucces,error);
    }];
}

十一昔园、 修改EKReminder


/**
 *  修改Reminder提醒事項(xiàng)
 *
 *  @param reminderIdentifierLocalKey  事件ID 本地存儲(chǔ)Key
 *  @param title      事件標(biāo)題
 *  @param startDate  開(kāi)始時(shí)間
 *  @param dueDate    預(yù)計(jì)結(jié)束時(shí)間
 *  @param alarmDate 鬧鐘Date
 *  @param notes      事件備注(傳nil蔓榄,則沒(méi)有)
 *  @param url        事件url(傳nil,則沒(méi)有)
 *  @param completion 回調(diào)方法
 */
- (void)modifyCalendarReminderWithReminderIdentifierLocalKey:(NSString *)reminderIdentifierLocalKey calendarTitle:(NSString *)title notes:(NSString * __nullable)notes startDate:(NSDate *)startDate dueDate:(NSDate *)dueDate alarmDate:(NSDate *)alarmDate url:(NSURL * __nullable)url completion:(CalendarCompletion)completion {
    
    if (![self checkCaledarAuthority:EKEntityTypeReminder]) {
        NSError * authorityError = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:@{NSLocalizedDescriptionKey : @"未授權(quán)"}];
        !completion?:completion(NO,authorityError);
        return;
    }
    
    EKReminder *tempReminder = [self checkCalendarReminderWithIdentifier:reminderIdentifierLocalKey];
    if (tempReminder) {
        [self deleteCalendarReminderWithIdentifier:reminderIdentifierLocalKey];
        [self createCalendarReminderWithReminderIdentifierLocalKey:reminderIdentifierLocalKey calendarTitle:title notes:notes startDate:startDate dueDate:dueDate alarmDate:alarmDate url:url completion:completion];
    }else{
        [self createCalendarReminderWithReminderIdentifierLocalKey:reminderIdentifierLocalKey calendarTitle:title notes:notes startDate:startDate dueDate:dueDate alarmDate:alarmDate url:url completion:completion];
    }
    
}

完整代碼

XTCalendarEventManager.h

#import <Foundation/Foundation.h>

#import <EventKit/EventKit.h>

typedef void(^__nullable CalendarCompletion)(BOOL isSuccess, NSError * _Nullable error);
typedef void(^__nullable CalendarFetchDataCompletion)(NSArray * _Nullable itemArray);

NS_ASSUME_NONNULL_BEGIN

@interface XTCalendarEventManager : NSObject
AS_SINGLETON(XTCalendarEventManager);


#pragma mark - 日歷提醒 -

/**
 *  添加日歷提醒事項(xiàng)
 *
 *  @param eventIdentifierLocalKey  事件ID 本地存儲(chǔ)Key
 *  @param title      事件標(biāo)題
 *  @param startDate  開(kāi)始時(shí)間
 *  @param endDate    結(jié)束時(shí)間
 *  @param allDay     是否全天
 *  @param alarmArray 鬧鐘集合(傳nil默刚,則沒(méi)有)
 *  @param notes      事件備注(傳nil甥郑,則沒(méi)有)
 *  @param url        事件url(傳nil,則沒(méi)有)
 *  @param completion 回調(diào)方法
 */
- (void)createCalendarEventWithEventIdentifierLocalKey:(NSString *)eventIdentifierLocalKey calendarTitle:(NSString *)title notes:(NSString * __nullable)notes startDate:(NSDate *)startDate endDate:(NSDate *)endDate allDay:(BOOL)allDay alarmArray:(NSArray <NSString *> * __nullable)alarmArray url:(NSURL * __nullable)url completion:(CalendarCompletion)completion;


/**
 *  查日歷事件
 *
 *  @param eventIdentifierLocalKey    事件ID(標(biāo)識(shí)符) 本地存儲(chǔ)Key
 */
- (EKEvent *)checkCalendarEventWithIdentifier:(NSString *)eventIdentifierLocalKey;


/**
 *  查日歷事件(可查詢一段時(shí)間內(nèi)的事件)
 *
 *  @param startDate  開(kāi)始時(shí)間
 *  @param endDate    結(jié)束時(shí)間
 *  @param modifytitle    標(biāo)題荤西,為空則都要查詢
 */
- (NSArray *)checkCalendarEventWithStartDate:(NSDate *)startDate endDate:(NSDate *)endDate modifytitle:(NSString * __nullable)modifytitle;


/**
 *  刪除日歷事件(刪除單個(gè))
 *
 *  @param eventIdentifierLocalKey    事件ID(標(biāo)識(shí)符) 本地存儲(chǔ)Key
 */
- (BOOL)deleteCalendarEventWithIdentifier:(NSString *)eventIdentifierLocalKey;


/**
 *  刪除日歷事件(可刪除一段時(shí)間內(nèi)的事件)
 *
 *  @param startDate  開(kāi)始時(shí)間
 *  @param endDate    結(jié)束時(shí)間
 *  @param modifytitle    標(biāo)題澜搅,為空則都要?jiǎng)h除
 */
- (BOOL)deleteCalendarEventWithStartDate:(NSDate *)startDate endDate:(NSDate *)endDate modifytitle:(NSString * __nullable)modifytitle;

/**
 *  修改日歷事件
 *
 *  @param eventIdentifierLocalKey 事件ID(標(biāo)識(shí)符) 本地key
 *  @param title      修改事件標(biāo)題
 *  @param startDate  修改開(kāi)始時(shí)間
 *  @param endDate    修改結(jié)束時(shí)間
 *  @param allDay     修改是否全天
 *  @param alarmArray 修改鬧鐘集合
 *  @param notes      修改事件備注
 *  @param url        修改事件url
 *  @param completion 回調(diào)方法
 */
- (void)modifyCalendarEventWithEventIdentifierLocalKey:(NSString *)eventIdentifierLocalKey calendarTitle:(NSString *)title notes:(NSString * __nullable)notes startDate:(NSDate *)startDate endDate:(NSDate *)endDate allDay:(BOOL)allDay alarmArray:(NSArray <NSString *> * __nullable)alarmArray url:(NSURL * __nullable)url completion:(CalendarCompletion)completion;




#pragma mark - Reminder 提醒 -
/**
 *  添加Reminder提醒事項(xiàng)
 *
 *  @param reminderIdentifierLocalKey  事件ID 本地存儲(chǔ)Key
 *  @param title      事件標(biāo)題
 *  @param startDate  開(kāi)始時(shí)間
 *  @param dueDate    預(yù)計(jì)結(jié)束時(shí)間
 *  @param alarmDate 鬧鐘Date
 *  @param notes      事件備注(傳nil,則沒(méi)有)
 *  @param url        事件url(傳nil邪锌,則沒(méi)有)
 *  @param completion 回調(diào)方法
 */
- (void)createCalendarReminderWithReminderIdentifierLocalKey:(NSString *)reminderIdentifierLocalKey calendarTitle:(NSString *)title notes:(NSString * __nullable)notes startDate:(NSDate *)startDate dueDate:(NSDate *)dueDate alarmDate:(NSDate *)alarmDate url:(NSURL * __nullable)url completion:(CalendarCompletion)completion;


/**
 *  查Reminder提醒項(xiàng)
 *
 *  @param reminderIdentifierLocalKey    事件ID(標(biāo)識(shí)符) 本地存儲(chǔ)Key
 */
- (EKReminder *)checkCalendarReminderWithIdentifier:(NSString *)reminderIdentifierLocalKey;



/**
 *  查詢Reminder提醒事項(xiàng) (一段時(shí)間)(以dueDate為參照)
 *
 *  @param startDate  開(kāi)始時(shí)間
 *  @param endDate    結(jié)束時(shí)間
 *  @param completion 回調(diào)方法
 */
- (void)checkCalendarIncompleteRemindersWithDueDateStarting:(nullable NSDate *)startDate endDate:(nullable NSDate *)endDate completion:(CalendarFetchDataCompletion)completion;


/**
 *  刪除Reminber(刪除單個(gè))
 *
 *  @param reminderIdentifierLocalKey    reminder ID(標(biāo)識(shí)符) 本地存儲(chǔ)Key
 */
- (BOOL)deleteCalendarReminderWithIdentifier:(NSString *)reminderIdentifierLocalKey;



/**
 *  刪除Reminder提醒事項(xiàng) (一段時(shí)間)(以dueDate為參照)
 *
 *  @param startDate  開(kāi)始時(shí)間
 *  @param endDate    結(jié)束時(shí)間
 *  @param completion 回調(diào)方法
 */

- (void)deleteCalendarIncompleteRemindersWithDueDateStarting:(nullable NSDate *)startDate endDate:(nullable NSDate *)endDate completion:(CalendarCompletion)completion;



/**
 *  修改Reminder提醒事項(xiàng)
 *
 *  @param reminderIdentifierLocalKey  事件ID 本地存儲(chǔ)Key
 *  @param title      事件標(biāo)題
 *  @param startDate  開(kāi)始時(shí)間
 *  @param dueDate    預(yù)計(jì)結(jié)束時(shí)間
 *  @param alarmDate 鬧鐘Date
 *  @param notes      事件備注(傳nil勉躺,則沒(méi)有)
 *  @param url        事件url(傳nil,則沒(méi)有)
 *  @param completion 回調(diào)方法
 */
- (void)modifyCalendarReminderWithReminderIdentifierLocalKey:(NSString *)reminderIdentifierLocalKey calendarTitle:(NSString *)title notes:(NSString * __nullable)notes startDate:(NSDate *)startDate dueDate:(NSDate *)dueDate alarmDate:(NSDate *)alarmDate url:(NSURL * __nullable)url completion:(CalendarCompletion)completion;
@end

NS_ASSUME_NONNULL_END

XTCalendarEventManager.m

#import "XTCalendarEventManager.h"
#import "NSString+isBlank.h"
#import "XTAuthorityManager.h"
static NSString *const kEventCalendarIdentifierLocalKey = @"XT_CloudMeeting_EventCalendar_Identifier_Key";
static NSString *const kReminderCalendarIdentifierLocalKey = @"XT_CloudMeeting_ReminderCalendar_Identifier_Key";

static NSString *const kEventIdentifier = @"XT_Calendar_Event_Key";
static NSString *const kReminderIIdentifier = @"XT_Calendar_Reminder_Key";


@interface XTCalendarEventManager ()
@property (nonatomic,strong) EKEventStore *eventStore;
@property (nonatomic,strong) NSMutableArray <NSString *> *calendarItemIdentifierArray; // 存儲(chǔ)日歷唯一標(biāo)識(shí)符
@end

@implementation XTCalendarEventManager
DEF_SINGLETON(XTCalendarEventManager);

- (instancetype)init {
    self = [super init];
    if (self) {
        self.eventStore = [[EKEventStore alloc] init];
        self.calendarItemIdentifierArray = [[NSMutableArray alloc] init];
        [self crateCalendar];
    }
    return self;
}

- (void)crateCalendar {
    EKCalendar *eventCalendar = [self findCustomCalendar:EKEntityTypeEvent];
    if (!eventCalendar) {
        [self createCustomCalendarWithEntityType:EKEntityTypeEvent title:@"云會(huì)議" completion:nil];
    }
    
    EKCalendar *reminderCalendar = [self findCustomCalendar:EKEntityTypeReminder];
    if (!reminderCalendar) {
        [self createCustomCalendarWithEntityType:EKEntityTypeReminder title:@"云會(huì)議" completion:nil];
    }
    

}

/**
 *  創(chuàng)建日歷
 *
 *  @param entityType      日歷類型 EKEntityTypeEvent EKEntityTypeReminder
 *  @param title      日歷標(biāo)題
 *  @param completion 回調(diào)方法
 */
- (void)createCustomCalendarWithEntityType:(EKEntityType)entityType title:(NSString *)title completion:(CalendarCompletion)completion {
    // 創(chuàng)建日歷
    EKCalendar * customCalendar = [EKCalendar calendarForEntityType:entityType eventStore:self.eventStore];
    //  必須設(shè)置source觅丰,否則無(wú)法創(chuàng)建calendar
    customCalendar.source = [self getAvailableCalendarSource];
    customCalendar.title = title;
    
    NSError *error = nil;
    BOOL isSuccess = [self.eventStore saveCalendar:customCalendar commit:YES error:&error];
    
    if (!error) {
        if (![NSString isBlankString:customCalendar.calendarIdentifier]) {
            
            //存儲(chǔ)calendarIdentifier
            NSString *calendarIdfLocalKey = nil;
            if (entityType == EKEntityTypeEvent) {
                calendarIdfLocalKey = kEventCalendarIdentifierLocalKey;
            }else{
                calendarIdfLocalKey = kReminderCalendarIdentifierLocalKey;
            }
            [XTUserDefault setValue:customCalendar.calendarIdentifier forKey:calendarIdfLocalKey];
            
        }else{
            isSuccess = NO;
            error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:@{NSLocalizedDescriptionKey : @"eventIdentifier不存在"}];
        }
    }else{
        isSuccess = NO;
    }
    
    !completion?:completion(isSuccess,error);
    
}

#pragma mark - 日歷提醒 -
/**
 *  添加日歷提醒事項(xiàng)
 *
 *  @param eventIdentifierLocalKey  事件ID 本地存儲(chǔ)Key
 *  @param title      事件標(biāo)題
 *  @param startDate  開(kāi)始時(shí)間
 *  @param endDate    結(jié)束時(shí)間
 *  @param allDay     是否全天
 *  @param alarmArray 鬧鐘集合(傳nil饵溅,則沒(méi)有)
 *  @param notes      事件備注(傳nil,則沒(méi)有)
 *  @param url        事件url(傳nil妇萄,則沒(méi)有)
 *  @param completion 回調(diào)方法
 */
- (void)createCalendarEventWithEventIdentifierLocalKey:(NSString *)eventIdentifierLocalKey calendarTitle:(NSString *)title notes:(NSString * __nullable)notes startDate:(NSDate *)startDate endDate:(NSDate *)endDate allDay:(BOOL)allDay alarmArray:(NSArray <NSString *> * __nullable)alarmArray url:(NSURL * __nullable)url completion:(CalendarCompletion)completion {
    
    if (![self checkCaledarAuthority:EKEntityTypeEvent]) {
        NSError * authorityError = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:@{NSLocalizedDescriptionKey : @"未授權(quán)"}];
        !completion?:completion(NO,authorityError);
        return;
    }
    
    
    BOOL isSuccess = NO;
    EKEvent *event = [EKEvent eventWithEventStore:self.eventStore];
    event.title = title;
    event.allDay = allDay;
    event.notes = notes;
    event.URL = url?:[NSURL URLWithString:@"xtcloudmeeting://"];
    event.startDate = startDate;
    event.endDate = endDate;
    
    //添加鬧鐘提醒
    if (alarmArray && alarmArray.count > 0) {
        for (NSString *timeString in alarmArray) {
            [event addAlarm:[EKAlarm alarmWithRelativeOffset:[timeString integerValue]]];
        }
    }
    
    // 存儲(chǔ)到日歷源中
    EKCalendar *eventCalendar = [self findCustomCalendar:EKEntityTypeEvent];
    if (eventCalendar) {
        [event setCalendar:eventCalendar];
    }else{
        [event setCalendar:[self.eventStore defaultCalendarForNewEvents]];
        NSLog(@"XTCalendarEventManager save defaultCalendarForNewEvents");
    }
    
    // 保存日歷
    NSError *isError;
    isSuccess = [self.eventStore saveEvent:event span:EKSpanThisEvent commit:YES error:&isError];
    
    if (!isError) {
        if (![NSString isBlankString:event.eventIdentifier] && ![NSString isBlankString:eventIdentifierLocalKey]) {
            //存儲(chǔ)日歷ID
            [XTUserDefault setValue:event.eventIdentifier forKey:[self getCalendarItemLocalKey:eventIdentifierLocalKey type:EKEntityTypeEvent]];
        }else{
            isSuccess = NO;
            isError = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:@{NSLocalizedDescriptionKey : @"eventIdentifier不存在"}];
        }
    }
    
    !completion?:completion(isSuccess,isError);
    
    NSLog(@"XTCalendarEventManager createCalendarEventWithEventIdentifierLocalKey %@",eventIdentifierLocalKey);
    
}

/**
 *  查日歷事件
 *
 *  @param eventIdentifierLocalKey    事件ID(標(biāo)識(shí)符) 本地存儲(chǔ)Key
 */
- (EKEvent *)checkCalendarEventWithIdentifier:(NSString *)eventIdentifierLocalKey{
    
    if (![self checkCaledarAuthority:EKEntityTypeEvent]) {
        NSLog(@"XTCalendarEventManager checkCalendarEventWithIdentifier 未授權(quán)");
        return nil;
    }
    
    NSString *eIdentifier = [XTUserDefault getValueForKey:[self getCalendarItemLocalKey:eventIdentifierLocalKey type:EKEntityTypeEvent]];
    if (![NSString isBlankString:eIdentifier]) {
        EKEvent *event = [self.eventStore eventWithIdentifier:eIdentifier];
        return event;
    }
    return nil;
}

/**
 *  查日歷事件(可查詢一段時(shí)間內(nèi)的事件)
 *
 *  @param startDate  開(kāi)始時(shí)間
 *  @param endDate    結(jié)束時(shí)間
 *  @param modifytitle    標(biāo)題概说,為空則都要查詢
 */
- (NSArray *)checkCalendarEventWithStartDate:(NSDate *)startDate endDate:(NSDate *)endDate modifytitle:(NSString * __nullable)modifytitle {
    
    if (![self checkCaledarAuthority:EKEntityTypeEvent]) {
        NSLog(@"XTCalendarEventManager checkCalendarEventWithDate 未授權(quán)");
        return nil;
    }
    
    // 查詢到所有的日歷
    EKCalendar *eventCalendar = [self findCustomCalendar:EKEntityTypeEvent];
    
    if (!eventCalendar) {
        eventCalendar = [self.eventStore defaultCalendarForNewEvents];
    }
    
    NSPredicate *predicate = [self.eventStore predicateForEventsWithStartDate:startDate endDate:endDate calendars:@[eventCalendar]];
    
    // 獲取到范圍內(nèi)的所有事件
    NSArray *request = [self.eventStore eventsMatchingPredicate:predicate];
    // 按開(kāi)始事件進(jìn)行排序
    request = [request sortedArrayUsingSelector:@selector(compareStartDateWithEvent:)];
    
    if ([NSString isBlankString:modifytitle]) {
        return request;
    }else{
        NSMutableArray *onlyRequest = [NSMutableArray array];
        for (int i = 0; i < request.count; i++) {
            EKEvent *event = request[i];
            if (event.title && [event.title isEqualToString:modifytitle]) {
                [onlyRequest addObject:event];
            }
        }
        return onlyRequest;
    }
}


/**
 *  刪除日歷事件(刪除單個(gè))
 *
 *  @param eventIdentifierLocalKey    事件ID(標(biāo)識(shí)符) 本地存儲(chǔ)Key
 */
- (BOOL)deleteCalendarEventWithIdentifier:(NSString *)eventIdentifierLocalKey{
    
    if (![self checkCaledarAuthority:EKEntityTypeEvent]) {
        NSLog(@"XTCalendarEventManager deleteCalendarEventWithIdentifier 未授權(quán)");
        return NO;
    }
    
    BOOL isDelete = NO;
    
    NSString *eIdentifier = [XTUserDefault getValueForKey:[self getCalendarItemLocalKey:eventIdentifierLocalKey type:EKEntityTypeEvent]];
    EKEvent *event;
    if (![NSString isBlankString:eIdentifier]) {
        event = [self.eventStore eventWithIdentifier:eIdentifier];
        isDelete = [self.eventStore removeEvent:event span:EKSpanThisEvent error:nil];
    }
    NSLog(@"XTCalendarEventManager deleteCalendarEventWithIdentifier %@ %d",eventIdentifierLocalKey,isDelete);
    return isDelete;
}

/**
 *  刪除日歷事件(可刪除一段時(shí)間內(nèi)的事件)
 *
 *  @param startDate  開(kāi)始時(shí)間
 *  @param endDate    結(jié)束時(shí)間
 *  @param modifytitle    標(biāo)題,為空則都要?jiǎng)h除
 */
- (BOOL)deleteCalendarEventWithStartDate:(NSDate *)startDate endDate:(NSDate *)endDate modifytitle:(NSString * __nullable)modifytitle {
    
    if (![self checkCaledarAuthority:EKEntityTypeEvent]) {
        NSLog(@"XTCalendarEventManager deleteCalendarEventWithDate 未授權(quán)");
        return NO;
    }
    
    // 獲取到此事件
    NSArray *request = [self checkCalendarEventWithStartDate:startDate endDate:endDate modifytitle:modifytitle];
    
    for (int i = 0; i < request.count; i ++) {
        // 刪除這一條事件
        EKEvent *event = request[i];
        NSError*error =nil;
        // commit:NO:最后再一次性提交
        [self.eventStore removeEvent:event span:EKSpanThisEvent commit:NO error:&error];
    }
    //一次提交所有操作到事件庫(kù)
    NSError *errored = nil;
    BOOL commitSuccess= [self.eventStore commit:&errored];
    return commitSuccess;
}

/**
 *  修改日歷事件
 *
 *  @param eventIdentifierLocalKey 事件ID(標(biāo)識(shí)符) 本地key
 *  @param title      修改事件標(biāo)題
 *  @param startDate  修改開(kāi)始時(shí)間
 *  @param endDate    修改結(jié)束時(shí)間
 *  @param allDay     修改是否全天
 *  @param alarmArray 修改鬧鐘集合
 *  @param notes      修改事件備注
 *  @param url        修改事件url
 *  @param completion 回調(diào)方法
 */
- (void)modifyCalendarEventWithEventIdentifierLocalKey:(NSString *)eventIdentifierLocalKey calendarTitle:(NSString *)title notes:(NSString * __nullable)notes startDate:(NSDate *)startDate endDate:(NSDate *)endDate allDay:(BOOL)allDay alarmArray:(NSArray <NSString *> * __nullable)alarmArray url:(NSURL * __nullable)url completion:(CalendarCompletion)completion{
    
    if (![self checkCaledarAuthority:EKEntityTypeEvent]) {
        NSError * authorityError = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:@{NSLocalizedDescriptionKey : @"未授權(quán)"}];
        !completion?:completion(NO,authorityError);
        return;
    }
    
    // 獲取到此事件
    EKEvent *event = [self checkCalendarEventWithIdentifier:eventIdentifierLocalKey];
    if (event) {
        BOOL isDelete = [self deleteCalendarEventWithIdentifier:eventIdentifierLocalKey];
        NSLog(@"XTCalendarEventManager modify delete %d",isDelete);
        [self createCalendarEventWithEventIdentifierLocalKey:eventIdentifierLocalKey calendarTitle:title notes:notes startDate:startDate endDate:endDate allDay:allDay alarmArray:alarmArray url:url completion:completion];
    }else{
        // 沒(méi)有此條日歷
        [self createCalendarEventWithEventIdentifierLocalKey:eventIdentifierLocalKey calendarTitle:title notes:notes startDate:startDate endDate:endDate allDay:allDay alarmArray:alarmArray url:url completion:completion];
        
    }
}

#pragma mark - Reminder 提醒 -
/**
 *  添加Reminder提醒事項(xiàng)
 *
 *  @param reminderIdentifierLocalKey  事件ID 本地存儲(chǔ)Key
 *  @param title      事件標(biāo)題
 *  @param startDate  開(kāi)始時(shí)間
 *  @param dueDate    預(yù)計(jì)結(jié)束時(shí)間
 *  @param alarmDate 鬧鐘Date
 *  @param notes      事件備注(傳nil嚣伐,則沒(méi)有)
 *  @param url        事件url(傳nil糖赔,則沒(méi)有)
 *  @param completion 回調(diào)方法
 */
- (void)createCalendarReminderWithReminderIdentifierLocalKey:(NSString *)reminderIdentifierLocalKey calendarTitle:(NSString *)title notes:(NSString * __nullable)notes startDate:(NSDate *)startDate dueDate:(NSDate *)dueDate alarmDate:(NSDate *)alarmDate url:(NSURL * __nullable)url completion:(CalendarCompletion)completion {
    
    if (![self checkCaledarAuthority:EKEntityTypeReminder]) {
        NSError * authorityError = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:@{NSLocalizedDescriptionKey : @"未授權(quán)"}];
        !completion?:completion(NO,authorityError);
        return;
    }
    
    EKReminder *reminder = [EKReminder reminderWithEventStore:self.eventStore];
    reminder.title = title;
    reminder.notes = notes;
    reminder.URL = url?:[NSURL URLWithString:@"xtcloudmeeting://"];
    
    reminder.startDateComponents = [[NSCalendar currentCalendar] components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitHour|NSCalendarUnitMinute|NSCalendarUnitSecond fromDate:startDate];
    reminder.dueDateComponents = [[NSCalendar currentCalendar] components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitHour|NSCalendarUnitMinute|NSCalendarUnitSecond fromDate:dueDate];
    
    //添加鬧鐘提醒
    [reminder addAlarm:[EKAlarm alarmWithAbsoluteDate:alarmDate]];
    
    // 存儲(chǔ)到日歷源中
    EKCalendar *reminderCalendar = [self findCustomCalendar:EKEntityTypeReminder];
    if (reminderCalendar) {
        [reminder setCalendar:reminderCalendar];
    }else{
        [reminder setCalendar:self.eventStore.defaultCalendarForNewReminders];
    }
    
    NSError *error = nil;
    BOOL isSuccess = [self.eventStore saveReminder:reminder commit:YES error:&error];
    
    if (!error) {
        if (![NSString isBlankString:reminder.calendarItemIdentifier] && ![NSString isBlankString:reminderIdentifierLocalKey]) {
            [XTUserDefault setValue:reminder.calendarItemIdentifier forKey:[self getCalendarItemLocalKey:reminderIdentifierLocalKey type:EKEntityTypeReminder]];
        }else{
            isSuccess = NO;
            error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:@{NSLocalizedDescriptionKey : @"eventIdentifier不存在"}];
        }
        
    }else{
        isSuccess = NO;
    }
    
    !completion?:completion(isSuccess,error);
    
    NSLog(@"XTCalendarEventManager createCalendarReminderWithReminderIdentifierLocalKey %@",reminderIdentifierLocalKey);
    
}


/**
 *  查Reminder提醒項(xiàng)
 *
 *  @param reminderIdentifierLocalKey    事件ID(標(biāo)識(shí)符) 本地存儲(chǔ)Key
 */
- (EKReminder *)checkCalendarReminderWithIdentifier:(NSString *)reminderIdentifierLocalKey{
    
    if (![self checkCaledarAuthority:EKEntityTypeReminder]) {
        NSLog(@"XTCalendarEventManager checkCalendarReminderWithIdentifier 未授權(quán)");
        return nil;
    }
    NSString *eIdentifier = [XTUserDefault getValueForKey:[self getCalendarItemLocalKey:reminderIdentifierLocalKey type:EKEntityTypeReminder]];
    if (![NSString isBlankString:eIdentifier]) {
        EKCalendarItem *calendarItem = [self.eventStore calendarItemWithIdentifier:eIdentifier];
        if ([calendarItem isKindOfClass:[EKReminder class]]) {
            return (EKReminder *)calendarItem;
        }
        return nil;
    }
    return nil;
}

/**
 *  查詢Reminder提醒事項(xiàng) (一段時(shí)間)(以dueDate為參照)
 *
 *  @param startDate  開(kāi)始時(shí)間
 *  @param endDate    結(jié)束時(shí)間
 *  @param completion 回調(diào)方法
 */
- (void)checkCalendarIncompleteRemindersWithDueDateStarting:(nullable NSDate *)startDate endDate:(nullable NSDate *)endDate completion:(CalendarFetchDataCompletion)completion{
    
    if (![self checkCaledarAuthority:EKEntityTypeReminder]) {
        !completion?:completion(nil);
        return;
    }
    
    EKCalendar *reminderCalendar = [self findCustomCalendar:EKEntityTypeReminder];
    
    if (!reminderCalendar) {
        reminderCalendar = [self.eventStore defaultCalendarForNewReminders];
    }
    
    NSPredicate *predicate = [self.eventStore predicateForIncompleteRemindersWithDueDateStarting:startDate ending:endDate calendars:@[reminderCalendar]];
    
    [self.eventStore fetchRemindersMatchingPredicate:predicate completion:^(NSArray<EKReminder *> * _Nullable reminders) {
        !completion?:completion(reminders);
    }];
}



/**
 *  刪除Reminber(刪除單個(gè))
 *
 *  @param reminderIdentifierLocalKey    reminder ID(標(biāo)識(shí)符) 本地存儲(chǔ)Key
 */
- (BOOL)deleteCalendarReminderWithIdentifier:(NSString *)reminderIdentifierLocalKey {
    
    if (![self checkCaledarAuthority:EKEntityTypeReminder]) {
        NSLog(@"XTCalendarEventManager deleteCalendarReminderWithIdentifier 未授權(quán)");
        return NO;
    }
    
    BOOL isDelete = NO;
    EKReminder *reminder = [self checkCalendarReminderWithIdentifier:reminderIdentifierLocalKey];
    if (reminder) {
        isDelete = [self.eventStore removeReminder:reminder commit:YES error:nil];
    }
    NSLog(@"XTCalendarEventManager deleteCalendarReminderWithIdentifier %@ %d",reminderIdentifierLocalKey,isDelete);
    return isDelete;
}

/**
 *  刪除Reminder提醒事項(xiàng) (一段時(shí)間)(以dueDate為參照)
 *
 *  @param startDate  開(kāi)始時(shí)間
 *  @param endDate    結(jié)束時(shí)間
 *  @param completion 回調(diào)方法
 */

- (void)deleteCalendarIncompleteRemindersWithDueDateStarting:(nullable NSDate *)startDate endDate:(nullable NSDate *)endDate completion:(CalendarCompletion)completion{
    
    if (![self checkCaledarAuthority:EKEntityTypeReminder]) {
        NSError * authorityError = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:@{NSLocalizedDescriptionKey : @"未授權(quán)"}];
        !completion?:completion(NO,authorityError);
        return;
    }
    
    weakify_self
    [self checkCalendarIncompleteRemindersWithDueDateStarting:startDate endDate:endDate completion:^(NSArray *itemArray) {
        strongify_self
        for (int i=0; i<itemArray.count; i++) {
            EKReminder *tempReminder = (EKReminder *)[itemArray objectAtIndex:i];
            [self.eventStore removeReminder:tempReminder commit:NO error:nil];
        }
        NSError *error = nil;
        BOOL commitSucces = [self.eventStore commit:&error];
        !completion?:completion(commitSucces,error);
    }];
}

/**
 *  修改Reminder提醒事項(xiàng)
 *
 *  @param reminderIdentifierLocalKey  事件ID 本地存儲(chǔ)Key
 *  @param title      事件標(biāo)題
 *  @param startDate  開(kāi)始時(shí)間
 *  @param dueDate    預(yù)計(jì)結(jié)束時(shí)間
 *  @param alarmDate 鬧鐘Date
 *  @param notes      事件備注(傳nil拍棕,則沒(méi)有)
 *  @param url        事件url(傳nil穗慕,則沒(méi)有)
 *  @param completion 回調(diào)方法
 */
- (void)modifyCalendarReminderWithReminderIdentifierLocalKey:(NSString *)reminderIdentifierLocalKey calendarTitle:(NSString *)title notes:(NSString * __nullable)notes startDate:(NSDate *)startDate dueDate:(NSDate *)dueDate alarmDate:(NSDate *)alarmDate url:(NSURL * __nullable)url completion:(CalendarCompletion)completion {
    
    if (![self checkCaledarAuthority:EKEntityTypeReminder]) {
        NSError * authorityError = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:@{NSLocalizedDescriptionKey : @"未授權(quán)"}];
        !completion?:completion(NO,authorityError);
        return;
    }
    
    EKReminder *tempReminder = [self checkCalendarReminderWithIdentifier:reminderIdentifierLocalKey];
    if (tempReminder) {
        [self deleteCalendarReminderWithIdentifier:reminderIdentifierLocalKey];
        [self createCalendarReminderWithReminderIdentifierLocalKey:reminderIdentifierLocalKey calendarTitle:title notes:notes startDate:startDate dueDate:dueDate alarmDate:alarmDate url:url completion:completion];
    }else{
        [self createCalendarReminderWithReminderIdentifierLocalKey:reminderIdentifierLocalKey calendarTitle:title notes:notes startDate:startDate dueDate:dueDate alarmDate:alarmDate url:url completion:completion];
    }
    
}


#pragma mark - Pravite -
- (EKSource *)getAvailableCalendarSource {
    EKSource *localSource = nil;
    for (EKSource *source in self.eventStore.sources){
        if (source.sourceType == EKSourceTypeCalDAV && [source.title isEqualToString:@"iCloud"]){
            localSource = source;
            break;
        }
    }
    if (localSource == nil){
        for (EKSource *source in self.eventStore.sources){
            if (source.sourceType == EKSourceTypeLocal){
                localSource = source;
                break;
            }
        }
    }
    
    if (localSource == nil) {
        NSLog(@"XTCalendarEventManager getAvailableCalendarSource error");
    }
    
    return localSource;
}

- (EKCalendar *)findCustomCalendar:(EKEntityType)entityType {
    
    EKCalendar *customCalendar = nil;
    NSString *calendarIdentifer = nil;
    
    if (entityType == EKEntityTypeEvent) {
        calendarIdentifer = [XTUserDefault getValueForKey:kEventCalendarIdentifierLocalKey];
    }else{
        calendarIdentifer = [XTUserDefault getValueForKey:kReminderCalendarIdentifierLocalKey];
    }
    
    if (![NSString isBlankString:calendarIdentifer]) {
        NSArray *tempCalendarArray = [self.eventStore calendarsForEntityType:entityType];
        for (int i = 0 ; i < tempCalendarArray.count; i ++) {
            EKCalendar *temCalendar = tempCalendarArray[i];
            if ([temCalendar.calendarIdentifier isEqualToString:calendarIdentifer]) {
                customCalendar = temCalendar;
                break;
            }
        }
    }
    return customCalendar;

}

- (NSString *)getCalendarItemLocalKey:(NSString *)identifierString type:(EKEntityType)type {
    NSString *localKey = nil;
    if (type == EKEntityTypeEvent) {
        localKey = [NSString stringWithFormat:@"%@_%@",kEventIdentifier,identifierString];
    }else{
        localKey = [NSString stringWithFormat:@"%@_%@",kReminderIIdentifier,identifierString];
    }
    return localKey;
}

- (BOOL)checkCaledarAuthority:(EKEntityType)type {
    BOOL isAuthority = NO;
    XTAuthorityStatus status = XTAuthorityStatusNotDetermined;
    if (type == EKEntityTypeEvent) {
        status = [[XTAuthorityManager sharedInstance] checkCalendarAuthority];
    }else {
        status = [[XTAuthorityManager sharedInstance] checkReminderAuthority];
    }
    
    if (status == XTAuthorityStatusAuthorized) {
        isAuthority = YES;
    }
    return isAuthority;
    
}
@end
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市巡社,隨后出現(xiàn)的幾起案子基茵,更是在濱河造成了極大的恐慌奋构,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拱层,死亡現(xiàn)場(chǎng)離奇詭異弥臼,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)根灯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門径缅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)掺栅,“玉大人,你說(shuō)我怎么就攤上這事纳猪⊙跷裕” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵氏堤,是天一觀的道長(zhǎng)沙绝。 經(jīng)常有香客問(wèn)我,道長(zhǎng)鼠锈,這世上最難降的妖魔是什么闪檬? 我笑而不...
    開(kāi)封第一講書人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮购笆,結(jié)果婚禮上粗悯,老公的妹妹穿的比我還像新娘。我一直安慰自己为黎,他們只是感情好行您,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布剪廉。 她就那樣靜靜地躺著娃循,像睡著了一般斗蒋。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上泉沾,一...
    開(kāi)封第一講書人閱讀 51,624評(píng)論 1 305
  • 那天捞蚂,我揣著相機(jī)與錄音跷究,去河邊找鬼。 笑死丁存,一個(gè)胖子當(dāng)著我的面吹牛柴我,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播聋伦,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼嘉抓,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了卵佛?” 一聲冷哼從身側(cè)響起截汪,我...
    開(kāi)封第一講書人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤衙解,失蹤者是張志新(化名)和其女友劉穎焰枢,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體暑椰,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡一汽,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年召夹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了恕沫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖爬虱,靈堂內(nèi)的尸體忽然破棺而出跑筝,到底是詐尸還是另有隱情,我是刑警寧澤赞警,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布愧旦,位于F島的核電站,受9級(jí)特大地震影響旁瘫,放射性物質(zhì)發(fā)生泄漏酬凳。R本人自食惡果不足惜遭庶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一峦睡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧煎谍,春花似錦粱快、人聲如沸叔扼。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蓄坏。三九已至,卻和暖如春结蟋,著一層夾襖步出監(jiān)牢的瞬間嵌屎,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工植榕, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留尊残,地道東北人淤堵。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓粘勒,卻偏偏與公主長(zhǎng)得像庙睡,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子统台,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容