How to implement "Open With" contextual menu in OS X - macos

Apps in OS X that can open files to launch their respective applications often let the user choose the app that'll open the file. An example is the Finder.
I am still unclear about what's the best solution to implement this. The challenges are performance and showing the app's icon.
First, to get the list of apps, I found only LSCopyApplicationURLsForURL().
The major difficulty for me now is to get the app icons. All I have is the URLs (paths) to the apps. The only way I know of to get the icons would be to create a CFBundle object and then read the app's plist to get the icon data. But that appears to invole lots of disk access, and I could imagine it'll be quite inefficient if the app is located on a remote file server.
I believe that there's also a cached database about the apps, which includes icons and display names (without extension), and such. But where is the API for that?
Note: The app is Carbon based, but I can make use of Cocoa (NS...) functions if necessary. Also, Support for 10.5, even 10.4, would be appreciated.

Alright. I solved it eventually.
I now use the Carbon function GetIconRefFromFileInfo(), which still works in Lion, though it's marked deprecated/obsolete.
I had also tried to use QLThumbnailImageCreate(), but that didn't get me any results, oddly. No idea what I did wrong.
For Cocoa apps, [[NSWorkspace sharedWorkspace] iconForFile:path] can be used (thanks to C. Grunenberg of DevonTechnologies for this tip, where it's used with EasyFind).

Related

Programmatically change order of full screen apps on macOS with AppKit

I’m trying to build a macOS utility that will allow me to programmatically change the order of full screen apps on my machine.
For example, let’s say the full screen apps on my machine are currently ordered as follows from left to right:
Chrome
iTerm
Xcode
I want to be able to call a method in AppKit to say something like “Move Xcode from full screen index 2 to index 0”. However, I can't identify an AppKit API that will help me do this.
NSScreen only gives the physical displays, not the full screen apps / spaces
NSApplication only gives the list of my app's windows
CGWindowListCopyWindowInfo looked promising, but it doesn't have information about the order of full screen apps. It also doesn't allow me to get a NSWindow object to have any hope of changing the order
Is what I'm trying to do even possible with AppKit?
Thank you!
Spaces (desktops and fullscreen apps are both just spaces) are a feature of the system Window Manager and only manageable by the user through the app Mission Control.
Of course, the Window Manager needs to expose an API for management, otherwise how would Mission Control interact with it? But that API is not public.
Reverse engineered headers for those private APIs can be found online, e.g.
https://github.com/NUIKit/CGSInternal
With those headers in place, you can write code like this:
#import "CGSInternal/CGSSpace.h"
CGSSpaceID activeSpace =
CGSGetActiveSpace(_CGSDefaultConnection());
NSLog(#"activeSpace: %zu", activeSpace);
CFArrayRef spaceArray =
CGSCopySpaces(_CGSDefaultConnection(), kCGSAllSpacesMask);
NSLog(#"allSpaces: %#", (__bridge id)spaceArray);
But the usual warnings apply here:
You cannot commit code using private APIs to Apple's App Store. There is no official documentation, all that is known about that API has been reversed engineered and may not always be correct. As the API is not public, Apple can change it at any time without any previous warning and thus your working code could break with every system update.

View loading as blank/white screen in Mac desktop application Xamarin.Mac

I have a Mac desktop application developed using Xamarin.Mac and XCode. The application is loading views that should have a considerable amount of content as blank/white screens for a select few end users. While I've not been able to reproduce the issue locally and it doesn't appear to be impacting many people, it does happen consistently for a handful of users(6 out of over 4,000 at the moment). The app works just fine for the vast majority and uses a main window controller that sets the content view as needed by calling the applicable ViewController. When the views in question are loaded for the given users, it renders as a blank white area.
I did notice however that at least some of the objects in the views are actually there, just not visible. For instance, one view has buttons that are still "there" as you can click on them if you know where they are supposed to be positioned(and they also fire correctly) but they are not actually visible as the area is all white with no visible content. It's as if everything is loading properly but the UI just isn't cooperating in displaying objects that are actually there. The closest thing I could find was this Diagnosing run loop issue (partially frozen UI) in Cocoa application
However I'm not sure it's the same problem and there was no resolution for that either. Has anyone else ever encountered something like this?
Ok, got some new info. After doing a remote session with a user we had no luck with the hardware and the console didn't provide much insight, however we did stumble across a workaround that fixes the issue and may give some clues as to the real problem.
When the application is initially installed, we prompt the user to ask if they want it moved to the default Applications directory. They do not have to move it as it will work the same from pretty much anywhere but we do this for convenience. For the users having this problem, we noticed that they all had the app installed in the Applications folder. By sheer luck, one user accidentally chose to not move the app after reinstalling it per our support team. When the app was installed in his default Downloads directory, the problem vanished and the app worked as expected.
After testing various other directory locations(Downloads, Documents, etc.) we found that the issue only seemed to exist if the app was installed in the Applications directory. Furthermore, if the app was installed in the Applications directory, we could simply move it or copy/paste it to another directory outside Applications and when launching it from that new location the issue was resolved.
Seems like there is something specific to the default Applications directory that is causing this to happen(as odd as that sounds). Keep in mind that even when the app is installed in the Applications directory, this issue only happens to 7 people out of over 4,000.
Any ideas as to what might be special about that directory that could explain what we're seeing?

Can I turn off saving in a Document Based app? (Swift for OSX)

I'm trying to make an extremely simple note-taking app for OSX: one that can have multiple windows open and where I can quickly write down something. I don't want to store anything anywhere.
Most importantly: it should not nag about saving on quitting the app.
I'm nearly there, but I am stuck at turning off saving.
Any ideas if this is possible for a Document Based swift-app?
(using Swift and Xcode, complete NOOB at this)
There are different types of applications. You can specify this when you start a new project.
Disable "create document-based application" when you start a new project and it won't nag you about saving anything.

Access to Safari bookmarks from another application

I was wondering how we can access Safari bookmarks from another Cocoa application on Mac OS X, in a way that is safe and secure for the future.
As you may know, two mechanisms were common to retreive Safari bookmarks:
either read Safari's Bookmarks.plist file
or use the SyncServices API.
However, the first is forbidden by sandboxing (mandatory for a distribution through the App Store), and the second has been deprecated since Mac OS X 10.7 Lion.
I believe that Apple deprecated SyncServices in favour of iCloud synching, but I can't find any iCloud API that allow access to the bookmarks (1).
Any hint on where to look? Native Cocoa is preferred, but any non-deprecated, sandboxing-compatible solution is welcome.
Thanks.
(1) and, honestly, going through the internet (and thus requiring an internet connexion) to retrieve on a machine something on the same machine seems... awkward — but well, if it was the way to go at least that would be a possibility.
You can read out the bookmarks plist file even when running in a sandbox if you expressly ask the user for consent: Present an open panel pointing to the plist directory and store the security-scoped bookmark you receive from it. I did this and my app wasn't rejected for this (but for other things).

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.

Resources