24

My goal is to add some invitees to an EKEvent. I've looked into other questions like this, such as this one, but all say that it is essentially impossible to add EKParticipants to an EKEvent programmatically. I do see that the attendees property is read-only, but I see other services like Sunrise using it in their mobile app.

I'm confident that they're using some system that at least partially integrates with EventKit because they're pulling in calendars from the user's iCal app. Invites sent from an added Exchange account, for example, are also clearly sent by the Exchange service as opposed to Sunrise's own invite (a result of either posting the event straight to Exchange or to posting it to iCal).

Any workaround this restriction would be very helpful - maybe an endpoint on Exchange that can be called to add/remove invitees? A workaround inside of EventKit? As long as it's not a private Apple API, I'd be more than happy to try it out.

Thanks!

Community
  • 1
  • 1
rebello95
  • 8,486
  • 5
  • 44
  • 65
  • From my experience, It was not possible to add `EKParticipant` objects even using private API. Save would always fail. Could be that I didn't dig hard enough, but my sense is that they only want people to add participants using EventKitUI (perhaps for privacy reasons). I'd be surprised if a popular application such as Sunrise would use private API to edit events. – Léo Natan Jun 05 '15 at 19:06
  • If you are interested in private API, let me know, and I'll give digging around a chance once again. – Léo Natan Jun 05 '15 at 19:07
  • @LeoNatan Thanks for your response. Unfortunately, I have to stick to public APIs to comply with App Store guidelines. Would there be any way to add attendees after the fact via a REST call using a calendar ID obtained from EventKit? – rebello95 Jun 05 '15 at 19:08
  • Not really. EK identifiers are usually device specific, and are not the real identifiers of the remote server (Exchange, iCloud, etc.). – Léo Natan Jun 05 '15 at 19:10
  • @LeoNatan Ah, good point. I just can't think of any other way they're doing it... – rebello95 Jun 05 '15 at 19:10
  • I installed and tested Sunrise. It is indeed interesting. They may be playing around with a hidden UI and feeding it information from their UI, so it actually does all the work. – Léo Natan Jun 05 '15 at 19:22
  • @LeoNatan I also considered that, but I believe those are private APIs. I wasn't able to access any useful UI elements from `EKEventEditViewController`. Have you been able to do this successfully? – rebello95 Jun 05 '15 at 22:13
  • I haven't tried. I will take a look if I have time today. – Léo Natan Jun 06 '15 at 06:06
  • @LeoNatan Thanks for your help. I did figure it out. Didn't actually have to use the UI library, I was able to do it with `EKAttendee` and `EKEvent`. Check out my answer for more info – rebello95 Jun 12 '15 at 03:01

2 Answers2

17

I figured it out!

Essentially, one must go into the EKAttendee class, which inherits from EKParticipant. I did this by creating a generic instance of that class using the NSClassFromString() method.

Once you have an attendee, you can set the properties using the setValue:ForKey: function.

Lastly, compile your EKAttendee instances into an array, and set that to the EKEvent's attendees property.

I tested this with an Exchange account on my device, and it worked like a charm - invite sent and everything!

The code below is what I'm now using to set the attendees of events. My example is for creating new events, but this can be done very simply for existing events by making a copy of the attendees list on the EKEvent, then modifying that and re-setting it.

//Create generic event info
EKEvent *event = [EKEvent eventWithEventStore:database];
event.title = @"TEST EVENT";
event.location = @"Test location";
event.notes = @"Example notes";
event.startDate = [NSDate date];
event.endDate = [[NSDate date] dateByAddingTimeInterval:(60 * 60)];
event.calendar = exchange;

//Do our super clever hack
NSMutableArray *attendees = [NSMutableArray new];
for (int i = 0; i < 5; i++) {

    //Initialize a EKAttendee object, which is not accessible and inherits from EKParticipant
    Class className = NSClassFromString(@"EKAttendee");
    id attendee = [className new];

    //Set the properties of this attendee using setValue:forKey:
    [attendee setValue:@"Invitee" forKey:@"firstName"];
    [attendee setValue:[NSString stringWithFormat:@"#%i", i + 1] forKey:@"lastName"];
    [attendee setValue:@"test@email.com" forKey:@"emailAddress"];

    //Add this attendee to a list so we can assign it to the event
    [attendees addObject:attendee];
}

//Finally, add the invitees to the event
[event setValue:attendees forKey:@"attendees"];

//Save the event!
NSError *error = nil;
[database saveEvent:event span:EKSpanThisEvent error:&error];
if (error) {
    NSLog(@"Save failed with error: %@", error);
} else {
    NSLog(@"Success!");
}
rebello95
  • 8,486
  • 5
  • 44
  • 65
  • Actually I've tried this method and have seen problems in the past. Make sure to test on more than just the simulator. – Léo Natan Jun 12 '15 at 06:53
  • @LeoNatan Yeah I've been testing on real devices and haven't had an issue yet. Guess we'll see... – rebello95 Jun 12 '15 at 13:39
  • @LeoNatan what kind of problems have you seen in the past with this? – Ewan Mellor Jun 12 '15 at 17:48
  • For example, test that all source types work, that invitees are updated about changes, cancellations, etc. – Léo Natan Jun 12 '15 at 18:08
  • 4
    I have used the above. But I am getting below error for iCal calendar. Save failed with error: Error Domain=EKErrorDomain Code=31 "Attendees cannot be changed" UserInfo=0x7f85a06d5880 {NSLocalizedDescription=Attendees cannot be changed} How can I proceed? Someone help me on this. – Neela Jun 23 '15 at 12:24
  • I'm getting "NSUnknownKeyException" in iOS 8.3 – Sripathi Jul 29 '15 at 05:16
  • 1
    @AndrewNester I've had an app go through review successfully with this approach without a problem. – rebello95 Oct 26 '15 at 16:31
  • 1
    @rebello95 thanks for quick response! Solution works, so I will try to submit to App Store and we'll see how it goes. Thanks again! – Andrew Nester Oct 26 '15 at 16:39
  • We've also tested this in beta testing just to see if it worked - it worked on one OS and then broke on the next. Never submitted to App Store with the functionality. – AlexK Jan 29 '16 at 20:41
  • This solution doesn't seem to work anymore. I am always getting "Attendees cannot be changed" error, when trying to save. Any help? – Beny Boariu May 02 '16 at 16:03
  • @SeanChense I am always getting this error in iOS 9.3.1. Can you please post some code? And are you testing in the simulator or device? – Beny Boariu May 03 '16 at 19:01
  • Tested on device. Maybe you'd better look out `event.calendar`.@BenyBoariu – SeanChense May 04 '16 at 06:14
  • Above code working for me too, but i need one functionality "I want to modify Attendee status". i Mean my status change to accept or maybe. is this possible ? – Ammaiappan Dec 16 '16 at 12:41
10

Swift version of rebello95's solution. I can confirm it works like a charm! I played around with the firstName and lastName properties but they do not seem to matter at all, so I left them out of my example. Only the emailAddress matters, which is matched against your contact list to display a full name in for example the native calendar app.

private func createEventWithAttendees() {
    let eventStore = EKEventStore()

    let calendars = eventStore.calendarsForEntityType(.Event)

    let event = EKEvent(eventStore: eventStore)
    event.title = "TEST EVENT"
    event.startDate = NSDate()
    event.endDate = NSDate().dateByAddingTimeInterval(60 * 60)
    event.calendar = calendars[0]

    var attendees = [EKParticipant]()
    for i in 0 ..< 5 {
        if let attendee = createParticipant(email: "test\(i)@email.com") {
            attendees.append(attendee)
        }
    }
    event.setValue(attendees, forKey: "attendees")

    try! eventStore.saveEvent(event, span: .ThisEvent)
}

private func createParticipant(email email: String) -> EKParticipant? {
    let clazz: AnyClass? = NSClassFromString("EKAttendee")
    if let type = clazz as? NSObject.Type {
        let attendee = type.init()
        attendee.setValue(email, forKey: "emailAddress")
        return attendee as? EKParticipant
    }
    return nil
}
Community
  • 1
  • 1
Tom van Zummeren
  • 9,130
  • 12
  • 52
  • 63
  • Tried this with Swift 3.1 and iOS 10.x - the `eventStore.saveEvent(...)` fails with error message `Error Domain=EKErrorDomain Code=35 "Attendees cannot be changed."` – leviathan Feb 24 '17 at 08:33
  • (Tested on Xcode 9.1 beta and iPhone X simulator) I am able to invite people and append (making changes to existing array) as an organizer, but not able to append as an invitee. Besides, I am not able to accept invitation. In all failed cases, the error is "Attendees cannot be changed." Any help? – Harry Ng Oct 06 '17 at 04:34
  • thanks...Don't try it in simulator .it's not working in simulator .... it worked for me in device . – Deepti Raghav Dec 19 '18 at 11:16
  • This is great except for existing invitations. How to accept an invitation? I also get "Attendees cannot be changed." when I try to save the change, just like @HarryNg – Burgler-dev Sep 14 '20 at 14:58