Modifying EKParticipants (attendees) in EventKit l

2020-01-30 09:00发布

问题:

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!

回答1:

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!");
}


回答2:

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
}