iOS unable to update calendar event when synced to exchange account - ios

i'm having an issue updating a calendar event on my ipad with an exchange email account on it. it'll create new events no problem, but on an update, it'll create another one, instead of updating the existing. a few things
code below works for any other type of email account (say gmail)
code below works if i don't have an email (so it just writes to the local calendar)
yes i've checked that my exchange email has calendars enabled
the error being thrown is:
Error getting event with identifier 9E678016-F8E4-46B1-9043-E54E09A148F0:5A89FFAF15B1408386A9CBD518BBDD770: Error Domain=EKCADErrorDomain Code=1010
here is the code that i'm using.
[self.eventStore requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error) {
if (granted) {
dispatch_sync(dispatch_get_main_queue(), ^{
NSString *appleEventId = [item valueForKey:#"appleEventId"];
EKEvent *currentEvent = [self.eventStore eventWithIdentifier:[item valueForKey:#"appleEventId"]];
if (currentEvent){
NSLog(#"LOG101: found an event with %#",appleEventId );
[self createEvent:currentEvent inEventStore:self.eventStore forActivity:item];
}
else{
NSLog(#"LOG101: COULD NOT FIND an event with %#",appleEventId );
EKEvent *event = [EKEvent eventWithEventStore:self.eventStore];
[self createEvent:event inEventStore:self.eventStore forActivity:item];
}
completionHandler(YES);
});
}
else{
dispatch_sync(dispatch_get_main_queue(), ^{
completionHandler(NO);
});
}
}];
(create event does a bunch of customer logic, but ultimately calls the following to create an event)
[eventStore saveEvent:event span:EKSpanThisEvent error:&err];
it's frustrating because the code works for everything else but exchange. any tips / suggestions would be great. thanks

I've been pulling hairs for two days over a similar problem!
The documentation for eventIdentifier states:
"If the calendar of an event changes, its identifier most likely changes as well."
https://developer.apple.com/reference/eventkit/ekevent/1507437-eventidentifier?language=objc
Also the documentation for calendarIdentifier states:
"A full sync with the calendar will lose this identifier. You should have a plan for dealing with a calendar whose identifier is no longer fetch-able by caching its other properties."
https://developer.apple.com/reference/eventkit/ekcalendar/1507380-calendaridentifier?language=objc
I thinks it's safe to say you can't rely on saving calendar or event identifiers locally for later use if the calendar is synchronised :-(

Related

Deleting and recreating EKCalendar

In my app, I create events in an EKCalendar. I fetch the events online, and in order to refresh the events, I want to first delete the calendar (if it exists), recreate it, and then put the new events in there.
To instantiate the calendar I use
- (EKCalendar *)calendar {
if (!_calendar) {
NSArray *calendars = [self.store calendarsForEntityType:EKEntityTypeEvent];
NSString *calendarTitle = #"MyCalendar";
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"title matches %#", calendarTitle];
NSArray *filtered = [calendars filteredArrayUsingPredicate:predicate];
if ([filtered count]) {
_calendar = [filtered firstObject];
} else {
_calendar = [EKCalendar calendarForEntityType:EKEntityTypeEvent eventStore:self.store];
_calendar.title = calendarTitle;
_calendar.source = self.store.defaultCalendarForNewEvents.source;
NSError *calendarErr = nil;
BOOL calendarSuccess = [self.store saveCalendar:_calendar commit:YES error:&calendarErr];
if (!calendarSuccess) {
NSLog(#"Calendar Error = %#", [calendarErr localizedDescription]);
}
}
}
return _calendar;
}
To delete the calendar, I use
-(IBAction)deleteCalendar{
NSError *error = nil;
[self.store removeCalendar:_calendar commit:YES error:&error];
}
Both methods work fine individually.
So, when I start the creation of events, I do the following:
[self deleteCalendar];//delete calendar and its events, in case it already exists
[self calendar];//create calendar
[self importEvents];//put events in calendar
Now, what I observe is the following:
On the first run of the app
a calendar is created
events are imported. (This is expected, and works just fine)
While the app is running, I trigger the above methods again with a button. With the following, for me puzzling, result:
the calendar is deleted (expected result)
NO calendar is created (WHY? that is my main question).The "if (!_calendar)" part of the method is considered FALSE, and nothing is executed.
The 'importEvents' method runs through its regular hoopla, without any apparent errors, although I would expect something like a 'no source' error.
Please advise.
UPDATE:
This could be an indicator of what is happening, but I still don't get it:
After a while, the events appear in a different calendar, i.e. not the calendar called 'myCalendar', but another, iCloud based calendar, apparently the one that at that point is the defaultCalendarForNewEvents. However, that also doesn't make any sense to me.
OK, so, what is happening:
I have deleted the Calendar from the store, effectively, but a reference to that calendar actually was still hanging around in my app.
I solved it as follows:
-(IBAction)deleteCalendar:(id)sender{
NSError *error = nil;
if(_calendar){
[self.store removeCalendar:_calendar commit:YES error:&error];
}
_calendar = nil;
}
I hope this is useful to someone

Azure Mobile Service Offline Data Sync - The item provided was not valid

I am using Azure Mobile Service as a backend for an iOS app. I have set up everything to work with offline sync which allows me to view, add, or modify data even when there is no network connection. I am now into testing and I run into an error: "The item provided was not valid" when I try to synchronize data.
Here's what I am doing:
I add a new athlete to the syncTableWithName:#"Athlete" with this:
NSDictionary *newItem = #{#"firstname": #"Charles", #"lastname": #"Lambert", #"laterality" : #"Orthodox"};
[self.athletesService addItem:newItem completion:^{
NSLog(#"New athlete added");
}];
Here's the addItem function:
-(void)addItem:(NSDictionary *)item completion:(CompletionBlock)completion
{
// Insert the item into the Athlete table
[self.syncTable insert:item completion:^(NSDictionary *result, NSError *error)
{
[self logErrorIfNotNil:error];
// Let the caller know that we finished
dispatch_async(dispatch_get_main_queue(), ^{
completion();
});
}];
}
For now everything is fine and the item is in the syncTable. The problem is when I try to synchronize with the Azure Mobile Service. Here's the syncData function I am calling:
-(void)syncData:(CompletionBlock)completion
{
// push all changes in the sync context, then pull new data
[self.client.syncContext pushWithCompletion:^(NSError *error) {
[self logErrorIfNotNil:error];
[self pullData:completion];
}];
}
The pushWithCompletion gets me the error: "The item provided was not valid." and same for the pullData function that gets called after:
-(void)pullData:(CompletionBlock)completion
{
MSQuery *query = [self.syncTable query];
// Pulls data from the remote server into the local table.
// We're pulling all items and filtering in the view
// query ID is used for incremental sync
[self.syncTable pullWithQuery:query queryId:#"allAthletes" completion:^(NSError *error) {
[self logErrorIfNotNil:error];
// Let the caller know that we finished
dispatch_async(dispatch_get_main_queue(), ^{
completion();
});
}];
}
I have tried inserting directly in the MSTable and that works fine. It's really when I am using the MSSyncTable that I run into this error. Although when I insert data manually in my database and that I synchronize my context I can fetch data and display within my UITableView.
Lookin forward to see what you guys think about this. Thanks a lot!
I just edited my question thanks to #phillipv.
When I add an item using NSDictionary just like I did I run into the error "The item provided was not valid". So I tried adding an item by first inserting it to my managedObjectContext and then calling:
NSDictionary *dict = [MSCoreDataStore tableItemFromManagedObject:newAthlete];
I then I get the error when I try to sync: "The item provided did not have a valid id."
I feel like I am experiencing a circle.. :S
#Charley14, you can work around the bug by adding the following handler.
- (void)tableOperation:(nonnull MSTableOperation *)operation onComplete:(nonnull MSSyncItemBlock)completion
{
NSMutableDictionary *rwItem = [NSMutableDictionary dictionaryWithDictionary:operation.item];
// Temporary workaround
[rwItem removeObjectsForKeys:#[ #"relationship1", #"relationship2"]];
operation.item = rwItem;
[operation executeWithCompletion:completion];
}
The tableOperation:onComplete: handler is simply removing keys that correspond to the relationships. You will have to replace 'relationship1', 'relationship2' in the code snippet with names of actual relationships in your application.
Once the bug (https://github.com/Azure/azure-mobile-services/issues/779) is fixed, this workaround can be removed.
This appears to be a bug in the iOS SDK, as the Many to One relationship is not supposed to be exposed in the object given to the operation during a Push call.
Created the following bug with more details on GitHub: https://github.com/Azure/azure-mobile-services/issues/779
The cause of the error message is due to the fact that the relationship is a NSSet on the object, and the NSJSONSerializer throws as it does not know how to convert that to JSON.

Can't add/change alarms EKAlarm to a [newly created] EKEvent event

I'm trying to create an event with one alarm programmatically like that:
+(void)exportEvent:(AgendaEvent*)evento
onCalendar:(EKCalendar*)calendar {
EKEventStore* store= [[[EKEventStore alloc] init] autorelease];
[store requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if(!granted) {
// show "not granted" message
return;
}
// save event
NSCalendar* gc= [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
EKEvent* event= [EKEvent eventWithEventStore:store];
event.title= evento.descrizione;
event.startDate= [gc dateFromComponents:evento.begin];
if(evento.end)
event.endDate= [gc dateFromComponents:evento.end];
else {
NSDateComponents* endDateComponents= [[evento.begin copy] autorelease];
endDateComponents.day++;
endDateComponents.hour= 0;
endDateComponents.minute= -1;
endDateComponents.second= 0;
NSDate* endDate= [gc dateFromComponents:endDateComponents];
// endDate is correctly set at 23:59 of the same day of beginDate, when all day beginDay is at 00:00
event.endDate= endDate;
event.allDay= YES;
}
event.calendar= calendar;
// reminder
NSDateComponents* reminderDateComponents= [[evento.begin copy] autorelease];
reminderDateComponents.day--;
reminderDateComponents.hour= 9;
reminderDateComponents.minute= 0;
NSDate* reminderDate= [gc dateFromComponents:reminderDateComponents];
// reminder date is correctly set at 9:00 of the previous day of beginDate
[event addAlarm:[EKAlarm alarmWithAbsoluteDate:reminderDate]];
NSError* err= nil;
[store saveEvent:event span:EKSpanThisEvent commit:YES error:&err];
if(err) {
// show "unable to export" message
return;
}
// show "exported" message
});
}];
}
but some times only (or more correctly often) [store saveEvent:event span:EKSpanThisEvent commit:YES error:&err] fails with:
2014-06-13 09:34:01.300 xxx[224:60b] CADObjectGetRelation failed with error Error Domain=NSMachErrorDomain Code=268435459 "The operation couldn’t be completed. (Mach error 268435459 - (ipc/send) invalid destination port)"
2014-06-13 09:34:01.301 xxx[224:60b] Impossibile esportare evento: Error Domain=EKErrorDomain Code=29 "Impossibile modificare avvisi." UserInfo=0x16a7c7e0 {NSLocalizedDescription=Impossibile modificare avvisi.}
I can't even find a description for code 29 in EKErrorDomain, does anybody have a clue?
Please mind that:
I'm not using arc as you can see but seems pretty correct to me (an to static analyzer too).
I've also tried to split the event save in two phases: one for the event and one for the alarm with exactly the same results.
"Impossibile modificare avvisi." means "Can not change alerts."
Tried on an iPad air with ios7.1.1 and ios7.1 simulator
CADObjectGetRelation related message is not always shown even if event creation fails but seems to not appear when the event and alarms are created.
Ok after some trial and error I managed to get it working.
The problem is that just before calling exportEvent: I was creating another EKEventStore to read and select an EKCalendar calendar.
I removed store from the question selector body and passed it as a parameter from the previous step and now it works. This, I suppose, because there is some ipc involved and store is deallocated between user calendar selection step and the actual event creation. If not enough time passes between deallocation in step1 and reallocation in step2 ipc connection gets denied which explains why sometimes it was working.

iOS - Can't add calendar event to Gmail calendar with EventKit

I want to add a new calendar event to the default iOS calendar. That works fine, until a user uses Gmail for syncing calendars.
I use the following code:
if (!calendar) {
calendar = [EKCalendar calendarForEntityType:EKEntityTypeEvent eventStore:eventStore];
// set calendar name
[calendar setTitle:#"My calendar"];
EKSource *theSource = [eventStore defaultCalendarForNewEvents].source;
calendar.source = theSource;
// save this in NSUserDefaults data for retrieval later
NSString *calendarIdentifier = [calendar calendarIdentifier];
NSError *error = nil;
BOOL saved = [eventStore saveCalendar:calendar commit:YES error:&error];
if (saved) {
// saved successfuly, store it's identifier in NSUserDefaults
[[NSUserDefaults standardUserDefaults] setObject:calendarIdentifier forKey:#"railplanner_calendar_identifier"];
} else {
// unable to save calendar
return NO;
}
}
This works fine when iCloud is enabled, or with local calendars.
But when a user uses Gmail for syncing calendars, my custom calendar doesn't appear in the calendars list. The local calendars disappear too.
Does anyone know how I can add a new calendar with a new event to a Gmail (or Google) Calendar?
Many thanks in advance.
This is likely to be impossible with the current Google Calendar synchronization, you could detect the type of the default calendar, and in the case of Google use the related Google iPhone API to create your new calendar (not really helpful I know)

Programatically add a reminder to the Reminders app

I'm creating a simple note application and I want to implement Reminders. The user would type a note, tap a button and it would set up a reminder in the Reminders app using the text. Is this possible, and if so, how do I do it? I have seen Apple's documentation on EventKit and EKReminders but it has been no help at all.
From the "Calendars and Reminders Programming Guide"? Specifically "Reading and Writing Reminders"
You can create reminders using the reminderWithEventStore: class method. The title and calendar properties are required. The calendar for a reminder is the list with which it is grouped.
Before you create a reminder, ask for permission:
In the .h:
#interface RemindMeViewController : UIViewController
{
EKEventStore *store;
}
and the .m, when you are going to need access to Reminders:
store = [[EKEventStore alloc] init];
[store requestAccessToEntityType:EKEntityTypeReminder
completion:^(BOOL granted, NSError *error) {
// Handle not being granted permission
}];
To actually add the reminder. This happens asynchronously, so if you try to add a reminder immediately after this, it will fail (crashes the app in my experience).
- (IBAction)addReminder:(id)sender
{
EKReminder *reminder = [EKReminder reminderWithEventStore:store];
[reminder setTitle:#"Buy Bread"];
EKCalendar *defaultReminderList = [store defaultCalendarForNewReminders];
[reminder setCalendar:defaultReminderList];
NSError *error = nil;
BOOL success = [store saveReminder:reminder
commit:YES
error:&error];
if (!success) {
NSLog(#"Error saving reminder: %#", [error localizedDescription]);
}
}

Resources