Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I'm having a strange issue EKEventStore, iCloud and local calendars. If iCloud is enabled the Calendar is created and events are saved into the calendar as you would expect. if iCloud is turned off and you try to save an event nothing happens, however the device continues to create iCloud calendars in a loop every 3-5 seconds until iCloud is turned back on and then all of those calendars flood into iCloud as duplicates. I'm using nearly the exact code that has been referenced here on SO many times as well as in Apples Docs. I'm completely stumped as to why it's not working and there seems to be very little documentation on EKEventStore in general.

//••••••••••••••••••••••••••••••••••••••••••••••• #pragma mark – Save Event //•••••••••••••••••••••••••••••••••••••••••••••••

-(void)saveEventWithDate:(NSDate *)startDate endDate:(NSDate *)endDate
{
    AppData *theData = [self theAppData];

    if([self checkIsDeviceVersionHigherThanRequiredVersion:@"6.0"]) {
        [eventStore requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error) { // iOS 6 Support

            if (granted){
                NSLog(@"Access Granted");
            } else {
                NSLog(@"Access Not Granted");
            }

        }];
    }

    EKEvent *event  = [EKEvent eventWithEventStore:eventStore];

    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

    if ([eventStore calendarWithIdentifier:[defaults objectForKey:@"My Calendar"]] != nil) // Calendar Existed
    {
        event.calendar  = [eventStore calendarWithIdentifier:[defaults objectForKey:@"My Calendar"]];
        NSLog(@"Calendar Existed");

    } 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];
    }

    NSLog(@"Type of Event:%@",typeOfEvent);

    if ([typeOfEvent isEqualToString:@"Hello"]) {
        event.title     = [NSString stringWithFormat:@"%@ Hello",[theData.hello_info objectForKey:@"customer_name"]];
        event.location  = [NSString stringWithFormat:@"Phone #%@",[theData.hello_info objectForKey:@"customer_phone_number"]];
        event.notes     = [NSString stringWithFormat:@"Hello Issue: %@",[theData.hello_info objectForKey:@"hello_issue"]];
        NSLog(@"Hello");
    }

    event.startDate = startDate;
    event.endDate   = endDate;
    event.allDay    = NO;
    EKAlarm *alarm = [EKAlarm alarmWithRelativeOffset:-1800]; // Half Hour Before
    event.alarms = [NSArray arrayWithObject:alarm];

    [eventStore saveEvent:event span:EKSpanThisEvent error:nil];

    SAFE_PERFORM_WITH_ARG(_delegate, @selector(wasScheduled), nil);
}

-(void)setupCalendarWithSource:(EKSource *)theSource withEvent:(EKEvent *)event {

    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

    EKCalendar *cal;
    cal = [EKCalendar calendarWithEventStore:eventStore];
    cal.title = @"My Appointments";
    cal.source = theSource;
    [eventStore saveCalendar:cal commit:YES error:nil];
    NSLog(@"cal id = %@", cal.calendarIdentifier);
    NSString *calendar_id = cal.calendarIdentifier;
    [defaults setObject:calendar_id forKey:@"My Calendar"];
    event.calendar  = cal;
}
like image 300
KDM Avatar asked Dec 13 '12 21:12

KDM


2 Answers

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

like image 186
Alex75 Avatar answered Oct 21 '22 11:10

Alex75


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;

}

like image 31
Bms270 Avatar answered Oct 21 '22 12:10

Bms270