UIDocumentInteractionController Calendar Access
Asked Answered
M

1

3

I have an ics (Calendar) file that I'm opening with a UIDocumentInteractionController, using presentOptionsMenuFromRect:. When this runs, the "Open In" menu looks like this.

As you can see, no "Add to Calendar" option. Here's what gets me: I'm using the same exact code for a .vcf (contact card) file, and it works as expected with the "Open In Contacts" option available.

Am I missing some sort of permission in my Info.plist for calendar access? Why can't UIDocumentInteractionController handle the .ics file type correctly, but .vcf works just fine? Those two file types are very similar. From the options menu, if I mail the ics file to myself and open it from the mail app, it reads it just fine, and I can add the events to my calendar, so I know the data is valid. I've searched high and low for a solution, and nobody seems to know why Calendar access isn't working. Some questions I've come across that remain unanswered:

Unable to Add ics file to Calendar

How can I have UIDocumentInteractionController show Calendar as an option for opening a .ics file?

If Apple is doing this deliberately, the only reason I can think of is because they'd rather developers use EventKit to add events to Calendar. If true, that solution is rather frustrating. Any insight on this problem would be much appreciated.

Marriageable answered 13/1, 2015 at 17:10 Comment(5)
Starting to get a little discouraged that there exists a solution to this problem right now. I'm going to submit a bug report to Apple and see if they have anything to say.Marriageable
I'm interested as well so make sure to post back if you find anything :)Wolverine
I ended up using EventKit to get it working. Instead of passing in an ics file, I'm parsing through the raw data and using EventKit to create a calendar w/ all the events. My guess would be that Apple prefers devs use this instead (security maybe?). That might not be the answer you want to hear, but it seems to be the only option. For what it's worth, I experienced many Android devices that couldn't read ICS files at all. I needed a third party importer app to get it in my device calendar. So it appears ICS files are not a high priority for either group.Marriageable
That's exactly what I did too. I answered your question with my solution so it can helps others. Also, I'm working on Android this week so if I find something I'll let you know.Wolverine
@Marriageable did yop find any solution on this is there any way we can show add to calender option on UIDocumentInteractionController.Alwyn
W
2

I ended up downloading the .ics file via (https://github.com/KiranPanesar/MXLCalendarManager). I was then able to use EventKit to parse the downloaded .ics file into an EKEvent and open it via EKEventEditViewController (https://developer.apple.com/library/prerelease/ios/samplecode/SimpleEKDemo/Listings/Classes_RootViewController_m.html). A little round about but seemed to work. Here's how I setup my webview controller class that implements this:

@interface WebViewController : UIViewController <UIWebViewDelegate, EKEventEditViewDelegate> {

// EKEventStore instance associated with the current Calendar application
@property (nonatomic, strong) EKEventStore *eventStore;

// Default calendar associated with the above event store
@property (nonatomic, strong) EKCalendar *defaultCalendar;

@end


@implementation WebViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    ...
    // Initialize the event store
    self.eventStore = [[EKEventStore alloc] init];
}


- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    NSURL *url = [request URL];
    NSString *path = [url absoluteString];

    NSRange range = [path rangeOfString:@".ics" options:NSCaseInsensitiveSearch];
    if (range.length > 0) {
        [self checkCalendarAndAddEvent:url];
        return NO;
    }
    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;

    return YES;
}

-(void)checkCalendarAndAddEvent:(NSURL*)url
{
     EKAuthorizationStatus status = [EKEventStore authorizationStatusForEntityType:EKEntityTypeEvent];
     if(status == EKAuthorizationStatusAuthorized)
     {
         [self addEventToCalendar:url];
     } else
     {
         [self.eventStore requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error)
         {
             if (granted)
             {
                  // Let's ensure that our code will be executed from the main queue
                  dispatch_async(dispatch_get_main_queue(), ^{
                     // The user has granted access to their Calendar; add to calendar
                     [self addEventToCalendar:url];
                  });
             }else
             {
                 UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Privacy Warning" message:@"Permission was not granted for Calendar"
                                                            delegate:nil
                                                   cancelButtonTitle:@"OK"
                                                   otherButtonTitles:nil];
                 [alert show];
             }
         }];
    }
}

-(void) addEventToCalendar: (NSURL *)url
{
    MXLCalendarManager* calendarManager = [[MXLCalendarManager alloc] init];
    self.defaultCalendar = self.eventStore.defaultCalendarForNewEvents;
    [calendarManager scanICSFileAtRemoteURL:url withCompletionHandler:^(MXLCalendar *calendar, NSError *error) {

        MXLCalendarEvent *mxlEvent = calendar.events.firstObject;

        EKEventEditViewController *addController = [[EKEventEditViewController alloc] init];
        EKEvent * event = [EKEvent eventWithEventStore:self.eventStore];
        event.location = mxlEvent.eventLocation;
        event.startDate = mxlEvent.eventStartDate;
        event.endDate = mxlEvent.eventEndDate;
        event.title = mxlEvent.eventSummary;
        event.notes = mxlEvent.eventDescription;

        addController.event = event;
        // Set addController's event store to the current event store
        addController.eventStore = self.eventStore;
        addController.editViewDelegate = self;
        [self presentViewController:addController animated:YES completion:nil];
   }];
}
@end

I also had to slightly modify parts of MXLCalendarManager.m to be ready for my specific types of .ics formatting. For example, my summary section of my .ics file looks like:

DESCRIPTION;LANGUAGE=en-us:The following details your appointment:\n\n\n

where as MXLCalendarManager is only looking for:

DESCRIPTION: (Something). 

I modified the code as such to account for the ;ln. This also removed all the artificial line breaks but allowed me add my own in the summary description:

    // Extract event description
    [eventScanner scanUpToString:@"DESCRIPTION" intoString:nil];
    [eventScanner scanUpToString:@":" intoString:nil];
    [eventScanner scanUpToString:@"\nSEQUENCE" intoString:&descriptionString];
    if(descriptionString.length > 1)
    {
        descriptionString = [descriptionString substringFromIndex:1];
        descriptionString = [[[descriptionString stringByReplacingOccurrencesOfString:@"\nSEQUENCE" withString:@""] stringByReplacingOccurrencesOfString:@"\r\n " withString:@""] stringByReplacingOccurrencesOfString:@"\\n" withString:@"\n"];
    }
Wolverine answered 6/3, 2015 at 17:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.