iOS EKEvent Store recreating iCloud calendars in a loop, won't save local.

谁说我不能喝 提交于 2019-11-29 07:42:54
Alex75

I'm not sure why you get this behavior, but I think due to the fact that having disabled iCloud, the system cannot make queries on it and then queue creation requests that are resolved once you wake iCloud (but I'm assuming).

Anyway, the first solution that comes to my mind is to check if iCloud is active or not in this way

EKSource *defaultSource = [eventStore defaultCalendarForNewEvents].source;

if (defaultSource.sourceType == EKSourceTypeCalDAV)
    NSLog(@"iCloud Enable");
else
    NSLog(@"iCloud Disable");

this is done you can save your events to the default source properly and then keep the 2 calendars (the local one and the cloud one) synchronized with each other ...

Reactivation of iCloud will still be prompted to add all local calendars.

see also the second answer here Accessing programmatically created calendar on iOS device (which is where I got the idea ;) )

I hope I was helpful.

EDIT: Maybe is not necessary to create a second calendar... Try to change the source of the calendar from EKSourceTypeCalDAV to EKSourceTypeLocal ... don't forget to save calendar with commit "YES"

EDIT2: Ok just tested ...

substitute this:

} else { // Create Calendar

    EKSource *theSource = nil;

    for (EKSource* src in eventStore.sources) {
        if ([src.title isEqualToString:@"iCloud"]) {
            theSource = src;
            break;
        }
        if (src.sourceType == EKSourceTypeLocal && theSource==nil) {
            theSource = src;
            break;
        }
    }

    [self setupCalendarWithSource:theSource withEvent:event];
}

with this ...

} else { // Create Calendar

    EKSource *theSource = [eventStore defaultCalendarForNewEvents].source;

    [self setupCalendarWithSource:theSource withEvent:event];
}

this way u will create the calendar in the right source (local if user deactivate iCloud and CalDAV otherwise)

then:

1) when user choose to deactivate iCloud should leave calendars on iphone (and not delete) so u have the cloud calendar in the local source

2) when user choose to activate iCloud will merge his local calendars with the cloud and there u go!!

i hope this help

If you want a bullet proof way to find iCloud Calendar and revert back to local Calendar if the iCloud is disabled, use the code below. I have included some comments which may help:

for (EKSource *source in eventStore.sources) {  //Check for iCloud
    if (source.sourceType == EKSourceTypeCalDAV && [source.title isEqualToString:@"iCloud"]) {

        NSLog(@"Found iCloud Service.");  //Found iCloud

        if([source calendarsForEntityType:EKEntityTypeEvent].count>0){     //Check to see if Calendar is enabled on iCloud

            NSLog(@"iCloud Calendar is Enabled."); //Calendar is Enabled

            if([self saveEventCalendarWithSource:source]){
                return YES;
            }

        }else{

            NSLog(@"iCloud Calendar is Disabled."); //Calendar is Disabled

        }
    }
}


//If we are here it means that we did not find iCloud Source with iCloud Name. Now trying any CalDAV type to see if we can find it

for (EKSource *source in self.reminderStore.sources) {  //Check for iCloud
    if (source.sourceType == EKSourceTypeCalDAV) {

        [self logData:@"Trying to save calendar in EKSourceTypeCalDAV Service."];

        if([self saveEventCalendarWithSource:source]){
            return YES;
        }

    }
}

//If we are here it means that we did not find iCloud and that means iCloud is not turned on. Use Local service now.

for (EKSource *source in self.reminderStore.sources) {  //Look for Local Source
    if (source.sourceType == EKSourceTypeLocal){  //Found Local Source
        NSLog(@"Found Local Source.");

        if([self saveEventCalendarWithSource:source]){
            return YES;
        }

    }
}

Here's the code to save Calendar:

- (Boolean) saveEventCalendarWithSource:(EKSource *)source{

EKCalendar *Calendar = nil;

MyCalendar = [EKCalendar calendarForEntityType:EKEntityTypeEvent eventStore:eventStore];

MyCalendar.title = @"XXX";
MyCalendar.CGColor = [UIColor blueColor].CGColor;

MyCalendar.source = source;

NSError *err;
if([eventStore saveCalendar:MyCalendar commit:YES error:&err]){

    if(MyCalendar.calendarIdentifier == nil){

        NSLog(@"Could not save Calendar: %@",err);

        return FALSE;
    }

    NSLog(@"Calendar Created. Here's the identifier %@",[MyCalendar calendarIdentifier]);

    return TRUE;
}

NSLog(@"Could not create calendar! Reason:%@",err.description);

return FALSE;

}

Your post was a big help, i was struggling witt the exact same bug. Thank you !

I just made a small modifications, as the solution of using the defaultCalendarForNewEvents' source was not working in every situation : some source won't let you create new calendars in them.

I just check the number of calendars in my icloud source. If the count is zero, then the calendar sync is off, and I take the local source :

EKSource* localSource = nil;
EKSource* iCloudSource = nil;
for (EKSource* source in _eventStore.sources){
    if (source.sourceType == EKSourceTypeLocal){
        localSource = source;
    }else if(source.sourceType == EKSourceTypeCalDAV && [source.title isEqualToString:@"iCloud"]){
        iCloudSource = source;
    }
}

if (iCloudSource && [iCloudSource.calendars count] != 0) {
    calendar.source = iCloudSource;
}else{
    calendar.source = localSource;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!