EKEvent is not following EKRecurrenceRule - ios

I am adding an event in iOS calendar using EventKit. The event is recurring event. I am setting recurring rules for the event programmatically. Event is getting added in calendar successfully but the dates shown on calendar are not same as I set them.
Event details
Recurring rules.
After event is successfully added to calendar this is what I get in calendar entry
My event is being show for 2017 but I didn't set the end date to 2017 it was 2016.
I tried adding the event with iOs calendar and got the same result.
Below is the code I am using.
/*!
* This method, called added event to the calendar
*/
- (void)addEventToCalendar
{
#try {
/* Event added to device calendar */
[SINGLETON.eventStore requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error) {
if (granted) {
[SINGLETON.event_Dateformat setDateFormat:[self dateFormatForSelectedLanguage]];
EKEvent *event = [EKEvent eventWithEventStore:SINGLETON.eventStore];
event.title = _event.eventTitle;
event.startDate = [SINGLETON.event_Dateformat dateFromString:eventInfo.eventActualStartDate];
event.endDate = [SINGLETON.event_Dateformat dateFromString:eventInfo.eventActualEndDate];
event.notes = flattenHTML(eventInfo.eventDescription);
// Get notification 2 hours before the event start time.
[event addAlarm:[EKAlarm alarmWithRelativeOffset:-(2*60*60)]];
[event setCalendar:[SINGLETON.eventStore defaultCalendarForNewEvents]];
/* Here opening Event edit view controller */
EKEventEditViewController *controller = [[EKEventEditViewController alloc] init];
controller.event = event;
controller.eventStore = SINGLETON.eventStore;
controller.editViewDelegate = self;
// Event is recurring mark it is repeat in calendar.
// List of days on which a particular event occurs.
NSMutableArray <EKRecurrenceDayOfWeek *> *daysOfTheWeek = [[NSMutableArray alloc] init];
if (_event.eventRecurrence &&
! [_event.eventDays containsString:#"N/A"]) {
for (NSString *dayName in [_event.eventDays componentsSeparatedByString:#","]) {
EKRecurrenceDayOfWeek *aDay = [EKRecurrenceDayOfWeek dayOfWeek:[self weekDayForDayName:dayName]];
[daysOfTheWeek addObject:aDay];
}
}
// When to stop reminding user for the event.
EKRecurrenceEnd *endDate = [EKRecurrenceEnd recurrenceEndWithEndDate:event.endDate];
EKRecurrenceRule *repeatRule = [[EKRecurrenceRule alloc] initRecurrenceWithFrequency:[self recurrenceFrequencey:_event.eventType] interval:1 daysOfTheWeek:daysOfTheWeek daysOfTheMonth:nil monthsOfTheYear:nil weeksOfTheYear:nil daysOfTheYear:nil setPositions:nil end:endDate];
event.allDay = NO;
event.recurrenceRules = #[repeatRule];
//[event addRecurrenceRule:repeatRule];
dispatch_async(dispatch_get_main_queue(), ^{
//NSError *erroOnSave = nil;
// [SINGLETON.eventStore saveEvent:event span:EKSpanFutureEvents commit:YES error:&erroOnSave];
[MBProgressHUD hideAllHUDsForView:self.view animated:YES];
[self presentViewController:controller animated:YES completion:nil];
});
}else{
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideAllHUDsForView:self.view animated:YES];
[SINGLETON alertViewTitle:#""
message:NSLocalizedString(#"Calendar setting alert", nil)
cancelTitle:NSLocalizedString(#"OK", nil)
doneTitle:nil
tag:0];
});
}
}];
}
#catch (NSException *exception) {
DLog(#"%#",exception);
}
}
Below is the event details I get in debugger:
EKEvent <0x7f94b1f19d00>
{
EKEvent <0x7f94b1f19d00>
{ title = TBW ;
location = ;
calendar = EKCalendar <0x7f94b439d280> {title = Calendar; type = Local; allowsModify = YES; color = #1BADF8;};
alarms = (
"EKAlarm <0x7f94b4352fe0> {triggerInterval = -7200.000000}"
);
URL = (null);
lastModified = 2016-04-25 05:18:27 +0000;
startTimeZone = Asia/Kolkata (GMT+5:30) offset 19800;
startTimeZone = Asia/Kolkata (GMT+5:30) offset 19800
};
location = ;
structuredLocation = (null);
startDate = 2015-09-20 18:30:00 +0000;
endDate = 2016-05-31 18:30:00 +0000;
allDay = 0;
floating = 0;
recurrence = EKRecurrenceRule <0x7f94b43e7110> RRULE FREQ=WEEKLY;INTERVAL=1;UNTIL=20160531T183000Z;BYDAY=MO,TH;
attendees = (null);
travelTime = (null);
startLocation = (null);
};
Please suggest what I am using wrong or I am missing something.

I have found a solution of this question with help of this answer
iOS calendar is behaving strangly
I was setting the Ends parameter in wrong way. It should be on same date as Starts. Time should be different.
Now my event screen looks like this

Related

EKEvent does not save EKAlarm but no errors on some devices only

I am creating an EKEvent with an EKAlarm as demonstrated in the code below. In most cases, this works perfectly.
However, on a limited number of devices, the alarm does not get set. The event is created in the calendar, but the alarm is missing.
EKEvent* event = [EKEvent eventWithEventStore:eventStore];
event.title = #"Event Title";
event.allDay = YES;
event.notes = #"Event Notes";
event.calendar = calendar ? calendar : eventStore.defaultCalendarForNewEvents;
event.startDate = date;
event.endDate = date;
EKAlarm *alarm = [EKAlarm alarmWithAbsoluteDate:reminderDate];
[event addAlarm:alarm];
NSError* error = nil;
BOOL success = [eventStore saveEvent:event span:EKSpanThisEvent commit:TRUE error:&error];
At the point, success is YES and error is nil. Additionally, the event object has an array of alarms which contains the alarm object. This becomes a problem if I try to remove that alarm and save the event (app crashes because the alarm doesn't actually exist).
I'm not able to determine what is unique about the affected devices. It occurs every time on the following devices:
iPhone 6 with iOS 8.1.1
iPhone 6 with iOS 8.4
iPhone 5 with iOS 8.4
And the following do not have the problem:
iPhone 6 with iOS 8.4
iPhone 6 with iOS 8.3
iPhone 6 with iOS 8.0.2
iPhone simulator with iOS 8.1 & iOS 8.4
iPhone 5 with iOS 9
All of the above devices are different physical devices. So it doesn't seem to be any particular device model or iOS version.
I have also tried on different calendars, including local calendars and synchronized (e.g. Exchange, Google) calendars. On the affected devices, the issue happens on all calendars.
I have also tried setting / not setting a Time Zone Override, and the Default Alert Times (Settings > Mail, Contacts, Calendars).
Has anyone experienced this issue?
UPDATE:
I have built a test app to isolate the issue from anything else that may be going on inside the original app. It works perfectly on most devices, but the issue still exists on affected devices.
Here is what the EKEvent object looks like immediately after [eventStore saveEvent:event span:EKSpanThisEvent commit:TRUE error:&error]. Note that this call returned YES and error is nil.
EKEvent Object: EKEvent <0x1742e1e00>
{
EKEvent <0x1742e1e00>
{ title = Event Title;
location = ;
calendar = EKCalendar <0x1740ab4c0> {title = Calendar; type = Exchange; allowsModify = YES; color = #CC73E1;};
alarms = (
"EKAlarm <0x170085190> {triggerDate = 2015-09-02 23:21:53 +0000}"
);
URL = (null);
lastModified = 2015-08-25 23:21:53 +0000;
startTimeZone = (null);
startTimeZone = (null)
};
location = ;
structuredLocation = (null);
startDate = 2015-09-04 14:00:00 +0000;
endDate = 2015-09-05 13:59:59 +0000;
allDay = 1;
floating = 1;
recurrence = (null);
attendees = (null);
travelTime = (null);
startLocation = (null);
};
Then I call [eventStore reset] and it looks like this:
EKEvent Object: EKEvent <0x1702e2600>
{
EKEvent <0x1702e2600>
{ title = Event Title;
location = ;
calendar = EKCalendar <0x1740ad200> {title = Calendar; type = Exchange; allowsModify = YES; color = #CC73E1;};
alarms = (null);
URL = (null);
lastModified = 2015-08-25 23:21:53 +0000;
startTimeZone = (null);
startTimeZone = (null)
};
location = ;
structuredLocation = (null);
startDate = 2015-09-04 14:00:00 +0000;
endDate = 2015-09-05 13:59:59 +0000;
allDay = 1;
floating = 1;
recurrence = (null);
attendees = (null);
travelTime = (null);
startLocation = (null);
};
UPDATE #2
I have done some more testing and have been able to reproduce the issue consistently on an Exchange synchronised calendar. I had ruled this out previously as I had asked a client to try selecting a non-Exchange calendar and they reported the issue still occurred.
From what I can tell, this seems to be an iOS bug. I was able to establish that if I add an EKAlarm after saving the EKEvent then save it again, the alarm is successfully added and appears attached to the event in the Calendar app.
I was therefore able to workaround the issue by resetting the EKEventStore after saving, reloading the EKEvent, and if the EKAlarm is missing, add it and save again.
if (!error) {
[eventStore reset];
event = [eventStore eventWithIdentifier:event.eventIdentifier];
if (event.alarms.count < 1) {
EKAlarm *alarm = [EKAlarm alarmWithAbsoluteDate:reminderDate];
[event addAlarm:alarm];
[eventStore saveEvent:event span:EKSpanThisEvent commit:TRUE error:&error];
}
}
My solution:
NSCalendarsUsageDescription and NSRemindersUsageDescription must be present in the Info.plist
EKEventStore requestAccessToEntityType:completion: has to be called with EKEntityTypeEvent and EKEntityTypeReminder
Check EKAlarm, rewrite if necessary:
event.alarms = alarms;
NSArray *alarms2 = event.alarms;
EKAlarm *alarm2 = [alarms2 objectAtIndex:0];
NSDate *alarmDate2 = [alarm2 absoluteDate];
NSError *error = nil;
BOOL result = [eventStore saveEvent:event span:EKSpanThisEvent commit:YES error:&error];
if (result)
{
[event refresh];
NSArray *alarms3 = event.alarms;
EKAlarm *alarm3 = [alarms3 objectAtIndex:0];
NSDate *alarmDate3 = [alarm3 absoluteDate];
if ((alarmDate3 == nil) || ![alarmDate2 isEqualToDate:alarmDate3])
{
[eventStore refreshSourcesIfNecessary];
event = [eventStore eventWithIdentifier:event.eventIdentifier];
[event addAlarm:[EKAlarm alarmWithAbsoluteDate:alarmDate2]];
result = [eventStore saveEvent:event span:EKSpanThisEvent commit:YES error:&error];
}
}

How to set reminder using my app?

I have created custom calendar with the help of MBCalender kit and I have add events for some specific dates now, how can I create reminder for those specific dates?
By using EKReminder
- (void)createReminder:(NSDate*) objDate {
EKReminder *reminder = [EKReminder
reminderWithEventStore:objEventStore];
// objEventStore is the object of EKEventStore if you create that global that will be good
reminder.title = #"title";
reminder.notes = #"note";
reminder.calendar = [objAppDelegate.eventStore defaultCalendarForNewReminders];
EKAlarm *alarm = [EKAlarm alarmWithAbsoluteDate:objDate];
[reminder addAlarm:alarm];
NSError *error = nil;
BOOL status = [objAppDelegate.eventStore saveReminder:reminder commit:YES error:&error];
if (status == NO) {
_strUDID = nil;
}
else {
_strUDID = [NSMutableString stringWithFormat:#"%#",reminder.calendarItemIdentifier];
}
}

Setting Reminder with a dueDate with Recurrence Rule iOS

Question: How do I properly set my reminder due date since I have a recurrence rule?
Here is what the reminder object looks like:
EKReminder <0x1700cf490> {title = Dickens's CANINE GOLD WELLNESS doses[1.00]; **dueDate = (null)**; **completionDate = (null)**; priority = 0; calendarItemIdentifier = D1D99FEA-2BFA-4DB1-9D86-7FB26246B50A; alarms = (
"EKAlarm <0x1780a9420> {triggerInterval = -79200.000000}"
)}
The error I am getting is:
Reminder Error=[A repeating reminder must have a due date.]
You can see in the code that I am fooling around with NSDateComponents as a solution since startDateComponents which I just set the month/day/year and local timezone of the reminder which will produce an all day reminder, which in this case is fine. I will probably move the date components and setting of the due date inside the recurrence section when it is done.
Here is my code:
-(void)setReminders:(NSString *)reminderText
andDate:(NSString *)reminderdate
andPetName:(NSString*)petName
andDose:(NSNumber *)dose {
EKEventStore *store = [[EKEventStore alloc] init];
NSDate * reminderNewDate = [self getDateFromString:reminderdate];
petName = [ConfigOps readProperty:kConfigOpsPetKey];
NSString *reminderTitle = [NSString stringWithFormat:#"%#'s %#", petName, reminderText];
NSUInteger doseCount = 0;
if ([dose integerValue] != 0 || dose != nil) {
doseCount = [dose integerValue];
}
else{
doseCount = 0;//NOTE: looks like purchases will have doses not reminders so set to 0 for now.
}
[store requestAccessToEntityType:EKEntityTypeReminder completion:^(BOOL granted, NSError *error) {
// access code here
EKReminder *new_reminder = [EKReminder reminderWithEventStore:store];
new_reminder.title = reminderTitle;
new_reminder.calendar = store.defaultCalendarForNewEvents;
//get the date components
NSDateComponents *comp = [[NSDateComponents alloc]init];
NSCalendar *gregorian = [[NSCalendar alloc]
initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *weekdayComponents =
[gregorian components:(NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit |
NSWeekdayCalendarUnit) fromDate:reminderNewDate];
NSInteger day = [weekdayComponents day];
NSInteger month = [weekdayComponents month];
//NSInteger weekday = [weekdayComponents weekday];//future reference
NSInteger year = [weekdayComponents yearForWeekOfYear];
//Month is dose+month = end of reccurence
month = month + doseCount;
[comp setYear:year];
[comp setMonth:month];
[comp setDay:day];
NSDate *date = [gregorian dateFromComponents:comp];
NSTimeZone *myNSTimeZone = gregorian.timeZone;
NSDateComponents *start = new_reminder.startDateComponents;
start.timeZone = myNSTimeZone;
start.month = [weekdayComponents month];
start.day = [weekdayComponents day];
start.year = [weekdayComponents yearForWeekOfYear];
new_reminder.startDateComponents = start;
new_reminder.dueDateComponents = start;
new_reminder.completed = NO;
//Create alarm 22 hours before
double alarmAmountInSeconds = 60.0*60.0*22.0;
EKAlarm *alarm = [EKAlarm alarmWithRelativeOffset:(-1.0*alarmAmountInSeconds)];
[new_reminder addAlarm:alarm];
//new_reminder.alarms = [NSArray arrayWithObject:alarm];
//create nice text for note.
//Hey there! petName needs remindertext from your friendly clinic, clinicName!
new_reminder.notes = reminderText;
if (doseCount != 0) {
EKRecurrenceRule *recurranceRule = [[EKRecurrenceRule alloc] initRecurrenceWithFrequency:EKRecurrenceFrequencyMonthly
interval:1
end:[EKRecurrenceEnd recurrenceEndWithOccurrenceCount:doseCount]
];
new_reminder.calendar = [store defaultCalendarForNewReminders];
[new_reminder addRecurrenceRule:recurranceRule];
}
NSError *er;
//EKEventEditViewController
BOOL success = [store saveReminder:new_reminder commit:YES error:&er];
if (success) {
// Handle here
NSString *alertMessage = [NSString stringWithFormat:#"Reminder Created for\n%#", reminderTitle];
NSString *alertTitle = #"Please check your Reminders";
UIAlertView *alertR = [[UIAlertView alloc]initWithTitle: alertTitle
message: alertMessage
delegate: self
cancelButtonTitle:nil
otherButtonTitles:#"OK",nil];
[alertR show];
}
else{
//log error
NSLog(#" Reminder Error=[%#]", [er localizedDescription]);
//log to error table in database &inform Flurry?
}
}];
}
The method works if there is no recurrence set since it doesn't require a start date/due date.
After some fixing of the date (I found was returning nil), I found that I have to set and end recurrence rule when adding a dose amount.
Here is the code which gets rid of the error (which is pretty funny of Apple to have - kudos Apple!).
if (doseCount != 0) {
EKRecurrenceRule *recurranceRule = [[EKRecurrenceRule alloc] initRecurrenceWithFrequency:EKRecurrenceFrequencyMonthly
interval:1
end:[EKRecurrenceEnd recurrenceEndWithOccurrenceCount:doseCount]
];
new_reminder.calendar = [store defaultCalendarForNewReminders];
//FIX for : recuurence end - Reminder Error = [A repeating reminder must have a due date.]
EKRecurrenceEnd *endRec = [EKRecurrenceEnd recurrenceEndWithEndDate:date];
EKRecurrenceRule *recur = [[EKRecurrenceRule alloc]initRecurrenceWithFrequency:EKRecurrenceFrequencyDaily interval: 1 end:endRec];
unsigned unitFlags= NSYearCalendarUnit|NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit |NSMinuteCalendarUnit|NSSecondCalendarUnit|NSTimeZoneCalendarUnit;
NSDateComponents *dailyComponents=[gregorian components:unitFlags fromDate:date];
[new_reminder setDueDateComponents:dailyComponents];
[new_reminder addRecurrenceRule:recur];
//add it.
[new_reminder addRecurrenceRule:recurranceRule];
}
Hope this helps someone get through this.
For those looking for Swift version:
func editReminder(r: EKReminder) -> Bool {
if(r.recurrenceRules.count > 0 && r.dueDateComponents == nil) {
let startDate = NSDate()
let dueDate: NSDate = NSDate(timeIntervalSinceNow: 31536000) // 1 year from now
let gregorian = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)
let unitFlags = NSCalendarUnit(UInt.max)
r.dueDateComponents = gregorian?.components(unitFlags, fromDate: dueDate)
}
var error: NSError?
return eventStore.saveReminder(r, commit: true, error: &error)
}

EKEvent adds same events to calendar

I have been testing this code to add a calendar to IOS Cal App, and also add some events to that specific calendar in the app.
I have an Ipad with IOS7, iCloud disabled.
First problem is when I create calendar I can't see newly created calendar in iPad's iCal App,Isn't that app supposed to show all calendars even if they are not added to iCloud?
second problem is my code keep adding same events every time I call that code.
Lets say I call the add function and it adds 3 events successfully firs time, next time when call that function with same events it supposed to skip adding but it adds that 3 events to calendar again now I have duplicate events, and so on...
-(void) initCalendar:(NSDictionary *)dataDict
{
self.eventStore = [[EKEventStore alloc] init];
if([self.eventStore respondsToSelector:#selector(requestAccessToEntityType:completion:)]) {
// iOS 6 and later
[self.eventStore requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error) {
if (granted){
//---- codes here when user allow your app to access theirs' calendar.
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
self.isCalendarAccepted=YES;
NSLog(#"self.isCalendarAccepted=YES;");
if ([defaults objectForKey:#"Calendar"] == nil) // Create Calendar if Needed
{
EKSource *theSource = nil;
for (EKSource *source in self.eventStore.sources) {
if (source.sourceType == EKSourceTypeCalDAV && [source.title isEqualToString:#"iCloud"]) {
theSource = source;
NSLog(#"iCloud Store Source");
break;
} else {
for (EKSource *source in self.eventStore.sources) {
if (source.sourceType == EKSourceTypeLocal) {
theSource = source;
NSLog(#"ios Local Store Source");
break;
}
}
}
}
//EKCalendar *calendar = [EKCalendar calendarWithEventStore:self.eventStore];
EKCalendar *calendar =[EKCalendar calendarForEntityType:EKEntityTypeEvent eventStore:self.eventStore];
calendar.title = #"My App Name";
if (theSource) {
calendar.source = theSource;
} else {
NSLog(#"Error: Local source not available");
return;
}
NSError *errorCalendar = nil;
BOOL result = [self.eventStore saveCalendar:calendar commit:YES error:&errorCalendar];
if (result) {
NSLog(#"Saved calendar to event store.");
[[NSUserDefaults standardUserDefaults] setObject:calendar.calendarIdentifier forKey:#"Calendar"];
[[NSUserDefaults standardUserDefaults] synchronize];
} else {
NSLog(#"Error saving calendar: %#.", errorCalendar);
}
}
//start adding event to calendar
[self addSelectedOwnEventsToCalendar:dataDict];
}
}];
}
}
-(void)addSelectedOwnEventsToCalendar:(NSDictionary *)dataDict
{
// Create a new event... save and commit
NSError *error = nil;
EKEvent *myEvent = [EKEvent eventWithEventStore:self.eventStore];
myEvent.allDay = NO;
myEvent.availability = EKEventAvailabilityFree;
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
NSDate *startDate = [[NSDate alloc] init];
NSString *startDateFormatted=[NSString stringWithFormat:#"%# %#",[dataDict objectForKey:#"start_date"],[dataDict objectForKey:#"starts"]];
startDate = [dateFormatter dateFromString:startDateFormatted];
NSDateFormatter *dateFormatter1 = [[NSDateFormatter alloc] init];
[dateFormatter1 setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
NSDate *endDate = [[NSDate alloc] init];
NSString *endDateFormatted=[NSString stringWithFormat:#"%# %#",[dataDict objectForKey:#"end_date"],[dataDict objectForKey:#"ends"]];
endDate = [dateFormatter1 dateFromString:endDateFormatted];
//compare dates
NSComparisonResult result = [startDate compare:endDate];
if (result == NSOrderedAscending) {
} else if (result == NSOrderedDescending) {
} else {
//the same
endDate=[startDate dateByAddingTimeInterval:60];
}
myEvent.startDate=startDate;
myEvent.endDate = endDate;
myEvent.title = [dataDict objectForKey:#"event_id"];
myEvent.calendar = [self.eventStore calendarWithIdentifier:[[NSUserDefaults standardUserDefaults] objectForKey:#"Calendar"]];
myEvent.location=[dataDict objectForKey:#"location"];
NSArray *cals = [self.eventStore calendarsForEntityType: EKEntityTypeEvent];
NSPredicate *predicateForEventsOnHolidayDate = [self.eventStore predicateForEventsWithStartDate:startDate endDate:endDate calendars:cals]; // nil will search through all calendars
NSArray *eventsOnHolidayDate = [self.eventStore eventsMatchingPredicate:predicateForEventsOnHolidayDate];
//NSLog(#"eventsOnHolidayDate %#",eventsOnHolidayDate);
BOOL eventExists = NO;
for (EKEvent *eventToCheck in eventsOnHolidayDate) {
NSLog(#"eventToCheck.title %#",eventToCheck.title);
NSLog(#"event_id %#",[dataDict objectForKey:#"event_id"]);
if ([eventToCheck.title isEqualToString:[dataDict objectForKey:#"event_id"]]) {
eventExists = YES;
NSLog(#"Event Already Exists");
}
}
//save eventts
if (eventExists == NO) {
[self.eventStore saveEvent:myEvent span:EKSpanThisEvent commit:YES error:&error];
if (!error) {
// NSLog(#"the event saved and committed correctly with identifier %#", myEvent.eventIdentifier);
} else {
NSLog(#"there was an error saving and committing the event");
NSLog(#"Error %#",error);
error = nil;
}
}
}
When I first run the code NSlog says (#"ios Local Store Source");
What am I missing?
Note: Code is working on simulator(Ipad ios 7) most of the time, I assuming something wrong with [NSUserDefaults] ?
First, see the discussion points about duplicates in the calendarItemExternalIdentifier documentation and determine if any of them apply.
Then, take a look at the following suggestions:
The calendar identifier can change. Ensure that the saved version for key Calendar in NSUserDefaults is the same as the one being accessed in addSelectedOwnEventsToCalendar:.
When retrieving the calendar identifier from the NSUserDefaults make sure that it’s valid.
Try using the saveEvent:span:error: method without the commit parameter. I’ve had problems using the save event method that takes a commit argument in the past.
Lastly, I believe that iOS calendar access authorization can only be tested on an iOS device, which is probably why it’s working in the simulator.

EKEvent is not added according to given EKRecurrenceRule

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?
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.
The bug was confirmed by Apple, at least until iOS 7.1.3 (which is the latest available version at this moment).

Resources