EKEvent is not added according to given EKRecurrenceRule

1.5k views Asked by At

I'm trying to add an event into calendar with recurrence rule RRULE:FREQ=YEARLY;BYMONTH=6,7;BYDAY=1TH

So according to this rule the event should be added yearly, each 1st thursday of june and july until expire date, which I've set in my project.

In my project, events are created but not according to the recurrence rule. With the following code the events added only on each 1st thursday of june. Why the events are not added on 1st thursday of each july also?

Here is .m file code

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    [self createEvent];
}

- (void)createEvent
{
    EKEventStore *eventStore = [[EKEventStore alloc] init];
    EKEvent *event = [EKEvent eventWithEventStore:eventStore];
    event.title = @"testRecurrenceRule";
    event.location = @"Dhaka";
    [event setCalendar:[eventStore defaultCalendarForNewEvents]];
    event.startDate = [self dateFromString:@"2013-06-18T21:00:00+06:00"];
    event.endDate = [self dateFromString:@"2013-06-18T22:00:00+06:00"];

    id recurrenceRule = [self recurrenceRuleForEvent];
    if(recurrenceRule != nil)
        [event addRecurrenceRule:recurrenceRule];

    if ([eventStore respondsToSelector:@selector(requestAccessToEntityType:completion:)])
    {
        // iOS 6 and later
        [eventStore requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error) {
            if (granted)
            {
                dispatch_async(dispatch_get_main_queue(), ^{
                    [self saveTheEvent:event eventStore:eventStore];
                    //[eventStore saveEvent:event span:EKSpanThisEvent error:error];
                });
            }
            else
            {
                dispatch_async(dispatch_get_main_queue(), ^{

                    //do nothing
                });
            }
        }];
    }
    else
    {
        [self saveTheEvent:event eventStore:eventStore];
    }

    textView.text = [NSString stringWithFormat:@"Event has been added with recurrence rule %@",recurrenceRule];
}

- (void)saveTheEvent:(EKEvent *)aEvent eventStore:(EKEventStore *)aStore
{
    [aStore saveEvent:aEvent span:EKSpanThisEvent error:NULL];
}

- (EKRecurrenceRule *)recurrenceRuleForEvent
{
    //just creating a recurrence rule for RRULE:FREQ=YEARLY;BYMONTH=6,7;BYDAY=1TH
    // setting the values directly for testing purpose.

    //FREQ=YEARLY
    EKRecurrenceFrequency recurrenceFrequency = EKRecurrenceFrequencyYearly;
    NSInteger recurrenceInterval = 1;                                             
    EKRecurrenceEnd *endRecurrence = nil;                                         
    NSMutableArray *monthsOfTheYearArray = [NSMutableArray array];               
    NSMutableArray *daysOfTheWeekArray = [NSMutableArray array];                
    NSMutableArray *daysOfTheMonthArray = [NSMutableArray array];               
    NSMutableArray *weeksOfTheYearArray = [NSMutableArray array];              
    NSMutableArray *daysOfTheYearArray = [NSMutableArray array];          
    NSMutableArray *setPositionsArray = [NSMutableArray array];         

    //BYMONTH=6,7
    [monthsOfTheYearArray addObject:[NSNumber numberWithInt:6]];
    [monthsOfTheYearArray addObject:[NSNumber numberWithInt:7]];

    //BYDAY=1TH
    [daysOfTheWeekArray addObject:[EKRecurrenceDayOfWeek dayOfWeek:5 weekNumber:1]];

    endRecurrence = [EKRecurrenceEnd recurrenceEndWithEndDate:[self dateFromString:@"2018-12-15T22:30+06:00"]];

    EKRecurrenceRule *recurrence = [[EKRecurrenceRule alloc] initRecurrenceWithFrequency:recurrenceFrequency
                                                                                interval:recurrenceInterval
                                                                           daysOfTheWeek:daysOfTheWeekArray
                                                                          daysOfTheMonth:daysOfTheMonthArray
                                                                         monthsOfTheYear:monthsOfTheYearArray
                                                                          weeksOfTheYear:weeksOfTheYearArray
                                                                           daysOfTheYear:daysOfTheYearArray
                                                                            setPositions:setPositionsArray
                                                                                     end:endRecurrence];
    return recurrence;
}

- (NSDate *)dateFromString:(NSString *)string
{
    //check if the date string in null
    if ([string length] == 0)
        return nil;

    NSString *dateString = nil;
    NSString *modifiedString = nil;
    BOOL secSpotMissing = false;

    NSRange range = [string rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"T"]];
    if (range.location != NSNotFound)
    {
        dateString = [string substringFromIndex:range.location];

        range = [dateString rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"+-Z"]];
        if (range.location != NSNotFound)
        {
            //seperate the time portion of date string and checking second field is missing or not. like is it HH:mm or HH:mm:ss?
            if ([[[dateString substringToIndex:range.location] componentsSeparatedByString:@":"] count] < 3)
                secSpotMissing = true;

            //seperate the time zone portion and checking is there any extra ':' on it. It should like -0600 not -06:00. If it has that extra ':', just replacing it here.
            dateString = [dateString substringFromIndex:range.location];
            if([dateString hasSuffix:@"Z"])
                modifiedString = [dateString stringByReplacingOccurrencesOfString:@"Z" withString:@"+0000"];
            else
                modifiedString = [dateString stringByReplacingOccurrencesOfString:@":" withString:@""];
            string = [string stringByReplacingOccurrencesOfString:dateString withString:modifiedString];
        }
    }
    else
        return nil;

    // converting the date string according to it's format.
    NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init];
    if (secSpotMissing)
        [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mmZZZ"];
    else
        [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZZZ"];
    return [dateFormatter dateFromString:string];
}

Can somebody please help me regarding this?

2

There are 2 answers

1
BSharer App - Share Books On

This seems a repeat of another question. Basically, according to "BYDAY" rule, the 1st for YEARLY frequency means the first week in the year - instead of 1st week each month.

@Shuvo, I did not read rfc. But here is Apple document EKRecurrenceDayOfWeek.

The EKRecurrenceDayOfWeek class represents a day of the week for use with an EKRecurrenceRule object. A day of the week can optionally have a week number, indicating a specific day in the recurrence rule’s frequency. For example, a day of the week with a day value of Tuesday and a week number of 2 would represent the second Tuesday of every month in a monthly recurrence rule, and the second Tuesday of every year in a yearly recurrence rule.

When you say "1st Thursday", that is correct - except in the context of yearly, it is 1st thursday of the year.

0
alex-i On

The bug was confirmed by Apple, at least until iOS 7.1.3 (which is the latest available version at this moment).