Programmatically launch Mac Calendar.app (and choose specific date) from my app? - macos

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.

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.

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://"]];

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.

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?

Related

AXIsProcessTrustedWithOptions doesn't return true even when the app is ticked in accessibility

As this question answered, I use AXIsProcessTrustedWithOptions with no option to test if my app has been enabled in the accessibility panel. If not, prompt the window to let users to enable it. There is button bound to this test call as well, so users don't need to close the UI and reopen it.
But sometimes, even after a user enables the app, AXIsProcessTrustedWithOptions still returns false. When I check the sqlite database, it shows my app as "kTCCServiceAccessibility|com.abc.def|0|1|1||". The first 1 digit indicates it has been allowed apparently. But the api call still returns false. At this point, if I close the UI, the app is unticked in the accessibility panel which causes a loop so users can never pass this step.
Another thing is this app is actually a version 2 of the previous app. But the user did replace the bundle with the v2 one. Not sure if this is related.
Any idea why this would happen?
NSDictionary *options = #{(id)kAXTrustedCheckOptionPrompt: #NO};
BOOL accessibilityEnabled = AXIsProcessTrustedWithOptions((CFDictionaryRef)options);
if (!accessibilityEnabled) {
NSString *urlString = #"x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility";
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:urlString]];
}
Update: In Xcode, Signing & Capabilities, make sure you have selected a Team and Signing Certificate.
Original answer:
I experienced this when developing an app on macOS 10.14.5 in Xcode trying both AXIsProcessTrusted and AXIsProcessTrustedWithOptions .
After wasting a lot of time trying all sorts of things, including different options for AXIsProcessTrustedWithOptions, logging out/in, rebooting, praying, and crying, I eventually shut down my machine and powered it back on in frustration. Then it worked. Hopefully nobody else has this same experience.

Custom URL Scheme for Settings on iOS 10?

Any Idea what happened to the Setting's Custom URL Scheme on iOS 10?
Is Apple still giving acess to third-pary apps to launch iOS Settings from the app via URL Scheme on iOS10? The Old URL scheme are not working anymore!
None of the previous methods for launching the root "Settings" app on iOS 8+ were officially supported by Apple, so unfortunately we can't rely on them. It's also possible that apps that relied on the undocumented behaviors could be rejected during App Store review, even if others have been approved--even if the same app had been previously approved!
I've been unable to discover any workaround either, so it seems your choices are:
Open the app-specific URL (as detailed in many places, including #alvin-varghese 's answer above), and then ask the user to navigate backwards. (A terrible user experience, since it involves the user knowing to scroll up from the app list into the main settings sections.)
Use an instructional screen or alert in your app to educate users on how to find it themselves. (Not much better, but at least aren't dropped into an unfamiliar context with no waypoints.)
There doesn't seem to be an officially supported way of making this happen in iOS 10, and as you pointed out, it seems like the old ways (adding prefs scheme to your Info.plist file and using openURL(_:)) do not work any more.
From iOS 10 some of the things are changed,
So, for something like bluetooth you need to write below lines of code,
Swift 3.0 :
func openBluetoothSettings(){
let urlBTSet = URL(string: "App-Prefs:root=Bluetooth")
let objApp = UIApplication.shared
objApp.openURL(urlBTSet!)
}
Objective-c
-(void) openBluetoothSettings{
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"App-Prefs:root=Bluetooth"]];
}
So, In above code what they have changed is string need to add "App-Prefs:root=Bluetooth" (This the example of opening bluetooth settings)
Don't forgot : 'Goto taget -> info -> URL Types -> Add "prefs" in URL Schemes'
Use this one, it works.
if let url = URL(string: UIApplicationOpenSettingsURLString) {
UIApplication.shared().open(url, options: [:], completionHandler: nil)
}

How to enumerate running/open apps in app order with applescript?

My end goal is to determine the most-recently-used web browser. I.e., a user may have both Safari and Firefox running, but which were they using most recently?
And so, I'm trying to enumerate the open apps, ordered by their window / visibility.
Does Cocoa / Applescript offer a way to retrieve this information?
You can use NSWorkspace's runningApplications method and NSRunningApplication's launchDate property to get you close to what you need.
Check out what the output if this code bit looks like in your console:
NSArray * arrayOfRunningApps = [[NSWorkspace sharedWorkspace] runningApplications];
if(arrayOfRunningApps)
{
for(NSRunningApplication * aUserApp in arrayOfRunningApps)
{
NSLog( #"%# launch time is %#", [aUserApp localizedName], [aUserApp.launchDate description] );
}
}
As for sorting on some criteria, that's really up to your implementation to do.
A couple caveats: these api's were introduced as of Snow Leopard (10.6) and
they only work on apps that the user can see (in the dock), not UNIX processes or admin/root daemons or whatever.
And I'm not certain about Applescript yet, but I'm hoping you'll be able to use this information to get to where you need to go.

Finder file icon badging (icon overlay) on Mac OS X >= 10.6

I'm searching for a solution to do File icon overlays (icon badging) (like Dropbox does it on mac) with cocoa on Mac.
Does anyone knows a way to do this?
I've searched the Xcode docs and looked into scpplugins source code which is kind of old carbon code.
A litte bit late, but maybe will be help someone.
I solved same problem with class NSWorkspace (see setIcon:forFile:options)
Basic idea:
Try to get preview of file with QLThumbnailImageCreate (if not NULL you will get thumbnail icon)
If you didn't get thumbnail, then get default OS X icon for file (NSWorkspace iconForFile)
Combine thumbnail (or default icon) with your badge
Set new icon to the file (NSWorkspace setIcon:forFile:options)
Since the Finder was reworked in Snow Leopard, the older Carbon methods will no longer work. The route I've taken to be able to badge icons in Finder involves writing a custom bundle which then needs to be injected into the Finder.
Look into Wolf Rentzsch's mach_inject (https://github.com/rentzsch/mach_star/tree/master/mach_inject) to be able to inject a custom bundle to a Cocoa application.
Use class-dump to be able to get a look into the header files of a Cocoa application (such as the Finder in Snow Leopard and Lion) to get an idea of what you will need to override in your own bundle.
I know this is an old question.
In recent time there is a library that implement this functionality: https://github.com/liferay/liferay-nativity.
NSDockTile makes this very simple:
NSDockTile *dockTile = [NSApp dockTile];
[dockTile setBadgeLabel:#"33"];
You can use the following two methods to have icon overlay over the folders/files.
You can use -setIcon:forFile:options: method on NSWorkspace if you want to change the icon of a file or folder in Mac OS X.
However after you apply icon overlay using this method, overlay exits even though you moved that file/folder outside. This may not be the exact solution.
Instead use the Finder Sync Extension target (File - New - Target - Finder Sync Extension) inside your app.
Once you created the extension, your application doesn't have direct communication with this target. In order to activate, use AppleScript command (I don't think there is a better alternative for this.)
To activate
NSString *pluginPath = [[[NSBundle mainBundle] builtInPlugInsPath] stringByAppendingPathComponent:#"yourextension.appex"];
NSString *pluginkitString = [NSString stringWithFormat:#"pluginkit -e use -a \"%#\"", pluginPath];
system([pluginkitString cStringUsingEncoding:NSUTF8StringEncoding]);
Once target activated, there are couple of ways were our application can communicate with that extension. Few of them are:
Using NSDistributedNotificationCenter. This class provides a way to send notifications to objects in other tasks(like the extension here).
Other way is to use:
[[NSUserDefaults alloc] initWithSuiteName:#"teamid.com.company.test"];
Both your application and the target should have common group identifier(i.e "teamid.com.company.test").
For this enable "App Groups" under Target - Capabilities - App Groups and give the identifier like the above (i.e"teamid.com.company.test") were teamid is the id that you will get it from your apple developer portal. Do the same steps for your Extension target as well.
Before to conclude, be sure that extension is activated or not. To check that go to System Preference - Extensions - Your App Finder.
This is the global point were user can enable/disable icon overlay for your application.

How do I send SMS without launching Text application?

I am developing a simple application which has to send an SMS message from the app itself instead of launching the native Text app.
Here's my action now. What should I change to achieve my desired functionality?
-(IBAction)startButtonPressed
{
NSString *phoneNumber = #"13136296693";
NSString *url = [NSString stringWithFormat: #"sms:%#",phoneNumber];
NSLog(#"Send SMS to: %# ", url);
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]];
}
Thanks!
Thomas
If I'm not mistaken I think sending text messages via your own application is only available in the iPhone 4.0 SDK.
Can't be done with the current SDK. I believe Apple announced that applications in iPhone OS 4.0 will be able to do this, using a similar interface to the existing MFMailComposeViewController malarkey.
Here's what apple says:
https://web.archive.org/web/20140604143837/http://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/SystemMessaging_TopicsForIOS/Articles/SendinganSMSMessage.html
Here's a tutorial fon another forum.
http://iosdevelopertips.com/core-services/how-to-send-an-sms-progammatically.html
It's still not entirely automated. The user has to tap the send button. I haven't gotten it working yet. Tell me if you have better luck. If you can think of a way for me to program my app to "tap" the send button instead of the user, let me know.

Resources