4

I'd like to include a button in my Mac app which, when pressed, will launch the user's default calendar app. Preferably, I'd like to have the calendar open to a certain date.

This is for OS X Mountain Lion.

Is there a general way to do this?

Edit: FWIW, this is what I'm doing now:

- (IBAction)launchCalendarApp:(id)sender
{
    [[NSWorkspace sharedWorkspace] launchApplication:@"/Applications/Calendar.app"];
}

I know hardcoding the path like this is a bad idea which is why I'm asking the question.

Update: This is what I ended up doing:

- (IBAction)launchCalendarApp:(id)sender
{
    NSWorkspace *sharedWorkspace = [NSWorkspace sharedWorkspace];
    NSString *iCalPath = [sharedWorkspace absolutePathForAppBundleWithIdentifier:@"com.apple.iCal"];
    BOOL didLaunch = [sharedWorkspace launchApplication:iCalPath];
    if (didLaunch == NO) {
        NSString *message = NSLocalizedString(@"The Calendar application could not be found.", @"Alert box message when we fail to launch the Calendar application");
        NSAlert *alert = [NSAlert alertWithMessageText:message defaultButton:nil alternateButton:nil otherButton:nil informativeTextWithFormat:@""];
        [alert setAlertStyle:NSCriticalAlertStyle];
        [alert runModal];
    }
}

It sounds like all of the possible ways of doing this are workarounds until a better API is developed. My solution is similar to Jay's suggestion. I'm using the bundle identifier to get the path because I think it is a little less brittle. Apple is unlikely to change the bundle ID in the future even if they (or the user) decides to rename the app. Unfortunately, this method doesn't get me to a specific date. I will investigate further some of the other suggestions (using ical:// etc.) when I have more time.

Update 2: NSGod has a terrific answer below that also opens the Calendar to a specific date provided your app is not sandboxed.

sam
  • 3,399
  • 4
  • 36
  • 51

4 Answers4

8

Note: I was still researching this while you updated with what you used, but I'll add this FWIW.

Using the bundle identifier of the application is generally a more robust way to refer to an app then using the name alone, as a user could move or rename the app in OS X, but they can't easily change the bundle identifier. Moreover, even while Apple renamed iCal.app to Calendar.app, the CFBundleIdentifier is still com.apple.iCal.

if (![[NSWorkspace sharedWorkspace]
                launchAppWithBundleIdentifier:@"com.apple.iCal"
                                      options:NSWorkspaceLaunchDefault
               additionalEventParamDescriptor:nil
                             launchIdentifier:NULL]) {
    NSLog(@"launching Calendar.app failed!");
}

The above code will work even if your app is sandboxed. You could potentially try to create a custom NSAppleEventDescriptor that would specify the equivalent of something like the following AppleScript code, but it will likely be denied because of the sandbox:

view calendar at date "Sunday, April 8, 2012 4:28:43 PM"

If your app doesn't have to be sandboxed, it's much easier if you use the Scripting Bridge, and with that method it's possible to select a specific NSDate.

Sample project using ScriptingBridge: OpenCalendar.zip

In that project, I use the following code:

SBCalendarApplication *calendarApp = [SBApplication
              applicationWithBundleIdentifier:@"com.apple.iCal"];
[calendarApp viewCalendarAt:[self.datePicker dateValue]];

That will launch Calendar.app/iCal.app and change the calendar to the specified date.

sam
  • 3,399
  • 4
  • 36
  • 51
NSGod
  • 22,699
  • 3
  • 58
  • 66
  • Terrific answer. I'm not sure of the Stackoverflow etiquette when it comes to accepting an answer after I've already accepted a different one, but I did up-vote this. – sam Apr 09 '13 at 17:56
  • Holy crap this is it! Thank you! – rocky Jan 15 '14 at 23:09
  • Can this be done in swift. Im using `let celendarURL = URL(string: "calshow:\(interval)")` and then `UIApplication.shared.open(celendarURL, options: [:], completionHandler: nil)` but if I change it to `ical` the calendar opens but not to the date – Kurt L. Dec 20 '20 at 01:44
4

So it sounds like for now you'd either have to resort to a hard wired approach, e.g.

// Launches Calendar.app on 10.7+
[[NSWorkspace sharedWorkspace] launchApplication:@"Calendar"];

Or use the URL scheme using what's supported by Calendar/iCal on OS X (pointed out by NSGod in comments below) similar to URL Scheme for opening the iCal app at a date or event? :

// Launches iCal (works at least with 10.6+)
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"ical://"]];
Community
  • 1
  • 1
Jay
  • 6,572
  • 3
  • 37
  • 65
  • This is what I'm doing now, but I don't like doing it because it's very brittle and it doesn't get me to a specific date. – sam Apr 08 '13 at 17:19
  • It's not nice but pragmatic - as long as iCal isn't renamed you should be ok. How about creating a temp. `.ics` file dynamically with the appointment you want and opening that one using the URL scheme? – Jay Apr 08 '13 at 17:22
  • The .ics idea is interesting. I will investigate. – sam Apr 08 '13 at 17:27
  • Not directly related but contains a link to a github project that might get you started with writing `.ics` files: http://stackoverflow.com/questions/6290484/how-to-create-an-ics-file – Jay Apr 08 '13 at 17:30
  • @jay iCal was renamed to Calendar in Lion, just saying. – JustSid Apr 08 '13 at 17:50
  • FWIW, the app name has changed to Calendar, but the bundle ID is still com.apple.iCal. So you could still pass com.apple.iCal to NSWorkspace's -absolutePathForAppBundleWithIdentifier: and it would give you the path to Calendar.app. – sam Apr 08 '13 at 20:46
  • If you check the `Info.plist` for the Mac version of Calendar.app, you'd see that it doesn't declare the `calshow://` URL scheme, only `webcal://` and `ical://`. `calshow://` is likely something specific to the iOS version of Calendar.app. – NSGod Apr 08 '13 at 21:50
1

You might try using EventKit to create a EKCalendarItem instance of the desired date and time, open the event, then remove it immediately thereafter. If it's well-timed, it may not even visibly blink on/off the user's calendar.

It's another kludge, but until NSWorkspace has an -openDate: method, kludges are the only recourse.

Joshua Nozzi
  • 60,946
  • 14
  • 140
  • 135
  • Sounds like the most robust approach yet to me. – Jay Apr 08 '13 at 18:06
  • Can you explain how to "open" the event from my app once I create it with EventKit? – sam Apr 08 '13 at 18:11
  • Jay's `calshow://` code in his answer should do it if there's no EventKit methods to "display this event in the default viewer" or something similar. The only pitfall I can see, beyond the possibility of a temporary event "blinking" is potential timing problems waiting for the event to be displayed in Calendar before removing it. Experimentation required. – Joshua Nozzi Apr 08 '13 at 18:32
0

As discussed on this thread, it seems there's no url scheme to launch iCal

URL Scheme for opening the iCal app at a date or event?

Community
  • 1
  • 1
ssantos
  • 16,001
  • 7
  • 50
  • 70